intpm.c revision 179622
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 179622 2008-06-06 18:29:56Z 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 113178972Sjhb sc->dev = dev; 114178972Sjhb 115165951Sjhb mtx_init(&sc->lock, device_get_nameunit(dev), "intsmb", MTX_DEF); 11643166Snsouch 117165951Sjhb rid = PCI_BASE_ADDR_SMB; 118165951Sjhb sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 119165951Sjhb RF_ACTIVE); 120165951Sjhb if (sc->io_res == NULL) { 121165951Sjhb device_printf(dev, "Could not allocate I/O space\n"); 122165951Sjhb error = ENXIO; 123165951Sjhb goto fail; 124165951Sjhb } 12543166Snsouch 126165951Sjhb#ifndef NO_CHANGE_PCICONF 127165951Sjhb pci_write_config(dev, PCIR_INTLINE, 0x9, 1); 128165951Sjhb pci_write_config(dev, PCI_HST_CFG_SMB, 129165951Sjhb PCI_INTR_SMB_IRQ9 | PCI_INTR_SMB_ENABLE, 1); 130165951Sjhb#endif 131165951Sjhb value = pci_read_config(dev, PCI_HST_CFG_SMB, 1); 132165951Sjhb switch (value & 0xe) { 133165951Sjhb case PCI_INTR_SMB_SMI: 134165951Sjhb str = "SMI"; 135165951Sjhb break; 136165951Sjhb case PCI_INTR_SMB_IRQ9: 137165951Sjhb str = "IRQ 9"; 138165951Sjhb break; 139165951Sjhb default: 140165951Sjhb str = "BOGUS"; 141165951Sjhb } 142165951Sjhb device_printf(dev, "intr %s %s ", str, 143165951Sjhb (value & 1) ? "enabled" : "disabled"); 144168870Sjhb printf("revision %d\n", pci_read_config(dev, PCI_REVID_SMB, 1)); 14543166Snsouch 146165951Sjhb if ((value & 0xe) != PCI_INTR_SMB_IRQ9) { 147165951Sjhb device_printf(dev, "Unsupported interrupt mode\n"); 148165951Sjhb error = ENXIO; 149165951Sjhb goto fail; 150165951Sjhb } 15146651Speter 152165951Sjhb /* Force IRQ 9. */ 153165951Sjhb rid = 0; 154165951Sjhb bus_set_resource(dev, SYS_RES_IRQ, rid, 9, 1); 155165951Sjhb sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 156165951Sjhb RF_SHAREABLE | RF_ACTIVE); 157165951Sjhb if (sc->irq_res == NULL) { 158165951Sjhb device_printf(dev, "Could not allocate irq\n"); 159165951Sjhb error = ENXIO; 160165951Sjhb goto fail; 161165951Sjhb } 16243166Snsouch 163179622Sjhb error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 164179622Sjhb NULL, intsmb_rawintr, sc, &sc->irq_hand); 165165951Sjhb if (error) { 166165951Sjhb device_printf(dev, "Failed to map intr\n"); 167165951Sjhb goto fail; 168165951Sjhb } 169162289Sjhb 170165951Sjhb value = pci_read_config(dev, PCI_BASE_ADDR_PM, 4); 171165951Sjhb device_printf(dev, "PM %s %x\n", (value & 1) ? "I/O mapped" : "Memory", 172165951Sjhb value & 0xfffe); 173162289Sjhb 174165951Sjhb sc->isbusy = 0; 175165951Sjhb sc->smbus = device_add_child(dev, "smbus", -1); 176165951Sjhb if (sc->smbus == NULL) { 177165951Sjhb error = ENXIO; 178165951Sjhb goto fail; 179165951Sjhb } 180165951Sjhb error = device_probe_and_attach(sc->smbus); 181165951Sjhb if (error) 182165951Sjhb goto fail; 183162289Sjhb 184165951Sjhb#ifdef ENABLE_ALART 185165951Sjhb /* Enable Arart */ 186165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN); 187165951Sjhb#endif 188165951Sjhb return (0); 18943166Snsouch 190165951Sjhbfail: 191165951Sjhb intsmb_detach(dev); 192165951Sjhb return (error); 193165951Sjhb} 194165951Sjhb 195162289Sjhbstatic int 196165951Sjhbintsmb_detach(device_t dev) 19743166Snsouch{ 198162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 199165951Sjhb int error; 200162289Sjhb 201165951Sjhb error = bus_generic_detach(dev); 202165951Sjhb if (error) 203165951Sjhb return (error); 204162289Sjhb 205165951Sjhb if (sc->smbus) 206165951Sjhb device_delete_child(dev, sc->smbus); 207165951Sjhb if (sc->irq_hand) 208165951Sjhb bus_teardown_intr(dev, sc->irq_res, sc->irq_hand); 209165951Sjhb if (sc->irq_res) 210165951Sjhb bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 211165951Sjhb if (sc->io_res) 212165951Sjhb bus_release_resource(dev, SYS_RES_IOPORT, PCI_BASE_ADDR_SMB, 213165951Sjhb sc->io_res); 214165951Sjhb mtx_destroy(&sc->lock); 215165951Sjhb return (0); 21643166Snsouch} 217165951Sjhb 218165951Sjhbstatic void 219165951Sjhbintsmb_rawintr(void *arg) 22043166Snsouch{ 221165951Sjhb struct intsmb_softc *sc = arg; 222162289Sjhb 223165951Sjhb INTSMB_LOCK(sc); 224165951Sjhb intsmb_intr(sc); 225165951Sjhb intsmb_slvintr(sc); 226165951Sjhb INTSMB_UNLOCK(sc); 22743166Snsouch} 22843166Snsouch 229162289Sjhbstatic int 230162234Sjhbintsmb_callback(device_t dev, int index, void *data) 23143166Snsouch{ 23243166Snsouch int error = 0; 233162289Sjhb 23443166Snsouch switch (index) { 23543166Snsouch case SMB_REQUEST_BUS: 23643166Snsouch break; 23743166Snsouch case SMB_RELEASE_BUS: 23843166Snsouch break; 23943166Snsouch default: 24043166Snsouch error = EINVAL; 24143166Snsouch } 242162289Sjhb 24343166Snsouch return (error); 24443166Snsouch} 245162289Sjhb 246162289Sjhb/* Counterpart of smbtx_smb_free(). */ 247162289Sjhbstatic int 248165951Sjhbintsmb_free(struct intsmb_softc *sc) 249162289Sjhb{ 250162289Sjhb 251165951Sjhb INTSMB_LOCK_ASSERT(sc); 252165951Sjhb if ((bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & PIIX4_SMBHSTSTAT_BUSY) || 25343166Snsouch#ifdef ENABLE_ALART 254165951Sjhb (bus_read_1(sc->io_res, PIIX4_SMBSLVSTS) & PIIX4_SMBSLVSTS_BUSY) || 25543166Snsouch#endif 256162289Sjhb sc->isbusy) 257165951Sjhb return (SMB_EBUSY); 258165951Sjhb 259162289Sjhb sc->isbusy = 1; 260162289Sjhb /* Disable Interrupt in slave part. */ 26143166Snsouch#ifndef ENABLE_ALART 262165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 0); 26343166Snsouch#endif 264162289Sjhb /* Reset INTR Flag to prepare INTR. */ 265165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTSTS, 266165951Sjhb PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR | 267165951Sjhb PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL); 268162289Sjhb return (0); 26943166Snsouch} 27043166Snsouch 27143166Snsouchstatic int 272165951Sjhbintsmb_intr(struct intsmb_softc *sc) 27343166Snsouch{ 274165951Sjhb int status, tmp; 275162289Sjhb 276165951Sjhb status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 277162289Sjhb if (status & PIIX4_SMBHSTSTAT_BUSY) 278162289Sjhb return (1); 279162289Sjhb 280162289Sjhb if (status & (PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR | 281162289Sjhb PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL)) { 282162289Sjhb 283165951Sjhb tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 284165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, 285162289Sjhb tmp & ~PIIX4_SMBHSTCNT_INTREN); 286162289Sjhb if (sc->isbusy) { 287162289Sjhb sc->isbusy = 0; 288162289Sjhb wakeup(sc); 28949064Snsouch } 290162289Sjhb return (0); 29143166Snsouch } 292162289Sjhb return (1); /* Not Completed */ 29343166Snsouch} 294162289Sjhb 29543166Snsouchstatic int 296165951Sjhbintsmb_slvintr(struct intsmb_softc *sc) 29743166Snsouch{ 298165951Sjhb int status; 299162289Sjhb 300165951Sjhb status = bus_read_1(sc->io_res, PIIX4_SMBSLVSTS); 301162289Sjhb if (status & PIIX4_SMBSLVSTS_BUSY) 302165951Sjhb return (1); 303165951Sjhb if (status & PIIX4_SMBSLVSTS_ALART) 304165951Sjhb intsmb_alrintr(sc); 305165951Sjhb else if (status & ~(PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 306162289Sjhb | PIIX4_SMBSLVSTS_SDW1)) { 30743166Snsouch } 308162289Sjhb 309162289Sjhb /* Reset Status Register */ 310165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVSTS, 311162289Sjhb PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 | 312162289Sjhb PIIX4_SMBSLVSTS_SDW1 | PIIX4_SMBSLVSTS_SLV); 313165951Sjhb return (0); 31443166Snsouch} 31543166Snsouch 316162289Sjhbstatic void 317165951Sjhbintsmb_alrintr(struct intsmb_softc *sc) 31843166Snsouch{ 31943166Snsouch int slvcnt; 32043288Sdillon#ifdef ENABLE_ALART 32143288Sdillon int error; 322165951Sjhb uint8_t addr; 32343288Sdillon#endif 32443288Sdillon 325162289Sjhb /* Stop generating INTR from ALART. */ 326165951Sjhb slvcnt = bus_read_1(sc->io_res, PIIX4_SMBSLVCNT); 32743166Snsouch#ifdef ENABLE_ALART 328165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 329162289Sjhb slvcnt & ~PIIX4_SMBSLVCNT_ALTEN); 33043166Snsouch#endif 33143166Snsouch DELAY(5); 332162289Sjhb 333162289Sjhb /* Ask bus who asserted it and then ask it what's the matter. */ 33443166Snsouch#ifdef ENABLE_ALART 335165951Sjhb error = intsmb_free(sc); 336165951Sjhb if (error) 337165951Sjhb return; 338162289Sjhb 339165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, SMBALTRESP | LSB); 340165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 1); 341165951Sjhb error = intsmb_stop_poll(sc); 342165951Sjhb if (error) 343165951Sjhb device_printf(sc->dev, "ALART: ERROR\n"); 344165951Sjhb else { 345165951Sjhb addr = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 346165951Sjhb device_printf(sc->dev, "ALART_RESPONSE: 0x%x\n", addr); 347165951Sjhb } 34843166Snsouch 349162289Sjhb /* Re-enable INTR from ALART. */ 350165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 351162289Sjhb slvcnt | PIIX4_SMBSLVCNT_ALTEN); 35243166Snsouch DELAY(5); 35343166Snsouch#endif 354162289Sjhb} 35543166Snsouch 35643166Snsouchstatic void 357165951Sjhbintsmb_start(struct intsmb_softc *sc, unsigned char cmd, int nointr) 35843166Snsouch{ 35943166Snsouch unsigned char tmp; 360162289Sjhb 361165951Sjhb INTSMB_LOCK_ASSERT(sc); 362165951Sjhb tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 363162289Sjhb tmp &= 0xe0; 36443166Snsouch tmp |= cmd; 365162289Sjhb tmp |= PIIX4_SMBHSTCNT_START; 366162289Sjhb 367162289Sjhb /* While not in autoconfiguration enable interrupts. */ 368162289Sjhb if (!cold || !nointr) 369162289Sjhb tmp |= PIIX4_SMBHSTCNT_INTREN; 370165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp); 37143166Snsouch} 37243166Snsouch 373165951Sjhbstatic int 374165951Sjhbintsmb_error(int status) 375165951Sjhb{ 376165951Sjhb int error = 0; 377165951Sjhb 378165951Sjhb if (status & PIIX4_SMBHSTSTAT_ERR) 379165951Sjhb error |= SMB_EBUSERR; 380165951Sjhb if (status & PIIX4_SMBHSTSTAT_BUSC) 381165951Sjhb error |= SMB_ECOLLI; 382165951Sjhb if (status & PIIX4_SMBHSTSTAT_FAIL) 383165951Sjhb error |= SMB_ENOACK; 384165951Sjhb return (error); 385165951Sjhb} 386165951Sjhb 387162289Sjhb/* 388162289Sjhb * Polling Code. 389162289Sjhb * 390162289Sjhb * Polling is not encouraged because it requires waiting for the 391162289Sjhb * device if it is busy. 392162289Sjhb * (29063505.pdf from Intel) But during boot, interrupt cannot be used, so use 393162289Sjhb * polling code then. 39443166Snsouch */ 395162289Sjhbstatic int 396165951Sjhbintsmb_stop_poll(struct intsmb_softc *sc) 397162289Sjhb{ 398165951Sjhb int error, i, status, tmp; 39943166Snsouch 400165951Sjhb INTSMB_LOCK_ASSERT(sc); 401165951Sjhb 402165951Sjhb /* First, wait for busy to be set. */ 403162289Sjhb for (i = 0; i < 0x7fff; i++) 404165951Sjhb if (bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & 405162289Sjhb PIIX4_SMBHSTSTAT_BUSY) 406162289Sjhb break; 407162289Sjhb 408165951Sjhb /* Wait for busy to clear. */ 409162289Sjhb for (i = 0; i < 0x7fff; i++) { 410165951Sjhb status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 411162289Sjhb if (!(status & PIIX4_SMBHSTSTAT_BUSY)) { 412162289Sjhb sc->isbusy = 0; 413165951Sjhb error = intsmb_error(status); 414162289Sjhb if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR)) 415178972Sjhb device_printf(sc->dev, "unknown cause why?\n"); 416162289Sjhb return (error); 41743166Snsouch } 41843166Snsouch } 419162289Sjhb 420165951Sjhb /* Timed out waiting for busy to clear. */ 421162289Sjhb sc->isbusy = 0; 422165951Sjhb tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 423165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp & ~PIIX4_SMBHSTCNT_INTREN); 424165951Sjhb return (SMB_ETIMEOUT); 42543166Snsouch} 426162289Sjhb 42743166Snsouch/* 428162289Sjhb * Wait for completion and return result. 42943166Snsouch */ 430162289Sjhbstatic int 431165951Sjhbintsmb_stop(struct intsmb_softc *sc) 432162289Sjhb{ 433165951Sjhb int error, status; 434162289Sjhb 435165951Sjhb INTSMB_LOCK_ASSERT(sc); 436165951Sjhb 437165951Sjhb if (cold) 438162289Sjhb /* So that it can use device during device probe on SMBus. */ 439165951Sjhb return (intsmb_stop_poll(sc)); 440162289Sjhb 441172667Sjhb error = msleep(sc, &sc->lock, PWAIT | PCATCH, "SMBWAI", hz / 8); 442165951Sjhb if (error == 0) { 443165951Sjhb status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 444162289Sjhb if (!(status & PIIX4_SMBHSTSTAT_BUSY)) { 445165951Sjhb error = intsmb_error(status); 446162289Sjhb if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR)) 447165951Sjhb device_printf(sc->dev, "unknown cause why?\n"); 44843166Snsouch#ifdef ENABLE_ALART 449165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 450162289Sjhb PIIX4_SMBSLVCNT_ALTEN); 45143166Snsouch#endif 452162289Sjhb return (error); 45343166Snsouch } 45443166Snsouch } 455162289Sjhb 456162289Sjhb /* Timeout Procedure. */ 457162289Sjhb sc->isbusy = 0; 458162289Sjhb 459162289Sjhb /* Re-enable supressed interrupt from slave part. */ 460165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN); 461165951Sjhb if (error == EWOULDBLOCK) 462165951Sjhb return (SMB_ETIMEOUT); 463165951Sjhb else 464165951Sjhb return (SMB_EABORT); 46543166Snsouch} 46643166Snsouch 46743166Snsouchstatic int 46843166Snsouchintsmb_quick(device_t dev, u_char slave, int how) 46943166Snsouch{ 470162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 471165951Sjhb int error; 472162289Sjhb u_char data; 473162289Sjhb 474162289Sjhb data = slave; 475162289Sjhb 476162289Sjhb /* Quick command is part of Address, I think. */ 477162289Sjhb switch(how) { 478162289Sjhb case SMB_QWRITE: 479162289Sjhb data &= ~LSB; 48043166Snsouch break; 481162289Sjhb case SMB_QREAD: 482162289Sjhb data |= LSB; 483162289Sjhb break; 484162289Sjhb default: 485165951Sjhb return (EINVAL); 486162289Sjhb } 487165951Sjhb 488165951Sjhb INTSMB_LOCK(sc); 489165951Sjhb error = intsmb_free(sc); 490165951Sjhb if (error) { 491165951Sjhb INTSMB_UNLOCK(sc); 492165951Sjhb return (error); 493162289Sjhb } 494165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, data); 495165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_QUICK, 0); 496165951Sjhb error = intsmb_stop(sc); 497165951Sjhb INTSMB_UNLOCK(sc); 498162289Sjhb return (error); 49943166Snsouch} 50043166Snsouch 50143166Snsouchstatic int 50243166Snsouchintsmb_sendb(device_t dev, u_char slave, char byte) 50343166Snsouch{ 504162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 505162289Sjhb int error; 506162289Sjhb 507165951Sjhb INTSMB_LOCK(sc); 508165951Sjhb error = intsmb_free(sc); 509165951Sjhb if (error) { 510165951Sjhb INTSMB_UNLOCK(sc); 511165951Sjhb return (error); 512162289Sjhb } 513165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 514165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, byte); 515165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0); 516165951Sjhb error = intsmb_stop(sc); 517165951Sjhb INTSMB_UNLOCK(sc); 518162289Sjhb return (error); 51943166Snsouch} 520162289Sjhb 52143166Snsouchstatic int 52243166Snsouchintsmb_recvb(device_t dev, u_char slave, char *byte) 52343166Snsouch{ 524162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 525162289Sjhb int error; 526162289Sjhb 527165951Sjhb INTSMB_LOCK(sc); 528165951Sjhb error = intsmb_free(sc); 529165951Sjhb if (error) { 530165951Sjhb INTSMB_UNLOCK(sc); 531165951Sjhb return (error); 532165951Sjhb } 533165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 534165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0); 535165951Sjhb error = intsmb_stop(sc); 536165951Sjhb if (error == 0) { 53743166Snsouch#ifdef RECV_IS_IN_CMD 538165951Sjhb /* 539165951Sjhb * Linux SMBus stuff also troubles 540165951Sjhb * Because Intel's datasheet does not make clear. 541165951Sjhb */ 542165951Sjhb *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTCMD); 54343166Snsouch#else 544165951Sjhb *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 54543166Snsouch#endif 546162289Sjhb } 547165951Sjhb INTSMB_UNLOCK(sc); 548162289Sjhb return (error); 54943166Snsouch} 550162289Sjhb 55143166Snsouchstatic int 55243166Snsouchintsmb_writeb(device_t dev, u_char slave, char cmd, char byte) 55343166Snsouch{ 554162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 555162289Sjhb int error; 556162289Sjhb 557165951Sjhb INTSMB_LOCK(sc); 558165951Sjhb error = intsmb_free(sc); 559165951Sjhb if (error) { 560165951Sjhb INTSMB_UNLOCK(sc); 561165951Sjhb return (error); 562162289Sjhb } 563165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 564165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 565165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, byte); 566165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0); 567165951Sjhb error = intsmb_stop(sc); 568165951Sjhb INTSMB_UNLOCK(sc); 569162289Sjhb return (error); 57043166Snsouch} 571162289Sjhb 57243166Snsouchstatic int 57343166Snsouchintsmb_writew(device_t dev, u_char slave, char cmd, short word) 57443166Snsouch{ 575162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 576162289Sjhb int error; 577162289Sjhb 578165951Sjhb INTSMB_LOCK(sc); 579165951Sjhb error = intsmb_free(sc); 580165951Sjhb if (error) { 581165951Sjhb INTSMB_UNLOCK(sc); 582165951Sjhb return (error); 583162289Sjhb } 584165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 585165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 586165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, word & 0xff); 587165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (word >> 8) & 0xff); 588165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 589165951Sjhb error = intsmb_stop(sc); 590165951Sjhb INTSMB_UNLOCK(sc); 591162289Sjhb return (error); 59243166Snsouch} 59343166Snsouch 59443166Snsouchstatic int 59543166Snsouchintsmb_readb(device_t dev, u_char slave, char cmd, char *byte) 59643166Snsouch{ 597162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 598162289Sjhb int error; 599162289Sjhb 600165951Sjhb INTSMB_LOCK(sc); 601165951Sjhb error = intsmb_free(sc); 602165951Sjhb if (error) { 603165951Sjhb INTSMB_UNLOCK(sc); 604165951Sjhb return (error); 605162289Sjhb } 606165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 607165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 608165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0); 609165951Sjhb error = intsmb_stop(sc); 610165951Sjhb if (error == 0) 611165951Sjhb *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 612165951Sjhb INTSMB_UNLOCK(sc); 613162289Sjhb return (error); 61443166Snsouch} 615165951Sjhb 61643166Snsouchstatic int 61743166Snsouchintsmb_readw(device_t dev, u_char slave, char cmd, short *word) 61843166Snsouch{ 619162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 620162289Sjhb int error; 621162289Sjhb 622165951Sjhb INTSMB_LOCK(sc); 623165951Sjhb error = intsmb_free(sc); 624165951Sjhb if (error) { 625165951Sjhb INTSMB_UNLOCK(sc); 626165951Sjhb return (error); 627162289Sjhb } 628165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 629165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 630165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 631165951Sjhb error = intsmb_stop(sc); 632165951Sjhb if (error == 0) { 633165951Sjhb *word = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 634165951Sjhb *word |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8; 635165951Sjhb } 636165951Sjhb INTSMB_UNLOCK(sc); 637162289Sjhb return (error); 63843166Snsouch} 639162289Sjhb 64043166Snsouch/* 64143166Snsouch * Data sheet claims that it implements all function, but also claims 64243166Snsouch * that it implements 7 function and not mention PCALL. So I don't know 64343166Snsouch * whether it will work. 64443166Snsouch */ 64543166Snsouchstatic int 64643166Snsouchintsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata) 64743166Snsouch{ 64843166Snsouch#ifdef PROCCALL_TEST 649162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 650162289Sjhb int error; 651162289Sjhb 652165951Sjhb INTSMB_LOCK(sc); 653165951Sjhb error = intsmb_free(sc); 654165951Sjhb if (error) { 655165951Sjhb INTSMB_UNLOCK(sc); 656165951Sjhb return (error); 657162289Sjhb } 658165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 659165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 660165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, sdata & 0xff); 661165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (sdata & 0xff) >> 8); 662165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 663165951Sjhb error = intsmb_stop(sc); 664165951Sjhb if (error == 0) { 665165951Sjhb *rdata = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 666165951Sjhb *rdata |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8; 667162289Sjhb } 668165951Sjhb INTSMB_UNLOCK(sc); 669162289Sjhb return (error); 67043166Snsouch#else 671165951Sjhb return (SMB_ENOTSUPP); 67243166Snsouch#endif 67343166Snsouch} 674162289Sjhb 67543166Snsouchstatic int 67643166Snsouchintsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) 67743166Snsouch{ 678162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 679162289Sjhb int error, i; 680162289Sjhb 681162289Sjhb if (count > SMBBLOCKTRANS_MAX || count == 0) 682165951Sjhb return (SMB_EINVAL); 683162289Sjhb 684165951Sjhb INTSMB_LOCK(sc); 685165951Sjhb error = intsmb_free(sc); 686165951Sjhb if (error) { 687165951Sjhb INTSMB_UNLOCK(sc); 688165951Sjhb return (error); 689162289Sjhb } 690165951Sjhb 691165951Sjhb /* Reset internal array index. */ 692165951Sjhb bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 693165951Sjhb 694165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 695165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 696165951Sjhb for (i = 0; i < count; i++) 697165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBBLKDAT, buf[i]); 698165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, count); 699165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0); 700165951Sjhb error = intsmb_stop(sc); 701165951Sjhb INTSMB_UNLOCK(sc); 702162289Sjhb return (error); 70343166Snsouch} 70443166Snsouch 70543166Snsouchstatic int 706162234Sjhbintsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf) 70743166Snsouch{ 708162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 709162289Sjhb int error, i; 710162234Sjhb u_char data, nread; 711162289Sjhb 712162289Sjhb if (*count > SMBBLOCKTRANS_MAX || *count == 0) 713165951Sjhb return (SMB_EINVAL); 714162289Sjhb 715165951Sjhb INTSMB_LOCK(sc); 716165951Sjhb error = intsmb_free(sc); 717165951Sjhb if (error) { 718165951Sjhb INTSMB_UNLOCK(sc); 719165951Sjhb return (error); 720165951Sjhb } 721165951Sjhb 722165951Sjhb /* Reset internal array index. */ 723165951Sjhb bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 724165951Sjhb 725165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 726165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 727165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, *count); 728165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0); 729165951Sjhb error = intsmb_stop(sc); 730165951Sjhb if (error == 0) { 731165951Sjhb nread = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 732165951Sjhb if (nread != 0 && nread <= SMBBLOCKTRANS_MAX) { 733165951Sjhb for (i = 0; i < nread; i++) { 734165951Sjhb data = bus_read_1(sc->io_res, PIIX4_SMBBLKDAT); 735165951Sjhb if (i < *count) 736165951Sjhb buf[i] = data; 73743166Snsouch } 738165951Sjhb *count = nread; 739165951Sjhb } else 740165951Sjhb error = EIO; 74143166Snsouch } 742165951Sjhb INTSMB_UNLOCK(sc); 743162289Sjhb return (error); 74443166Snsouch} 74543166Snsouch 746165951Sjhbstatic devclass_t intsmb_devclass; 74743166Snsouch 748165951Sjhbstatic device_method_t intsmb_methods[] = { 749165951Sjhb /* Device interface */ 750165951Sjhb DEVMETHOD(device_probe, intsmb_probe), 751165951Sjhb DEVMETHOD(device_attach, intsmb_attach), 752165951Sjhb DEVMETHOD(device_detach, intsmb_detach), 75346651Speter 754165951Sjhb /* Bus interface */ 755165951Sjhb DEVMETHOD(bus_print_child, bus_generic_print_child), 75643166Snsouch 757165951Sjhb /* SMBus interface */ 758165951Sjhb DEVMETHOD(smbus_callback, intsmb_callback), 759165951Sjhb DEVMETHOD(smbus_quick, intsmb_quick), 760165951Sjhb DEVMETHOD(smbus_sendb, intsmb_sendb), 761165951Sjhb DEVMETHOD(smbus_recvb, intsmb_recvb), 762165951Sjhb DEVMETHOD(smbus_writeb, intsmb_writeb), 763165951Sjhb DEVMETHOD(smbus_writew, intsmb_writew), 764165951Sjhb DEVMETHOD(smbus_readb, intsmb_readb), 765165951Sjhb DEVMETHOD(smbus_readw, intsmb_readw), 766165951Sjhb DEVMETHOD(smbus_pcall, intsmb_pcall), 767165951Sjhb DEVMETHOD(smbus_bwrite, intsmb_bwrite), 768165951Sjhb DEVMETHOD(smbus_bread, intsmb_bread), 769162289Sjhb 770165951Sjhb { 0, 0 } 771165951Sjhb}; 77243166Snsouch 773165951Sjhbstatic driver_t intsmb_driver = { 774165951Sjhb "intsmb", 775165951Sjhb intsmb_methods, 776165951Sjhb sizeof(struct intsmb_softc), 777165951Sjhb}; 778162289Sjhb 779165951SjhbDRIVER_MODULE(intsmb, pci, intsmb_driver, intsmb_devclass, 0, 0); 780162289SjhbDRIVER_MODULE(smbus, intsmb, smbus_driver, smbus_devclass, 0, 0); 781165951SjhbMODULE_DEPEND(intsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 782165951SjhbMODULE_VERSION(intsmb, 1); 783