intpm.c revision 172667
143166Snsouch/*- 243166Snsouch * Copyright (c) 1998, 1999 Takanori Watanabe 343166Snsouch * All rights reserved. 443166Snsouch * 543166Snsouch * Redistribution and use in source and binary forms, with or without 643166Snsouch * modification, are permitted provided that the following conditions 743166Snsouch * are met: 843166Snsouch * 1. Redistributions of source code must retain the above copyright 943166Snsouch * notice, this list of conditions and the following disclaimer. 1043166Snsouch * 2. Redistributions in binary form must reproduce the above copyright 1143166Snsouch * notice, this list of conditions and the following disclaimer in the 1243166Snsouch * documentation and/or other materials provided with the distribution. 1343166Snsouch * 1443166Snsouch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1543166Snsouch * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1643166Snsouch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1743166Snsouch * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1843166Snsouch * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1943166Snsouch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2043166Snsouch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2143166Snsouch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2243166Snsouch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2343166Snsouch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2443166Snsouch * SUCH DAMAGE. 2543166Snsouch */ 2643166Snsouch 27116192Sobrien#include <sys/cdefs.h> 28116192Sobrien__FBSDID("$FreeBSD: head/sys/pci/intpm.c 172667 2007-10-15 16:18:20Z jhb $"); 29116192Sobrien 3043166Snsouch#include <sys/param.h> 3143166Snsouch#include <sys/systm.h> 32165951Sjhb#include <sys/bus.h> 3343166Snsouch#include <sys/kernel.h> 34165951Sjhb#include <sys/lock.h> 3543166Snsouch#include <sys/module.h> 36165951Sjhb#include <sys/mutex.h> 3746651Speter#include <sys/rman.h> 38165951Sjhb#include <machine/bus.h> 3943166Snsouch#include <dev/smbus/smbconf.h> 4043166Snsouch 4143166Snsouch#include "smbus_if.h" 4243166Snsouch 43119288Simp#include <dev/pci/pcireg.h> 44119288Simp#include <dev/pci/pcivar.h> 4543166Snsouch#include <pci/intpmreg.h> 4643166Snsouch 4743166Snsouch#include "opt_intpm.h" 4843166Snsouch 49165951Sjhbstruct intsmb_softc { 50165951Sjhb device_t dev; 51165951Sjhb struct resource *io_res; 52165951Sjhb struct resource *irq_res; 53165951Sjhb void *irq_hand; 54165951Sjhb device_t smbus; 55165951Sjhb int isbusy; 56165951Sjhb struct mtx lock; 5743166Snsouch}; 58162289Sjhb 59165951Sjhb#define INTSMB_LOCK(sc) mtx_lock(&(sc)->lock) 60165951Sjhb#define INTSMB_UNLOCK(sc) mtx_unlock(&(sc)->lock) 61165951Sjhb#define INTSMB_LOCK_ASSERT(sc) mtx_assert(&(sc)->lock, MA_OWNED) 62165951Sjhb 6343166Snsouchstatic int intsmb_probe(device_t); 6443166Snsouchstatic int intsmb_attach(device_t); 65165951Sjhbstatic int intsmb_detach(device_t); 66165951Sjhbstatic int intsmb_intr(struct intsmb_softc *sc); 67165951Sjhbstatic int intsmb_slvintr(struct intsmb_softc *sc); 68165951Sjhbstatic void intsmb_alrintr(struct intsmb_softc *sc); 69162234Sjhbstatic int intsmb_callback(device_t dev, int index, void *data); 7043166Snsouchstatic int intsmb_quick(device_t dev, u_char slave, int how); 7143166Snsouchstatic int intsmb_sendb(device_t dev, u_char slave, char byte); 7243166Snsouchstatic int intsmb_recvb(device_t dev, u_char slave, char *byte); 7343166Snsouchstatic int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte); 7443166Snsouchstatic int intsmb_writew(device_t dev, u_char slave, char cmd, short word); 7543166Snsouchstatic int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte); 7643166Snsouchstatic int intsmb_readw(device_t dev, u_char slave, char cmd, short *word); 7743166Snsouchstatic int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata); 7843166Snsouchstatic int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf); 79162234Sjhbstatic int intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf); 80165951Sjhbstatic void intsmb_start(struct intsmb_softc *sc, u_char cmd, int nointr); 81165951Sjhbstatic int intsmb_stop(struct intsmb_softc *sc); 82165951Sjhbstatic int intsmb_stop_poll(struct intsmb_softc *sc); 83165951Sjhbstatic int intsmb_free(struct intsmb_softc *sc); 84165951Sjhbstatic void intsmb_rawintr(void *arg); 85162289Sjhb 86165951Sjhbstatic int 87165951Sjhbintsmb_probe(device_t dev) 88165951Sjhb{ 8943166Snsouch 90165951Sjhb switch (pci_get_devid(dev)) { 91165951Sjhb case 0x71138086: /* Intel 82371AB */ 92165951Sjhb case 0x719b8086: /* Intel 82443MX */ 93165951Sjhb#if 0 94165951Sjhb /* Not a good idea yet, this stops isab0 functioning */ 95165951Sjhb case 0x02001166: /* ServerWorks OSB4 */ 96165951Sjhb#endif 97165951Sjhb device_set_desc(dev, "Intel PIIX4 SMBUS Interface"); 98165951Sjhb break; 99165951Sjhb default: 100165951Sjhb return (ENXIO); 101165951Sjhb } 10243166Snsouch 103165951Sjhb return (BUS_PROBE_DEFAULT); 104165951Sjhb} 105162289Sjhb 106165951Sjhbstatic int 107165951Sjhbintsmb_attach(device_t dev) 108165951Sjhb{ 109165951Sjhb struct intsmb_softc *sc = device_get_softc(dev); 110165951Sjhb int error, rid, value; 111165951Sjhb char *str; 112162289Sjhb 113165951Sjhb mtx_init(&sc->lock, device_get_nameunit(dev), "intsmb", MTX_DEF); 11443166Snsouch 115165951Sjhb rid = PCI_BASE_ADDR_SMB; 116165951Sjhb sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 117165951Sjhb RF_ACTIVE); 118165951Sjhb if (sc->io_res == NULL) { 119165951Sjhb device_printf(dev, "Could not allocate I/O space\n"); 120165951Sjhb error = ENXIO; 121165951Sjhb goto fail; 122165951Sjhb } 12343166Snsouch 124165951Sjhb#ifndef NO_CHANGE_PCICONF 125165951Sjhb pci_write_config(dev, PCIR_INTLINE, 0x9, 1); 126165951Sjhb pci_write_config(dev, PCI_HST_CFG_SMB, 127165951Sjhb PCI_INTR_SMB_IRQ9 | PCI_INTR_SMB_ENABLE, 1); 128165951Sjhb#endif 129165951Sjhb value = pci_read_config(dev, PCI_HST_CFG_SMB, 1); 130165951Sjhb switch (value & 0xe) { 131165951Sjhb case PCI_INTR_SMB_SMI: 132165951Sjhb str = "SMI"; 133165951Sjhb break; 134165951Sjhb case PCI_INTR_SMB_IRQ9: 135165951Sjhb str = "IRQ 9"; 136165951Sjhb break; 137165951Sjhb default: 138165951Sjhb str = "BOGUS"; 139165951Sjhb } 140165951Sjhb device_printf(dev, "intr %s %s ", str, 141165951Sjhb (value & 1) ? "enabled" : "disabled"); 142168870Sjhb printf("revision %d\n", pci_read_config(dev, PCI_REVID_SMB, 1)); 14343166Snsouch 144165951Sjhb if ((value & 0xe) != PCI_INTR_SMB_IRQ9) { 145165951Sjhb device_printf(dev, "Unsupported interrupt mode\n"); 146165951Sjhb error = ENXIO; 147165951Sjhb goto fail; 148165951Sjhb } 14946651Speter 150165951Sjhb /* Force IRQ 9. */ 151165951Sjhb rid = 0; 152165951Sjhb bus_set_resource(dev, SYS_RES_IRQ, rid, 9, 1); 153165951Sjhb sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 154165951Sjhb RF_SHAREABLE | RF_ACTIVE); 155165951Sjhb if (sc->irq_res == NULL) { 156165951Sjhb device_printf(dev, "Could not allocate irq\n"); 157165951Sjhb error = ENXIO; 158165951Sjhb goto fail; 159165951Sjhb } 16043166Snsouch 161166901Spiso error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC, NULL, 162166901Spiso intsmb_rawintr, sc, &sc->irq_hand); 163165951Sjhb if (error) { 164165951Sjhb device_printf(dev, "Failed to map intr\n"); 165165951Sjhb goto fail; 166165951Sjhb } 167162289Sjhb 168165951Sjhb value = pci_read_config(dev, PCI_BASE_ADDR_PM, 4); 169165951Sjhb device_printf(dev, "PM %s %x\n", (value & 1) ? "I/O mapped" : "Memory", 170165951Sjhb value & 0xfffe); 171162289Sjhb 172165951Sjhb sc->isbusy = 0; 173165951Sjhb sc->smbus = device_add_child(dev, "smbus", -1); 174165951Sjhb if (sc->smbus == NULL) { 175165951Sjhb error = ENXIO; 176165951Sjhb goto fail; 177165951Sjhb } 178165951Sjhb error = device_probe_and_attach(sc->smbus); 179165951Sjhb if (error) 180165951Sjhb goto fail; 181162289Sjhb 182165951Sjhb#ifdef ENABLE_ALART 183165951Sjhb /* Enable Arart */ 184165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN); 185165951Sjhb#endif 186165951Sjhb return (0); 18743166Snsouch 188165951Sjhbfail: 189165951Sjhb intsmb_detach(dev); 190165951Sjhb return (error); 191165951Sjhb} 192165951Sjhb 193162289Sjhbstatic int 194165951Sjhbintsmb_detach(device_t dev) 19543166Snsouch{ 196162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 197165951Sjhb int error; 198162289Sjhb 199165951Sjhb error = bus_generic_detach(dev); 200165951Sjhb if (error) 201165951Sjhb return (error); 202162289Sjhb 203165951Sjhb if (sc->smbus) 204165951Sjhb device_delete_child(dev, sc->smbus); 205165951Sjhb if (sc->irq_hand) 206165951Sjhb bus_teardown_intr(dev, sc->irq_res, sc->irq_hand); 207165951Sjhb if (sc->irq_res) 208165951Sjhb bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 209165951Sjhb if (sc->io_res) 210165951Sjhb bus_release_resource(dev, SYS_RES_IOPORT, PCI_BASE_ADDR_SMB, 211165951Sjhb sc->io_res); 212165951Sjhb mtx_destroy(&sc->lock); 213165951Sjhb return (0); 21443166Snsouch} 215165951Sjhb 216165951Sjhbstatic void 217165951Sjhbintsmb_rawintr(void *arg) 21843166Snsouch{ 219165951Sjhb struct intsmb_softc *sc = arg; 220162289Sjhb 221165951Sjhb INTSMB_LOCK(sc); 222165951Sjhb intsmb_intr(sc); 223165951Sjhb intsmb_slvintr(sc); 224165951Sjhb INTSMB_UNLOCK(sc); 22543166Snsouch} 22643166Snsouch 227162289Sjhbstatic int 228162234Sjhbintsmb_callback(device_t dev, int index, void *data) 22943166Snsouch{ 23043166Snsouch int error = 0; 231162289Sjhb 23243166Snsouch switch (index) { 23343166Snsouch case SMB_REQUEST_BUS: 23443166Snsouch break; 23543166Snsouch case SMB_RELEASE_BUS: 23643166Snsouch break; 23743166Snsouch default: 23843166Snsouch error = EINVAL; 23943166Snsouch } 240162289Sjhb 24143166Snsouch return (error); 24243166Snsouch} 243162289Sjhb 244162289Sjhb/* Counterpart of smbtx_smb_free(). */ 245162289Sjhbstatic int 246165951Sjhbintsmb_free(struct intsmb_softc *sc) 247162289Sjhb{ 248162289Sjhb 249165951Sjhb INTSMB_LOCK_ASSERT(sc); 250165951Sjhb if ((bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & PIIX4_SMBHSTSTAT_BUSY) || 25143166Snsouch#ifdef ENABLE_ALART 252165951Sjhb (bus_read_1(sc->io_res, PIIX4_SMBSLVSTS) & PIIX4_SMBSLVSTS_BUSY) || 25343166Snsouch#endif 254162289Sjhb sc->isbusy) 255165951Sjhb return (SMB_EBUSY); 256165951Sjhb 257162289Sjhb sc->isbusy = 1; 258162289Sjhb /* Disable Interrupt in slave part. */ 25943166Snsouch#ifndef ENABLE_ALART 260165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 0); 26143166Snsouch#endif 262162289Sjhb /* Reset INTR Flag to prepare INTR. */ 263165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTSTS, 264165951Sjhb PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR | 265165951Sjhb PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL); 266162289Sjhb return (0); 26743166Snsouch} 26843166Snsouch 26943166Snsouchstatic int 270165951Sjhbintsmb_intr(struct intsmb_softc *sc) 27143166Snsouch{ 272165951Sjhb int status, tmp; 273162289Sjhb 274165951Sjhb status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 275162289Sjhb if (status & PIIX4_SMBHSTSTAT_BUSY) 276162289Sjhb return (1); 277162289Sjhb 278162289Sjhb if (status & (PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR | 279162289Sjhb PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL)) { 280162289Sjhb 281165951Sjhb tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 282165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, 283162289Sjhb tmp & ~PIIX4_SMBHSTCNT_INTREN); 284162289Sjhb if (sc->isbusy) { 285162289Sjhb sc->isbusy = 0; 286162289Sjhb wakeup(sc); 28749064Snsouch } 288162289Sjhb return (0); 28943166Snsouch } 290162289Sjhb return (1); /* Not Completed */ 29143166Snsouch} 292162289Sjhb 29343166Snsouchstatic int 294165951Sjhbintsmb_slvintr(struct intsmb_softc *sc) 29543166Snsouch{ 296165951Sjhb int status; 297162289Sjhb 298165951Sjhb status = bus_read_1(sc->io_res, PIIX4_SMBSLVSTS); 299162289Sjhb if (status & PIIX4_SMBSLVSTS_BUSY) 300165951Sjhb return (1); 301165951Sjhb if (status & PIIX4_SMBSLVSTS_ALART) 302165951Sjhb intsmb_alrintr(sc); 303165951Sjhb else if (status & ~(PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 304162289Sjhb | PIIX4_SMBSLVSTS_SDW1)) { 30543166Snsouch } 306162289Sjhb 307162289Sjhb /* Reset Status Register */ 308165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVSTS, 309162289Sjhb PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 | 310162289Sjhb PIIX4_SMBSLVSTS_SDW1 | PIIX4_SMBSLVSTS_SLV); 311165951Sjhb return (0); 31243166Snsouch} 31343166Snsouch 314162289Sjhbstatic void 315165951Sjhbintsmb_alrintr(struct intsmb_softc *sc) 31643166Snsouch{ 31743166Snsouch int slvcnt; 31843288Sdillon#ifdef ENABLE_ALART 31943288Sdillon int error; 320165951Sjhb uint8_t addr; 32143288Sdillon#endif 32243288Sdillon 323162289Sjhb /* Stop generating INTR from ALART. */ 324165951Sjhb slvcnt = bus_read_1(sc->io_res, PIIX4_SMBSLVCNT); 32543166Snsouch#ifdef ENABLE_ALART 326165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 327162289Sjhb slvcnt & ~PIIX4_SMBSLVCNT_ALTEN); 32843166Snsouch#endif 32943166Snsouch DELAY(5); 330162289Sjhb 331162289Sjhb /* Ask bus who asserted it and then ask it what's the matter. */ 33243166Snsouch#ifdef ENABLE_ALART 333165951Sjhb error = intsmb_free(sc); 334165951Sjhb if (error) 335165951Sjhb return; 336162289Sjhb 337165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, SMBALTRESP | LSB); 338165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 1); 339165951Sjhb error = intsmb_stop_poll(sc); 340165951Sjhb if (error) 341165951Sjhb device_printf(sc->dev, "ALART: ERROR\n"); 342165951Sjhb else { 343165951Sjhb addr = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 344165951Sjhb device_printf(sc->dev, "ALART_RESPONSE: 0x%x\n", addr); 345165951Sjhb } 34643166Snsouch 347162289Sjhb /* Re-enable INTR from ALART. */ 348165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 349162289Sjhb slvcnt | PIIX4_SMBSLVCNT_ALTEN); 35043166Snsouch DELAY(5); 35143166Snsouch#endif 352162289Sjhb} 35343166Snsouch 35443166Snsouchstatic void 355165951Sjhbintsmb_start(struct intsmb_softc *sc, unsigned char cmd, int nointr) 35643166Snsouch{ 35743166Snsouch unsigned char tmp; 358162289Sjhb 359165951Sjhb INTSMB_LOCK_ASSERT(sc); 360165951Sjhb tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 361162289Sjhb tmp &= 0xe0; 36243166Snsouch tmp |= cmd; 363162289Sjhb tmp |= PIIX4_SMBHSTCNT_START; 364162289Sjhb 365162289Sjhb /* While not in autoconfiguration enable interrupts. */ 366162289Sjhb if (!cold || !nointr) 367162289Sjhb tmp |= PIIX4_SMBHSTCNT_INTREN; 368165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp); 36943166Snsouch} 37043166Snsouch 371165951Sjhbstatic int 372165951Sjhbintsmb_error(int status) 373165951Sjhb{ 374165951Sjhb int error = 0; 375165951Sjhb 376165951Sjhb if (status & PIIX4_SMBHSTSTAT_ERR) 377165951Sjhb error |= SMB_EBUSERR; 378165951Sjhb if (status & PIIX4_SMBHSTSTAT_BUSC) 379165951Sjhb error |= SMB_ECOLLI; 380165951Sjhb if (status & PIIX4_SMBHSTSTAT_FAIL) 381165951Sjhb error |= SMB_ENOACK; 382165951Sjhb return (error); 383165951Sjhb} 384165951Sjhb 385162289Sjhb/* 386162289Sjhb * Polling Code. 387162289Sjhb * 388162289Sjhb * Polling is not encouraged because it requires waiting for the 389162289Sjhb * device if it is busy. 390162289Sjhb * (29063505.pdf from Intel) But during boot, interrupt cannot be used, so use 391162289Sjhb * polling code then. 39243166Snsouch */ 393162289Sjhbstatic int 394165951Sjhbintsmb_stop_poll(struct intsmb_softc *sc) 395162289Sjhb{ 396165951Sjhb int error, i, status, tmp; 39743166Snsouch 398165951Sjhb INTSMB_LOCK_ASSERT(sc); 399165951Sjhb 400165951Sjhb /* First, wait for busy to be set. */ 401162289Sjhb for (i = 0; i < 0x7fff; i++) 402165951Sjhb if (bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & 403162289Sjhb PIIX4_SMBHSTSTAT_BUSY) 404162289Sjhb break; 405162289Sjhb 406165951Sjhb /* Wait for busy to clear. */ 407162289Sjhb for (i = 0; i < 0x7fff; i++) { 408165951Sjhb status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 409162289Sjhb if (!(status & PIIX4_SMBHSTSTAT_BUSY)) { 410162289Sjhb sc->isbusy = 0; 411165951Sjhb error = intsmb_error(status); 412162289Sjhb if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR)) 413165951Sjhb device_printf(sc->dev, "unknown cause why?"); 414162289Sjhb return (error); 41543166Snsouch } 41643166Snsouch } 417162289Sjhb 418165951Sjhb /* Timed out waiting for busy to clear. */ 419162289Sjhb sc->isbusy = 0; 420165951Sjhb tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 421165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp & ~PIIX4_SMBHSTCNT_INTREN); 422165951Sjhb return (SMB_ETIMEOUT); 42343166Snsouch} 424162289Sjhb 42543166Snsouch/* 426162289Sjhb * Wait for completion and return result. 42743166Snsouch */ 428162289Sjhbstatic int 429165951Sjhbintsmb_stop(struct intsmb_softc *sc) 430162289Sjhb{ 431165951Sjhb int error, status; 432162289Sjhb 433165951Sjhb INTSMB_LOCK_ASSERT(sc); 434165951Sjhb 435165951Sjhb if (cold) 436162289Sjhb /* So that it can use device during device probe on SMBus. */ 437165951Sjhb return (intsmb_stop_poll(sc)); 438162289Sjhb 439172667Sjhb error = msleep(sc, &sc->lock, PWAIT | PCATCH, "SMBWAI", hz / 8); 440165951Sjhb if (error == 0) { 441165951Sjhb status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 442162289Sjhb if (!(status & PIIX4_SMBHSTSTAT_BUSY)) { 443165951Sjhb error = intsmb_error(status); 444162289Sjhb if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR)) 445165951Sjhb device_printf(sc->dev, "unknown cause why?\n"); 44643166Snsouch#ifdef ENABLE_ALART 447165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 448162289Sjhb PIIX4_SMBSLVCNT_ALTEN); 44943166Snsouch#endif 450162289Sjhb return (error); 45143166Snsouch } 45243166Snsouch } 453162289Sjhb 454162289Sjhb /* Timeout Procedure. */ 455162289Sjhb sc->isbusy = 0; 456162289Sjhb 457162289Sjhb /* Re-enable supressed interrupt from slave part. */ 458165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN); 459165951Sjhb if (error == EWOULDBLOCK) 460165951Sjhb return (SMB_ETIMEOUT); 461165951Sjhb else 462165951Sjhb return (SMB_EABORT); 46343166Snsouch} 46443166Snsouch 46543166Snsouchstatic int 46643166Snsouchintsmb_quick(device_t dev, u_char slave, int how) 46743166Snsouch{ 468162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 469165951Sjhb int error; 470162289Sjhb u_char data; 471162289Sjhb 472162289Sjhb data = slave; 473162289Sjhb 474162289Sjhb /* Quick command is part of Address, I think. */ 475162289Sjhb switch(how) { 476162289Sjhb case SMB_QWRITE: 477162289Sjhb data &= ~LSB; 47843166Snsouch break; 479162289Sjhb case SMB_QREAD: 480162289Sjhb data |= LSB; 481162289Sjhb break; 482162289Sjhb default: 483165951Sjhb return (EINVAL); 484162289Sjhb } 485165951Sjhb 486165951Sjhb INTSMB_LOCK(sc); 487165951Sjhb error = intsmb_free(sc); 488165951Sjhb if (error) { 489165951Sjhb INTSMB_UNLOCK(sc); 490165951Sjhb return (error); 491162289Sjhb } 492165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, data); 493165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_QUICK, 0); 494165951Sjhb error = intsmb_stop(sc); 495165951Sjhb INTSMB_UNLOCK(sc); 496162289Sjhb return (error); 49743166Snsouch} 49843166Snsouch 49943166Snsouchstatic int 50043166Snsouchintsmb_sendb(device_t dev, u_char slave, char byte) 50143166Snsouch{ 502162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 503162289Sjhb int error; 504162289Sjhb 505165951Sjhb INTSMB_LOCK(sc); 506165951Sjhb error = intsmb_free(sc); 507165951Sjhb if (error) { 508165951Sjhb INTSMB_UNLOCK(sc); 509165951Sjhb return (error); 510162289Sjhb } 511165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 512165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, byte); 513165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0); 514165951Sjhb error = intsmb_stop(sc); 515165951Sjhb INTSMB_UNLOCK(sc); 516162289Sjhb return (error); 51743166Snsouch} 518162289Sjhb 51943166Snsouchstatic int 52043166Snsouchintsmb_recvb(device_t dev, u_char slave, char *byte) 52143166Snsouch{ 522162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 523162289Sjhb int error; 524162289Sjhb 525165951Sjhb INTSMB_LOCK(sc); 526165951Sjhb error = intsmb_free(sc); 527165951Sjhb if (error) { 528165951Sjhb INTSMB_UNLOCK(sc); 529165951Sjhb return (error); 530165951Sjhb } 531165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 532165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0); 533165951Sjhb error = intsmb_stop(sc); 534165951Sjhb if (error == 0) { 53543166Snsouch#ifdef RECV_IS_IN_CMD 536165951Sjhb /* 537165951Sjhb * Linux SMBus stuff also troubles 538165951Sjhb * Because Intel's datasheet does not make clear. 539165951Sjhb */ 540165951Sjhb *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTCMD); 54143166Snsouch#else 542165951Sjhb *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 54343166Snsouch#endif 544162289Sjhb } 545165951Sjhb INTSMB_UNLOCK(sc); 546162289Sjhb return (error); 54743166Snsouch} 548162289Sjhb 54943166Snsouchstatic int 55043166Snsouchintsmb_writeb(device_t dev, u_char slave, char cmd, char byte) 55143166Snsouch{ 552162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 553162289Sjhb int error; 554162289Sjhb 555165951Sjhb INTSMB_LOCK(sc); 556165951Sjhb error = intsmb_free(sc); 557165951Sjhb if (error) { 558165951Sjhb INTSMB_UNLOCK(sc); 559165951Sjhb return (error); 560162289Sjhb } 561165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 562165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 563165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, byte); 564165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0); 565165951Sjhb error = intsmb_stop(sc); 566165951Sjhb INTSMB_UNLOCK(sc); 567162289Sjhb return (error); 56843166Snsouch} 569162289Sjhb 57043166Snsouchstatic int 57143166Snsouchintsmb_writew(device_t dev, u_char slave, char cmd, short word) 57243166Snsouch{ 573162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 574162289Sjhb int error; 575162289Sjhb 576165951Sjhb INTSMB_LOCK(sc); 577165951Sjhb error = intsmb_free(sc); 578165951Sjhb if (error) { 579165951Sjhb INTSMB_UNLOCK(sc); 580165951Sjhb return (error); 581162289Sjhb } 582165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 583165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 584165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, word & 0xff); 585165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (word >> 8) & 0xff); 586165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 587165951Sjhb error = intsmb_stop(sc); 588165951Sjhb INTSMB_UNLOCK(sc); 589162289Sjhb return (error); 59043166Snsouch} 59143166Snsouch 59243166Snsouchstatic int 59343166Snsouchintsmb_readb(device_t dev, u_char slave, char cmd, char *byte) 59443166Snsouch{ 595162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 596162289Sjhb int error; 597162289Sjhb 598165951Sjhb INTSMB_LOCK(sc); 599165951Sjhb error = intsmb_free(sc); 600165951Sjhb if (error) { 601165951Sjhb INTSMB_UNLOCK(sc); 602165951Sjhb return (error); 603162289Sjhb } 604165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 605165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 606165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0); 607165951Sjhb error = intsmb_stop(sc); 608165951Sjhb if (error == 0) 609165951Sjhb *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 610165951Sjhb INTSMB_UNLOCK(sc); 611162289Sjhb return (error); 61243166Snsouch} 613165951Sjhb 61443166Snsouchstatic int 61543166Snsouchintsmb_readw(device_t dev, u_char slave, char cmd, short *word) 61643166Snsouch{ 617162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 618162289Sjhb int error; 619162289Sjhb 620165951Sjhb INTSMB_LOCK(sc); 621165951Sjhb error = intsmb_free(sc); 622165951Sjhb if (error) { 623165951Sjhb INTSMB_UNLOCK(sc); 624165951Sjhb return (error); 625162289Sjhb } 626165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 627165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 628165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 629165951Sjhb error = intsmb_stop(sc); 630165951Sjhb if (error == 0) { 631165951Sjhb *word = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 632165951Sjhb *word |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8; 633165951Sjhb } 634165951Sjhb INTSMB_UNLOCK(sc); 635162289Sjhb return (error); 63643166Snsouch} 637162289Sjhb 63843166Snsouch/* 63943166Snsouch * Data sheet claims that it implements all function, but also claims 64043166Snsouch * that it implements 7 function and not mention PCALL. So I don't know 64143166Snsouch * whether it will work. 64243166Snsouch */ 64343166Snsouchstatic int 64443166Snsouchintsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata) 64543166Snsouch{ 64643166Snsouch#ifdef PROCCALL_TEST 647162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 648162289Sjhb int error; 649162289Sjhb 650165951Sjhb INTSMB_LOCK(sc); 651165951Sjhb error = intsmb_free(sc); 652165951Sjhb if (error) { 653165951Sjhb INTSMB_UNLOCK(sc); 654165951Sjhb return (error); 655162289Sjhb } 656165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 657165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 658165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, sdata & 0xff); 659165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (sdata & 0xff) >> 8); 660165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 661165951Sjhb error = intsmb_stop(sc); 662165951Sjhb if (error == 0) { 663165951Sjhb *rdata = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 664165951Sjhb *rdata |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8; 665162289Sjhb } 666165951Sjhb INTSMB_UNLOCK(sc); 667162289Sjhb return (error); 66843166Snsouch#else 669165951Sjhb return (SMB_ENOTSUPP); 67043166Snsouch#endif 67143166Snsouch} 672162289Sjhb 67343166Snsouchstatic int 67443166Snsouchintsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) 67543166Snsouch{ 676162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 677162289Sjhb int error, i; 678162289Sjhb 679162289Sjhb if (count > SMBBLOCKTRANS_MAX || count == 0) 680165951Sjhb return (SMB_EINVAL); 681162289Sjhb 682165951Sjhb INTSMB_LOCK(sc); 683165951Sjhb error = intsmb_free(sc); 684165951Sjhb if (error) { 685165951Sjhb INTSMB_UNLOCK(sc); 686165951Sjhb return (error); 687162289Sjhb } 688165951Sjhb 689165951Sjhb /* Reset internal array index. */ 690165951Sjhb bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 691165951Sjhb 692165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 693165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 694165951Sjhb for (i = 0; i < count; i++) 695165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBBLKDAT, buf[i]); 696165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, count); 697165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0); 698165951Sjhb error = intsmb_stop(sc); 699165951Sjhb INTSMB_UNLOCK(sc); 700162289Sjhb return (error); 70143166Snsouch} 70243166Snsouch 70343166Snsouchstatic int 704162234Sjhbintsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf) 70543166Snsouch{ 706162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 707162289Sjhb int error, i; 708162234Sjhb u_char data, nread; 709162289Sjhb 710162289Sjhb if (*count > SMBBLOCKTRANS_MAX || *count == 0) 711165951Sjhb return (SMB_EINVAL); 712162289Sjhb 713165951Sjhb INTSMB_LOCK(sc); 714165951Sjhb error = intsmb_free(sc); 715165951Sjhb if (error) { 716165951Sjhb INTSMB_UNLOCK(sc); 717165951Sjhb return (error); 718165951Sjhb } 719165951Sjhb 720165951Sjhb /* Reset internal array index. */ 721165951Sjhb bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 722165951Sjhb 723165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 724165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 725165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, *count); 726165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0); 727165951Sjhb error = intsmb_stop(sc); 728165951Sjhb if (error == 0) { 729165951Sjhb nread = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 730165951Sjhb if (nread != 0 && nread <= SMBBLOCKTRANS_MAX) { 731165951Sjhb for (i = 0; i < nread; i++) { 732165951Sjhb data = bus_read_1(sc->io_res, PIIX4_SMBBLKDAT); 733165951Sjhb if (i < *count) 734165951Sjhb buf[i] = data; 73543166Snsouch } 736165951Sjhb *count = nread; 737165951Sjhb } else 738165951Sjhb error = EIO; 73943166Snsouch } 740165951Sjhb INTSMB_UNLOCK(sc); 741162289Sjhb return (error); 74243166Snsouch} 74343166Snsouch 744165951Sjhbstatic devclass_t intsmb_devclass; 74543166Snsouch 746165951Sjhbstatic device_method_t intsmb_methods[] = { 747165951Sjhb /* Device interface */ 748165951Sjhb DEVMETHOD(device_probe, intsmb_probe), 749165951Sjhb DEVMETHOD(device_attach, intsmb_attach), 750165951Sjhb DEVMETHOD(device_detach, intsmb_detach), 75146651Speter 752165951Sjhb /* Bus interface */ 753165951Sjhb DEVMETHOD(bus_print_child, bus_generic_print_child), 75443166Snsouch 755165951Sjhb /* SMBus interface */ 756165951Sjhb DEVMETHOD(smbus_callback, intsmb_callback), 757165951Sjhb DEVMETHOD(smbus_quick, intsmb_quick), 758165951Sjhb DEVMETHOD(smbus_sendb, intsmb_sendb), 759165951Sjhb DEVMETHOD(smbus_recvb, intsmb_recvb), 760165951Sjhb DEVMETHOD(smbus_writeb, intsmb_writeb), 761165951Sjhb DEVMETHOD(smbus_writew, intsmb_writew), 762165951Sjhb DEVMETHOD(smbus_readb, intsmb_readb), 763165951Sjhb DEVMETHOD(smbus_readw, intsmb_readw), 764165951Sjhb DEVMETHOD(smbus_pcall, intsmb_pcall), 765165951Sjhb DEVMETHOD(smbus_bwrite, intsmb_bwrite), 766165951Sjhb DEVMETHOD(smbus_bread, intsmb_bread), 767162289Sjhb 768165951Sjhb { 0, 0 } 769165951Sjhb}; 77043166Snsouch 771165951Sjhbstatic driver_t intsmb_driver = { 772165951Sjhb "intsmb", 773165951Sjhb intsmb_methods, 774165951Sjhb sizeof(struct intsmb_softc), 775165951Sjhb}; 776162289Sjhb 777165951SjhbDRIVER_MODULE(intsmb, pci, intsmb_driver, intsmb_devclass, 0, 0); 778162289SjhbDRIVER_MODULE(smbus, intsmb, smbus_driver, smbus_devclass, 0, 0); 779165951SjhbMODULE_DEPEND(intsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 780165951SjhbMODULE_VERSION(intsmb, 1); 781