Module Name: src Committed By: msaitoh Date: Tue Jan 5 11:24:43 UTC 2016
Modified Files: src/share/man/man4: Makefile Added Files: src/share/man/man4: ismt.4 src/sys/dev/pci: ismt.c Log Message: Port FreeBSD's ismt(4) driver. ismt(4) supports Intel Chipset internal SMBus 2.0 controller with DMA. It's different from ichsmb(4). Supported chipsets are S1200 and C2000. To generate a diff of this commit: cvs rdiff -u -r1.625 -r1.626 src/share/man/man4/Makefile cvs rdiff -u -r0 -r1.1 src/share/man/man4/ismt.4 cvs rdiff -u -r0 -r1.1 src/sys/dev/pci/ismt.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/share/man/man4/Makefile diff -u src/share/man/man4/Makefile:1.625 src/share/man/man4/Makefile:1.626 --- src/share/man/man4/Makefile:1.625 Wed Dec 16 08:20:03 2015 +++ src/share/man/man4/Makefile Tue Jan 5 11:24:43 2016 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.625 2015/12/16 08:20:03 jdc Exp $ +# $NetBSD: Makefile,v 1.626 2016/01/05 11:24:43 msaitoh Exp $ # @(#)Makefile 8.1 (Berkeley) 6/18/93 MAN= aac.4 ac97.4 acardide.4 aceride.4 acphy.4 \ @@ -33,7 +33,8 @@ MAN= aac.4 ac97.4 acardide.4 aceride.4 a ifmedia.4 igmafb.4 igphy.4 igsfb.4 iha.4 ihphy.4 iic.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 \ - irmce.4 isp.4 isv.4 itesio.4 iteide.4 iwi.4 iwm.4 iwn.4 ixg.4 ixpide.4 \ + irmce.4 isp.4 ismt.4 isv.4 itesio.4 iteide.4 iwi.4 iwm.4 iwn.4 ixg.4 \ + ixpide.4 \ jme.4 jmide.4 joy.4 \ kloader.4 kse.4 ksyms.4 kttcp.4 \ lc.4 ld.4 lii.4 lo.4 lua.4 lxtphy.4 \ Added files: Index: src/share/man/man4/ismt.4 diff -u /dev/null src/share/man/man4/ismt.4:1.1 --- /dev/null Tue Jan 5 11:24:43 2016 +++ src/share/man/man4/ismt.4 Tue Jan 5 11:24:43 2016 @@ -0,0 +1,79 @@ +.\" $NetBSD: ismt.4,v 1.1 2016/01/05 11:24:43 msaitoh Exp $ +.\" +.\" Copyright (c) 2015 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Masanobu SAITOH. +.\" +.\" 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +.\" +.Dd January 5, 2016 +.Dt ISMT 4 +.Os +.Sh NAME +.Nm ismt +.Nd Intel Chipset internal SMBus 2.0 controller with DMA +.Sh SYNOPSIS +.Cd "ismt* at pci? dev ? function ?" +.Cd "iic* at ismt?" +.Sh DESCRIPTION +The +.Nm +driver provides support for the Intel chipset internal SMBus 2.0 host interface +with DMA to be used with the +.Xr iic 4 +framework. +.Pp +Supported chipsets: +.Pp +.Bl -bullet -compact -offset indent +.It +S1200 series. +.It +C2000 series. +.El +.Sh SEE ALSO +.Xr iic 4 , +.Xr intro 4 , +.Xr pci 4 +.Sh HISTORY +The +.Nm +driver first appeared in +.Fx 11.0 +and in +.Nx 8.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm +driver was written by +.An Jim Harris +.Aq jimhar...@freebsd.org +for +.Fx +and ported to +.Nx +by +.An Masanobu SAITOH +.Aq msai...@netbsd.org . Index: src/sys/dev/pci/ismt.c diff -u /dev/null src/sys/dev/pci/ismt.c:1.1 --- /dev/null Tue Jan 5 11:24:43 2016 +++ src/sys/dev/pci/ismt.c Tue Jan 5 11:24:43 2016 @@ -0,0 +1,881 @@ +/*- + * Copyright (c) 2016 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Masanobu SAITOH. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/*- + * Copyright (C) 2014 Intel Corporation + * 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. + * 3. Neither the name of Intel Corporation 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 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. + */ + +#include <sys/cdefs.h> +#if 0 +__FBSDID("$FreeBSD: head/sys/dev/ismt/ismt.c 266474 2014-05-20 19:55:06Z jimharris $"); +#endif +__KERNEL_RCSID(0, "$NetBSD: ismt.c,v 1.1 2016/01/05 11:24:43 msaitoh Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/proc.h> + +#include <sys/bus.h> + +#include <dev/pci/pcidevs.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> + +#include <dev/i2c/i2cvar.h> + +#define ISMT_DESC_ENTRIES 32 + +/* Hardware Descriptor Constants - Control Field */ +#define ISMT_DESC_CWRL 0x01 /* Command/Write Length */ +#define ISMT_DESC_BLK 0X04 /* Perform Block Transaction */ +#define ISMT_DESC_FAIR 0x08 /* Set fairness flag upon successful arbit. */ +#define ISMT_DESC_PEC 0x10 /* Packet Error Code */ +#define ISMT_DESC_I2C 0x20 /* I2C Enable */ +#define ISMT_DESC_INT 0x40 /* Interrupt */ +#define ISMT_DESC_SOE 0x80 /* Stop On Error */ + +/* Hardware Descriptor Constants - Status Field */ +#define ISMT_DESC_SCS 0x01 /* Success */ +#define ISMT_DESC_DLTO 0x04 /* Data Low Time Out */ +#define ISMT_DESC_NAK 0x08 /* NAK Received */ +#define ISMT_DESC_CRC 0x10 /* CRC Error */ +#define ISMT_DESC_CLTO 0x20 /* Clock Low Time Out */ +#define ISMT_DESC_COL 0x40 /* Collisions */ +#define ISMT_DESC_LPR 0x80 /* Large Packet Received */ + +/* Macros */ +#define ISMT_DESC_ADDR_RW(addr, is_read) ((addr << 1) | (is_read)) + +/* iSMT General Register address offsets (SMBBAR + <addr>) */ +#define ISMT_GR_GCTRL 0x000 /* General Control */ +#define ISMT_GR_SMTICL 0x008 /* SMT Interrupt Cause Location */ +#define ISMT_GR_ERRINTMSK 0x010 /* Error Interrupt Mask */ +#define ISMT_GR_ERRAERMSK 0x014 /* Error AER Mask */ +#define ISMT_GR_ERRSTS 0x018 /* Error Status */ +#define ISMT_GR_ERRINFO 0x01c /* Error Information */ + +/* iSMT Master Registers */ +#define ISMT_MSTR_MDBA 0x100 /* Master Descriptor Base Address */ +#define ISMT_MSTR_MCTRL 0x108 /* Master Control */ +#define ISMT_MSTR_MSTS 0x10c /* Master Status */ +#define ISMT_MSTR_MDS 0x110 /* Master Descriptor Size */ +#define ISMT_MSTR_RPOLICY 0x114 /* Retry Policy */ + +/* iSMT Miscellaneous Registers */ +#define ISMT_SPGT 0x300 /* SMBus PHY Global Timing */ + +/* General Control Register (GCTRL) bit definitions */ +#define ISMT_GCTRL_TRST 0x04 /* Target Reset */ +#define ISMT_GCTRL_KILL 0x08 /* Kill */ +#define ISMT_GCTRL_SRST 0x40 /* Soft Reset */ + +/* Master Control Register (MCTRL) bit definitions */ +#define ISMT_MCTRL_SS 0x01 /* Start/Stop */ +#define ISMT_MCTRL_MEIE 0x10 /* Master Error Interrupt Enable */ +#define ISMT_MCTRL_FMHP 0x00ff0000 /* Firmware Master Head Ptr (FMHP) */ + +/* Master Status Register (MSTS) bit definitions */ +#define ISMT_MSTS_HMTP 0xff0000 /* HW Master Tail Pointer (HMTP) */ +#define ISMT_MSTS_MIS 0x20 /* Master Interrupt Status (MIS) */ +#define ISMT_MSTS_MEIS 0x10 /* Master Error Int Status (MEIS) */ +#define ISMT_MSTS_IP 0x01 /* In Progress */ + +/* Master Descriptor Size (MDS) bit definitions */ +#define ISMT_MDS_MASK 0xff /* Master Descriptor Size mask (MDS) */ + +/* SMBus PHY Global Timing Register (SPGT) bit definitions */ +#define ISMT_SPGT_SPD_MASK 0xc0000000 /* SMBus Speed mask */ +#define ISMT_SPGT_SPD_80K 0x00 /* 80 kHz */ +#define ISMT_SPGT_SPD_100K (0x1 << 30) /* 100 kHz */ +#define ISMT_SPGT_SPD_400K (0x2 << 30) /* 400 kHz */ +#define ISMT_SPGT_SPD_1M (0x3 << 30) /* 1 MHz */ + +/* MSI Control Register (MSICTL) bit definitions */ +#define ISMT_MSICTL_MSIE 0x01 /* MSI Enable */ + +#define ISMT_MAX_BLOCK_SIZE 32 /* per SMBus spec */ + +#define ISMT_INTR_TIMEOUT (hz / 50) /* 0.02s */ +#define ISMT_POLL_DELAY 100 /* 100usec */ +#define ISMT_POLL_COUNT 200 /* 100usec * 200 = 0.02s */ + +//#define ISMT_DEBUG aprint_debug_dev +#ifndef ISMT_DEBUG +#define ISMT_DEBUG(...) +#endif + +/* iSMT Hardware Descriptor */ +struct ismt_desc { + uint8_t tgtaddr_rw; /* target address & r/w bit */ + uint8_t wr_len_cmd; /* write length in bytes or a command */ + uint8_t rd_len; /* read length */ + uint8_t control; /* control bits */ + uint8_t status; /* status bits */ + uint8_t retry; /* collision retry and retry count */ + uint8_t rxbytes; /* received bytes */ + uint8_t txbytes; /* transmitted bytes */ + uint32_t dptr_low; /* lower 32 bit of the data pointer */ + uint32_t dptr_high; /* upper 32 bit of the data pointer */ +} __packed; + +#define DESC_SIZE (ISMT_DESC_ENTRIES * sizeof(struct ismt_desc)) + +#define DMA_BUFFER_SIZE 64 + +struct ismt_softc { + device_t pcidev; + device_t smbdev; + + struct i2c_controller sc_i2c_tag; + kmutex_t sc_i2c_mutex; + + pci_chipset_tag_t sc_pc; + pcitag_t sc_pcitag; + pci_intr_handle_t *sc_pihp; + void *sc_ih; + + bus_space_tag_t mmio_tag; + bus_space_handle_t mmio_handle; + bus_size_t mmio_size; + + uint8_t head; + + struct ismt_desc *desc; + bus_dma_tag_t desc_dma_tag; + bus_dmamap_t desc_dma_map; + bus_dma_segment_t desc_dma_seg; + int desc_rseg; + + uint8_t *dma_buffer; + bus_dma_tag_t dma_buffer_dma_tag; + bus_dmamap_t dma_buffer_dma_map; + bus_dma_segment_t dma_buffer_dma_seg; + int dma_buffer_rseg; + + uint8_t using_msi; +}; + +static int ismt_intr(void *); +static int ismt_i2c_acquire_bus(void *, int); +static void ismt_i2c_release_bus(void *, int); +static int ismt_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, + size_t, void *, size_t, int); +static struct ismt_desc *ismt_alloc_desc(struct ismt_softc *); +static int ismt_submit(struct ismt_softc *, struct ismt_desc *, + i2c_addr_t, uint8_t, int); +static int ismt_quick(struct ismt_softc *, i2c_addr_t, i2c_op_t, int); +static int ismt_sendb(struct ismt_softc *, i2c_addr_t, i2c_op_t, char, + int); +static int ismt_recvb(struct ismt_softc *, i2c_addr_t, i2c_op_t, int); +static int ismt_writeb(struct ismt_softc *, i2c_addr_t, i2c_op_t, uint8_t, + char, int); +static int ismt_writew(struct ismt_softc *, i2c_addr_t, i2c_op_t, uint8_t, + uint16_t, int); +static int ismt_readb(struct ismt_softc *, i2c_addr_t, i2c_op_t, char, + int); +static int ismt_readw(struct ismt_softc *, i2c_addr_t, i2c_op_t, char, + int); + +static int ismt_match(device_t, cfdata_t, void *); +static void ismt_attach(device_t, device_t, void *); +static int ismt_detach(device_t, int); +static int ismt_rescan(device_t, const char *, const int *); +static void ismt_config_interrupts(device_t); +static void ismt_chdet(device_t, device_t); + +CFATTACH_DECL3_NEW(ismt, sizeof(struct ismt_softc), + ismt_match, ismt_attach, ismt_detach, NULL, ismt_rescan, ismt_chdet, + DVF_DETACH_SHUTDOWN); + +static int +ismt_intr(void *arg) +{ + struct ismt_softc *sc = arg; + uint32_t val; + + val = bus_space_read_4(sc->mmio_tag, sc->mmio_handle, ISMT_MSTR_MSTS); + if ((sc->using_msi == 0) + && (val & (ISMT_MSTS_MIS | ISMT_MSTS_MEIS)) == 0) + return 0; /* Not for me */ + + ISMT_DEBUG(sc->pcidev, "%s MSTS = 0x%08x\n", __func__, val); + + val |= (ISMT_MSTS_MIS | ISMT_MSTS_MEIS); + bus_space_write_4(sc->mmio_tag, sc->mmio_handle, ISMT_MSTR_MSTS, val); + + if (sc->using_msi) + wakeup(sc); + + return 1; +} + +static int +ismt_i2c_acquire_bus(void *cookie, int flags) +{ + struct ismt_softc *sc = cookie; + + mutex_enter(&sc->sc_i2c_mutex); + return 0; +} + +static void +ismt_i2c_release_bus(void *cookie, int flags) +{ + struct ismt_softc *sc = cookie; + + mutex_exit(&sc->sc_i2c_mutex); +} + +static int +ismt_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, + const void *cmd, size_t cmdlen, void *buf, size_t buflen, int flags) +{ + struct ismt_softc *sc = cookie; + uint8_t *p = buf; + int rv; + + ISMT_DEBUG(sc->pcidev, "exec: op %d, addr 0x%02x, cmdlen %zu, " + " buflen %zu, flags 0x%02x\n", op, addr, cmdlen, buflen, flags); + + if ((cmdlen == 0) && (buflen == 0)) + return ismt_quick(sc, addr, op, flags); + + if (I2C_OP_READ_P(op) && (cmdlen == 0) && (buflen == 1)) { + rv = ismt_recvb(sc, addr, op, flags); + if (rv == -1) + return -1; + *p = (uint8_t)rv; + return 0; + } + + if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 1)) { + rv = ismt_readb(sc, addr, op, *(const uint8_t*)cmd, flags); + if (rv == -1) + return -1; + *p = (uint8_t)rv; + return 0; + } + + if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 2)) { + rv = ismt_readw(sc, addr, op, *(const uint8_t*)cmd, flags); + if (rv == -1) + return -1; + *(uint16_t *)p = (uint16_t)rv; + return 0; + } + + if ((I2C_OP_WRITE_P(op)) && (cmdlen == 0) && (buflen == 1)) + return ismt_sendb(sc, addr, op, *(uint8_t*)buf, flags); + + if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 1)) + return ismt_writeb(sc, addr, op, *(const uint8_t*)cmd, + *(uint8_t*)buf, flags); + + if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 2)) + return ismt_writew(sc, addr, op, + *(const uint8_t*)cmd, *((uint16_t *)buf), flags); + + return -1; +} + +static struct ismt_desc * +ismt_alloc_desc(struct ismt_softc *sc) +{ + struct ismt_desc *desc; + + KASSERT(mutex_owned(&sc->sc_i2c_mutex)); + + desc = &sc->desc[sc->head++]; + if (sc->head == ISMT_DESC_ENTRIES) + sc->head = 0; + + memset(desc, 0, sizeof(*desc)); + + return (desc); +} + +static int +ismt_submit(struct ismt_softc *sc, struct ismt_desc *desc, i2c_addr_t slave, + uint8_t is_read, int flags) +{ + uint32_t err, fmhp, val; + int timeout, i; + + if (sc->using_msi == 0) + flags |= I2C_F_POLL; + desc->control |= ISMT_DESC_FAIR; + if ((flags & I2C_F_POLL) == 0) + desc->control |= ISMT_DESC_INT; + + desc->tgtaddr_rw = ISMT_DESC_ADDR_RW(slave, is_read); + desc->dptr_low = (sc->dma_buffer_dma_map->dm_segs[0].ds_addr + & 0xFFFFFFFFLL); + desc->dptr_high = (sc->dma_buffer_dma_map->dm_segs[0].ds_addr >> 32); + + bus_dmamap_sync(sc->desc_dma_tag, sc->desc_dma_map, + desc - &sc->desc[0], sizeof(struct ismt_desc), + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + fmhp = sc->head << 16; + val = bus_space_read_4(sc->mmio_tag, sc->mmio_handle, ISMT_MSTR_MCTRL); + val &= ~ISMT_MCTRL_FMHP; + val |= fmhp; + bus_space_write_4(sc->mmio_tag, sc->mmio_handle, ISMT_MSTR_MCTRL, val); + + /* set the start bit */ + val = bus_space_read_4(sc->mmio_tag, sc->mmio_handle, ISMT_MSTR_MCTRL); + val |= ISMT_MCTRL_SS; + bus_space_write_4(sc->mmio_tag, sc->mmio_handle, ISMT_MSTR_MCTRL, val); + + i = 0; + if ((flags & I2C_F_POLL) == 0) { + timeout = ISMT_INTR_TIMEOUT; + if (timeout == 0) + timeout = 1; + err = tsleep(sc, PWAIT, "ismt_wait", timeout); + if (err != 0) { + ISMT_DEBUG(sc->pcidev, "%s timeout\n", __func__); + return -1; + } + } else { + /* Polling */ + for (i = 0; i < ISMT_POLL_COUNT; i++) { + val = bus_space_read_4(sc->mmio_tag, sc->mmio_handle, + ISMT_MSTR_MSTS); + if ((val & (ISMT_MSTS_MIS | ISMT_MSTS_MEIS)) != 0) { + ismt_intr(sc); + err = 0; + break; + } + delay(ISMT_POLL_DELAY); + } + if (i >= ISMT_POLL_COUNT) { + ISMT_DEBUG(sc->pcidev, "%s polling timeout. " + "MSTS = %08x\n", __func__, val); + return -1; + } + } + + bus_dmamap_sync(sc->desc_dma_tag, sc->desc_dma_map, + desc - &sc->desc[0], sizeof(struct ismt_desc), + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + ISMT_DEBUG(sc->pcidev, "%s status=0x%02x\n", __func__, desc->status); + + if (desc->status & ISMT_DESC_SCS) + return 0; + + if (desc->status & ISMT_DESC_NAK) + return -1; + + if (desc->status & ISMT_DESC_CRC) + return -1; + + if (desc->status & ISMT_DESC_COL) + return -1; + + if (desc->status & ISMT_DESC_LPR) + return -1; + + if (desc->status & (ISMT_DESC_DLTO | ISMT_DESC_CLTO)) + return -1; + + return -1; +} + +static int +ismt_quick(struct ismt_softc *sc, i2c_addr_t slave, i2c_op_t op, int flags) +{ + struct ismt_desc *desc; + int is_read; + + ISMT_DEBUG(sc->pcidev, "%s\n", __func__); + + desc = ismt_alloc_desc(sc); + is_read = I2C_OP_READ_P(op); + return (ismt_submit(sc, desc, slave, is_read, flags)); +} + +static int +ismt_sendb(struct ismt_softc *sc, i2c_addr_t slave, i2c_op_t op, char byte, + int flags) +{ + struct ismt_desc *desc; + + ISMT_DEBUG(sc->pcidev, "%s\n", __func__); + + desc = ismt_alloc_desc(sc); + desc->control = ISMT_DESC_CWRL; + desc->wr_len_cmd = byte; + + return (ismt_submit(sc, desc, slave, 0, flags)); +} + +static int +ismt_recvb(struct ismt_softc *sc, i2c_addr_t slave, i2c_op_t op, int flags) +{ + struct ismt_desc *desc; + int err; + + ISMT_DEBUG(sc->pcidev, "%s\n", __func__); + + desc = ismt_alloc_desc(sc); + desc->rd_len = 1; + + err = ismt_submit(sc, desc, slave, 1, flags); + + if (err != 0) + return (err); + + return sc->dma_buffer[0]; +} + +static int +ismt_writeb(struct ismt_softc *sc, i2c_addr_t slave, i2c_op_t op, uint8_t cmd, + char byte, int flags) +{ + struct ismt_desc *desc; + + ISMT_DEBUG(sc->pcidev, "%s\n", __func__); + + desc = ismt_alloc_desc(sc); + desc->wr_len_cmd = 2; + sc->dma_buffer[0] = cmd; + sc->dma_buffer[1] = byte; + + return (ismt_submit(sc, desc, slave, 0, flags)); +} + +static int +ismt_writew(struct ismt_softc *sc, i2c_addr_t slave, i2c_op_t op, uint8_t cmd, + uint16_t word, int flags) +{ + struct ismt_desc *desc; + + ISMT_DEBUG(sc->pcidev, "%s\n", __func__); + + desc = ismt_alloc_desc(sc); + desc->wr_len_cmd = 3; + sc->dma_buffer[0] = cmd; + sc->dma_buffer[1] = word & 0xFF; + sc->dma_buffer[2] = word >> 8; + + return (ismt_submit(sc, desc, slave, 0, flags)); +} + +static int +ismt_readb(struct ismt_softc *sc, i2c_addr_t slave, i2c_op_t op, char cmd, + int flags) +{ + struct ismt_desc *desc; + int err; + + ISMT_DEBUG(sc->pcidev, "%s\n", __func__); + + desc = ismt_alloc_desc(sc); + desc->control = ISMT_DESC_CWRL; + desc->wr_len_cmd = cmd; + desc->rd_len = 1; + + err = ismt_submit(sc, desc, slave, 1, flags); + + if (err != 0) + return (err); + + return sc->dma_buffer[0]; +} + +static int +ismt_readw(struct ismt_softc *sc, i2c_addr_t slave, i2c_op_t op, char cmd, + int flags) +{ + struct ismt_desc *desc; + uint16_t word; + int err; + + ISMT_DEBUG(sc->pcidev, "%s\n", __func__); + + desc = ismt_alloc_desc(sc); + desc->control = ISMT_DESC_CWRL; + desc->wr_len_cmd = cmd; + desc->rd_len = 2; + + err = ismt_submit(sc, desc, slave, 1, flags); + + if (err != 0) + return (err); + + word = sc->dma_buffer[0] | (sc->dma_buffer[1] << 8); + + return word; +} + +#if 0 +static int +ismt_pcall(struct ismt_softc *sc, i2c_addr_t slave, i2c_op_t op, char cmd, + uint16_t sdata, uint16_t *rdata, int flags) +{ + struct ismt_desc *desc; + int err; + + ISMT_DEBUG(sc->pcidev, "%s\n", __func__); + + desc = ismt_alloc_desc(sc); + desc->wr_len_cmd = 3; + desc->rd_len = 2; + sc->dma_buffer[0] = cmd; + sc->dma_buffer[1] = sdata & 0xff; + sc->dma_buffer[2] = sdata >> 8; + + err = ismt_submit(sc, desc, slave, 0, flags); + + if (err != 0) + return (err); + + *rdata = sc->dma_buffer[0] | (sc->dma_buffer[1] << 8); + + return (err); +} + +static int +ismt_bwrite(struct ismt_softc *sc, i2c_addr_t slave, i2c_op_t op, char cmd, + u_char count, char *buf, int flags) +{ + struct ismt_desc *desc; + + ISMT_DEBUG(sc->pcidev, "%s\n", __func__); + + if (count == 0 || count > ISMT_MAX_BLOCK_SIZE) + return -1; + + desc = ismt_alloc_desc(sc); + desc->control = ISMT_DESC_I2C; + desc->wr_len_cmd = count + 1; + sc->dma_buffer[0] = cmd; + memcpy(&sc->dma_buffer[1], buf, count); + + return (ismt_submit(sc, desc, slave, 0, flags)); +} + +static int +ismt_bread(struct ismt_softc *sc, i2c_addr_t slave, i2c_op_t op, char cmd, + u_char *count, char *buf, int flags) +{ + struct ismt_desc *desc; + int err; + + ISMT_DEBUG(sc->pcidev, "%s\n", __func__); + + if (*count == 0 || *count > ISMT_MAX_BLOCK_SIZE) + return -1; + + desc = ismt_alloc_desc(sc); + desc->control = ISMT_DESC_I2C | ISMT_DESC_CWRL; + desc->wr_len_cmd = cmd; + desc->rd_len = *count; + + err = ismt_submit(sc, desc, slave, 0, flags); + + if (err != 0) + return (err); + + memcpy(buf, sc->dma_buffer, desc->rxbytes); + *count = desc->rxbytes; + + return (err); +} +#endif + +static int +ismt_detach(device_t self, int flags) +{ + struct ismt_softc *sc; + int rv = 0; + + ISMT_DEBUG(self, "%s\n", __func__); + sc = device_private(self); + if (sc->smbdev != NULL) { + rv = config_detach(sc->smbdev, flags); + if (rv != 0) + return rv; + } + if (sc->sc_ih != NULL) { + pci_intr_disestablish(sc->sc_pc, sc->sc_ih); + sc->sc_ih = NULL; + } + if (sc->sc_pihp != NULL) { + pci_intr_release(sc->sc_pc, sc->sc_pihp, 1); + sc->sc_pihp = NULL; + } + + bus_dmamap_unload(sc->desc_dma_tag, sc->desc_dma_map); + bus_dmamap_unload(sc->dma_buffer_dma_tag, sc->dma_buffer_dma_map); + + bus_dmamem_free(sc->desc_dma_tag, &sc->desc_dma_seg, sc->desc_rseg); + bus_dmamem_free(sc->dma_buffer_dma_tag, &sc->dma_buffer_dma_seg, + sc->dma_buffer_rseg); + + if (sc->mmio_size) + bus_space_unmap(sc->mmio_tag, sc->mmio_handle, sc->mmio_size); + + mutex_destroy(&sc->sc_i2c_mutex); + return rv; +} + +static void +ismt_attach(device_t parent, device_t self, void *aux) +{ + struct ismt_softc *sc = device_private(self); + struct pci_attach_args *pa = aux; + const char *intrstr = NULL; + char intrbuf[PCI_INTRSTR_LEN]; + pcireg_t reg; + int val; + + sc->pcidev = self; + sc->sc_pc = pa->pa_pc; + sc->sc_pcitag = pa->pa_tag; + + /* Enable busmastering */ + reg = pci_conf_read(sc->sc_pc, sc->sc_pcitag, PCI_COMMAND_STATUS_REG); + reg |= PCI_COMMAND_MASTER_ENABLE; + pci_conf_write(sc->sc_pc, sc->sc_pcitag, PCI_COMMAND_STATUS_REG, reg); + + pci_aprint_devinfo(pa, NULL); + + /* Map mem space */ + if (pci_mapreg_map(pa, PCI_BAR0, PCI_MAPREG_TYPE_MEM, 0, + &sc->mmio_tag, &sc->mmio_handle, NULL, &sc->mmio_size)) { + aprint_error_dev(self, "can't map mem space\n"); + goto fail; + } + + if (pci_dma64_available(pa)) { + sc->desc_dma_tag = pa->pa_dmat64; + sc->dma_buffer_dma_tag = pa->pa_dmat64; + } else { + sc->desc_dma_tag = pa->pa_dmat; + sc->dma_buffer_dma_tag = pa->pa_dmat; + } + bus_dmamem_alloc(sc->desc_dma_tag, DESC_SIZE, PAGE_SIZE, 0, + &sc->desc_dma_seg, ISMT_DESC_ENTRIES, &sc->desc_rseg, + BUS_DMA_WAITOK); + bus_dmamem_alloc(sc->dma_buffer_dma_tag, DMA_BUFFER_SIZE, PAGE_SIZE, 0, + &sc->dma_buffer_dma_seg, 1, &sc->dma_buffer_rseg, BUS_DMA_WAITOK); + + bus_dmamem_map(sc->desc_dma_tag, &sc->desc_dma_seg, + sc->desc_rseg, DESC_SIZE, (void **)&sc->desc, BUS_DMA_COHERENT); + bus_dmamem_map(sc->dma_buffer_dma_tag, &sc->dma_buffer_dma_seg, + sc->dma_buffer_rseg, DMA_BUFFER_SIZE, (void **)&sc->dma_buffer, + BUS_DMA_COHERENT); + + bus_dmamap_create(sc->desc_dma_tag, DESC_SIZE, 1, + DESC_SIZE, 0, 0, &sc->desc_dma_map); + bus_dmamap_create(sc->dma_buffer_dma_tag, DMA_BUFFER_SIZE, 1, + DMA_BUFFER_SIZE, 0, 0, &sc->dma_buffer_dma_map); + + bus_dmamap_load(sc->desc_dma_tag, + sc->desc_dma_map, sc->desc, DESC_SIZE, NULL, 0); + bus_dmamap_load(sc->dma_buffer_dma_tag, + sc->dma_buffer_dma_map, sc->dma_buffer, DMA_BUFFER_SIZE, + NULL, 0); + + bus_space_write_4(sc->mmio_tag, sc->mmio_handle, ISMT_MSTR_MDBA, + (sc->desc_dma_map->dm_segs[0].ds_addr & 0xFFFFFFFFLL)); + bus_space_write_4(sc->mmio_tag, sc->mmio_handle, ISMT_MSTR_MDBA + 4, + (sc->desc_dma_map->dm_segs[0].ds_addr >> 32)); + + /* initialize the Master Control Register (MCTRL) */ + bus_space_write_4(sc->mmio_tag, sc->mmio_handle, ISMT_MSTR_MCTRL, + ISMT_MCTRL_MEIE); + + /* initialize the Master Status Register (MSTS) */ + bus_space_write_4(sc->mmio_tag, sc->mmio_handle, ISMT_MSTR_MSTS, 0); + + /* initialize the Master Descriptor Size (MDS) */ + val = bus_space_read_4(sc->mmio_tag, sc->mmio_handle, ISMT_MSTR_MDS); + val &= ~ISMT_MDS_MASK; + val |= (ISMT_DESC_ENTRIES - 1); + bus_space_write_4(sc->mmio_tag, sc->mmio_handle, ISMT_MSTR_MDS, val); + + if (pci_intr_alloc(pa, &sc->sc_pihp, NULL, 0)) { + aprint_error_dev(self, "couldn't map interrupt\n"); + return; + } + intrstr = pci_intr_string(pa->pa_pc, sc->sc_pihp[0], intrbuf, + sizeof(intrbuf)); + sc->sc_ih = pci_intr_establish(pa->pa_pc, sc->sc_pihp[0], IPL_BIO, + ismt_intr, sc); + if (sc->sc_ih == NULL) { + aprint_error_dev(sc->pcidev, "unable to establish %s\n", + (pci_intr_type(sc->sc_pihp[0]) + == PCI_INTR_TYPE_MSI) ? "MSI" : "INTx"); + /* Polling */ + } + + if (pci_intr_type(sc->sc_pihp[0]) == PCI_INTR_TYPE_MSI) + sc->using_msi = 1; + + aprint_normal_dev(sc->pcidev, "interrupting at %s\n", intrstr); + + sc->smbdev = NULL; + mutex_init(&sc->sc_i2c_mutex, MUTEX_DEFAULT, IPL_NONE); + if (!pmf_device_register(self, NULL, NULL)) + aprint_error_dev(self, "couldn't establish power handler\n"); + + config_interrupts(self, ismt_config_interrupts); + return; + +fail: + ismt_detach(sc->pcidev, 0); + + return; +} + +static int +ismt_match(device_t parent, cfdata_t match, void *aux) +{ + struct pci_attach_args *pa = aux; + + if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_INTEL) + return 0; + + switch (PCI_PRODUCT(pa->pa_id)) { + case PCI_PRODUCT_INTEL_S1200_SMBUS_0: + case PCI_PRODUCT_INTEL_S1200_SMBUS_1: + case PCI_PRODUCT_INTEL_C2000_SMBUS: + break; + default: + return 0; + } + + return 1; +} + +static int +ismt_rescan(device_t self, const char *ifattr, const int *flags) +{ + struct ismt_softc *sc = device_private(self); + struct i2cbus_attach_args iba; + + if (!ifattr_match(ifattr, "i2cbus")) + return 0; + + if (sc->smbdev) + return 0; + + /* Attach I2C bus */ + sc->sc_i2c_tag.ic_cookie = sc; + sc->sc_i2c_tag.ic_acquire_bus = ismt_i2c_acquire_bus; + sc->sc_i2c_tag.ic_release_bus = ismt_i2c_release_bus; + sc->sc_i2c_tag.ic_exec = ismt_i2c_exec; + + memset(&iba, 0, sizeof(iba)); + iba.iba_type = I2C_TYPE_SMBUS; + iba.iba_tag = &sc->sc_i2c_tag; + sc->smbdev = config_found_ia(self, ifattr, &iba, iicbus_print); + + return 0; +} + +static void +ismt_config_interrupts(device_t self) +{ + int flags = 0; + + ismt_rescan(self, "i2cbus", &flags); +} + +static void +ismt_chdet(device_t self, device_t child) +{ + struct ismt_softc *sc = device_private(self); + + if (sc->smbdev == child) + sc->smbdev = NULL; + +} + +MODULE(MODULE_CLASS_DRIVER, ismt, "pci"); + +#ifdef _MODULE +#include "ioconf.c" +#endif + +static int +ismt_modcmd(modcmd_t cmd, void *opaque) +{ + int error = 0; + + switch (cmd) { + case MODULE_CMD_INIT: +#ifdef _MODULE + error = config_init_component(cfdriver_ioconf_ismt, + cfattach_ioconf_ismt, cfdata_ioconf_ismt); +#endif + return error; + case MODULE_CMD_FINI: +#ifdef _MODULE + error = config_fini_component(cfdriver_ioconf_ismt, + cfattach_ioconf_ismt, cfdata_ioconf_ismt); +#endif + return error; + default: + return ENOTTY; + } +}