intpm.c revision 197325
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 197325 2009-09-19 08:56:28Z avg $"); 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; 56197128Savg int cfg_irq9; 57197128Savg int poll; 58165951Sjhb struct mtx lock; 5943166Snsouch}; 60162289Sjhb 61165951Sjhb#define INTSMB_LOCK(sc) mtx_lock(&(sc)->lock) 62165951Sjhb#define INTSMB_UNLOCK(sc) mtx_unlock(&(sc)->lock) 63165951Sjhb#define INTSMB_LOCK_ASSERT(sc) mtx_assert(&(sc)->lock, MA_OWNED) 64165951Sjhb 6543166Snsouchstatic int intsmb_probe(device_t); 6643166Snsouchstatic int intsmb_attach(device_t); 67165951Sjhbstatic int intsmb_detach(device_t); 68165951Sjhbstatic int intsmb_intr(struct intsmb_softc *sc); 69165951Sjhbstatic int intsmb_slvintr(struct intsmb_softc *sc); 70165951Sjhbstatic void intsmb_alrintr(struct intsmb_softc *sc); 71162234Sjhbstatic int intsmb_callback(device_t dev, int index, void *data); 7243166Snsouchstatic int intsmb_quick(device_t dev, u_char slave, int how); 7343166Snsouchstatic int intsmb_sendb(device_t dev, u_char slave, char byte); 7443166Snsouchstatic int intsmb_recvb(device_t dev, u_char slave, char *byte); 7543166Snsouchstatic int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte); 7643166Snsouchstatic int intsmb_writew(device_t dev, u_char slave, char cmd, short word); 7743166Snsouchstatic int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte); 7843166Snsouchstatic int intsmb_readw(device_t dev, u_char slave, char cmd, short *word); 7943166Snsouchstatic int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata); 8043166Snsouchstatic int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf); 81162234Sjhbstatic int intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf); 82165951Sjhbstatic void intsmb_start(struct intsmb_softc *sc, u_char cmd, int nointr); 83165951Sjhbstatic int intsmb_stop(struct intsmb_softc *sc); 84165951Sjhbstatic int intsmb_stop_poll(struct intsmb_softc *sc); 85165951Sjhbstatic int intsmb_free(struct intsmb_softc *sc); 86165951Sjhbstatic void intsmb_rawintr(void *arg); 87162289Sjhb 88165951Sjhbstatic int 89165951Sjhbintsmb_probe(device_t dev) 90165951Sjhb{ 9143166Snsouch 92165951Sjhb switch (pci_get_devid(dev)) { 93165951Sjhb case 0x71138086: /* Intel 82371AB */ 94165951Sjhb case 0x719b8086: /* Intel 82443MX */ 95165951Sjhb#if 0 96165951Sjhb /* Not a good idea yet, this stops isab0 functioning */ 97165951Sjhb case 0x02001166: /* ServerWorks OSB4 */ 98165951Sjhb#endif 99165951Sjhb device_set_desc(dev, "Intel PIIX4 SMBUS Interface"); 100165951Sjhb break; 101197128Savg case 0x43851002: 102197128Savg device_set_desc(dev, "AMD SB600/700/710/750 SMBus Controller"); 103197128Savg /* XXX Maybe force polling right here? */ 104197128Savg break; 105165951Sjhb default: 106165951Sjhb return (ENXIO); 107165951Sjhb } 10843166Snsouch 109165951Sjhb return (BUS_PROBE_DEFAULT); 110165951Sjhb} 111162289Sjhb 112165951Sjhbstatic int 113165951Sjhbintsmb_attach(device_t dev) 114165951Sjhb{ 115165951Sjhb struct intsmb_softc *sc = device_get_softc(dev); 116165951Sjhb int error, rid, value; 117197128Savg int intr; 118165951Sjhb char *str; 119162289Sjhb 120178972Sjhb sc->dev = dev; 121178972Sjhb 122165951Sjhb mtx_init(&sc->lock, device_get_nameunit(dev), "intsmb", MTX_DEF); 12343166Snsouch 124197128Savg sc->cfg_irq9 = 0; 125197128Savg#ifndef NO_CHANGE_PCICONF 126197128Savg switch (pci_get_devid(dev)) { 127197128Savg case 0x71138086: /* Intel 82371AB */ 128197128Savg case 0x719b8086: /* Intel 82443MX */ 129197128Savg /* Changing configuration is allowed. */ 130197128Savg sc->cfg_irq9 = 1; 131197128Savg break; 132197128Savg } 133197128Savg#endif 134197128Savg 135165951Sjhb rid = PCI_BASE_ADDR_SMB; 136165951Sjhb sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 137165951Sjhb RF_ACTIVE); 138165951Sjhb if (sc->io_res == NULL) { 139165951Sjhb device_printf(dev, "Could not allocate I/O space\n"); 140165951Sjhb error = ENXIO; 141165951Sjhb goto fail; 142165951Sjhb } 14343166Snsouch 144197128Savg if (sc->cfg_irq9) { 145197128Savg pci_write_config(dev, PCIR_INTLINE, 0x9, 1); 146197128Savg pci_write_config(dev, PCI_HST_CFG_SMB, 147197128Savg PCI_INTR_SMB_IRQ9 | PCI_INTR_SMB_ENABLE, 1); 148197128Savg } 149165951Sjhb value = pci_read_config(dev, PCI_HST_CFG_SMB, 1); 150197128Savg sc->poll = (value & PCI_INTR_SMB_ENABLE) == 0; 151197128Savg intr = value & PCI_INTR_SMB_MASK; 152197128Savg switch (intr) { 153165951Sjhb case PCI_INTR_SMB_SMI: 154165951Sjhb str = "SMI"; 155165951Sjhb break; 156165951Sjhb case PCI_INTR_SMB_IRQ9: 157165951Sjhb str = "IRQ 9"; 158165951Sjhb break; 159197128Savg case PCI_INTR_SMB_IRQ_PCI: 160197128Savg str = "PCI IRQ"; 161197128Savg break; 162165951Sjhb default: 163165951Sjhb str = "BOGUS"; 164165951Sjhb } 165197128Savg 166165951Sjhb device_printf(dev, "intr %s %s ", str, 167197128Savg sc->poll == 0 ? "enabled" : "disabled"); 168168870Sjhb printf("revision %d\n", pci_read_config(dev, PCI_REVID_SMB, 1)); 16943166Snsouch 170197325Savg if (!sc->poll && intr == PCI_INTR_SMB_SMI) { 171197325Savg device_printf(dev, 172197325Savg "using polling mode when configured interrupt is SMI\n"); 173197325Savg sc->poll = 1; 174197325Savg } 175197325Savg 176197128Savg if (sc->poll) 177197128Savg goto no_intr; 178197128Savg 179197128Savg if (intr != PCI_INTR_SMB_IRQ9 && intr != PCI_INTR_SMB_IRQ_PCI) { 180165951Sjhb device_printf(dev, "Unsupported interrupt mode\n"); 181165951Sjhb error = ENXIO; 182165951Sjhb goto fail; 183165951Sjhb } 18446651Speter 185165951Sjhb /* Force IRQ 9. */ 186165951Sjhb rid = 0; 187197128Savg if (sc->cfg_irq9) 188197128Savg bus_set_resource(dev, SYS_RES_IRQ, rid, 9, 1); 189197128Savg 190165951Sjhb sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 191165951Sjhb RF_SHAREABLE | RF_ACTIVE); 192165951Sjhb if (sc->irq_res == NULL) { 193165951Sjhb device_printf(dev, "Could not allocate irq\n"); 194165951Sjhb error = ENXIO; 195165951Sjhb goto fail; 196165951Sjhb } 19743166Snsouch 198179622Sjhb error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 199179622Sjhb NULL, intsmb_rawintr, sc, &sc->irq_hand); 200165951Sjhb if (error) { 201165951Sjhb device_printf(dev, "Failed to map intr\n"); 202165951Sjhb goto fail; 203165951Sjhb } 204162289Sjhb 205197128Savgno_intr: 206165951Sjhb sc->isbusy = 0; 207165951Sjhb sc->smbus = device_add_child(dev, "smbus", -1); 208165951Sjhb if (sc->smbus == NULL) { 209165951Sjhb error = ENXIO; 210165951Sjhb goto fail; 211165951Sjhb } 212165951Sjhb error = device_probe_and_attach(sc->smbus); 213165951Sjhb if (error) 214165951Sjhb goto fail; 215162289Sjhb 216165951Sjhb#ifdef ENABLE_ALART 217165951Sjhb /* Enable Arart */ 218165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN); 219165951Sjhb#endif 220165951Sjhb return (0); 22143166Snsouch 222165951Sjhbfail: 223165951Sjhb intsmb_detach(dev); 224165951Sjhb return (error); 225165951Sjhb} 226165951Sjhb 227162289Sjhbstatic int 228165951Sjhbintsmb_detach(device_t dev) 22943166Snsouch{ 230162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 231165951Sjhb int error; 232162289Sjhb 233165951Sjhb error = bus_generic_detach(dev); 234165951Sjhb if (error) 235165951Sjhb return (error); 236162289Sjhb 237165951Sjhb if (sc->smbus) 238165951Sjhb device_delete_child(dev, sc->smbus); 239165951Sjhb if (sc->irq_hand) 240165951Sjhb bus_teardown_intr(dev, sc->irq_res, sc->irq_hand); 241165951Sjhb if (sc->irq_res) 242165951Sjhb bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 243165951Sjhb if (sc->io_res) 244165951Sjhb bus_release_resource(dev, SYS_RES_IOPORT, PCI_BASE_ADDR_SMB, 245165951Sjhb sc->io_res); 246165951Sjhb mtx_destroy(&sc->lock); 247165951Sjhb return (0); 24843166Snsouch} 249165951Sjhb 250165951Sjhbstatic void 251165951Sjhbintsmb_rawintr(void *arg) 25243166Snsouch{ 253165951Sjhb struct intsmb_softc *sc = arg; 254162289Sjhb 255165951Sjhb INTSMB_LOCK(sc); 256165951Sjhb intsmb_intr(sc); 257165951Sjhb intsmb_slvintr(sc); 258165951Sjhb INTSMB_UNLOCK(sc); 25943166Snsouch} 26043166Snsouch 261162289Sjhbstatic int 262162234Sjhbintsmb_callback(device_t dev, int index, void *data) 26343166Snsouch{ 26443166Snsouch int error = 0; 265162289Sjhb 26643166Snsouch switch (index) { 26743166Snsouch case SMB_REQUEST_BUS: 26843166Snsouch break; 26943166Snsouch case SMB_RELEASE_BUS: 27043166Snsouch break; 27143166Snsouch default: 27243166Snsouch error = EINVAL; 27343166Snsouch } 274162289Sjhb 27543166Snsouch return (error); 27643166Snsouch} 277162289Sjhb 278162289Sjhb/* Counterpart of smbtx_smb_free(). */ 279162289Sjhbstatic int 280165951Sjhbintsmb_free(struct intsmb_softc *sc) 281162289Sjhb{ 282162289Sjhb 283165951Sjhb INTSMB_LOCK_ASSERT(sc); 284165951Sjhb if ((bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & PIIX4_SMBHSTSTAT_BUSY) || 28543166Snsouch#ifdef ENABLE_ALART 286165951Sjhb (bus_read_1(sc->io_res, PIIX4_SMBSLVSTS) & PIIX4_SMBSLVSTS_BUSY) || 28743166Snsouch#endif 288162289Sjhb sc->isbusy) 289165951Sjhb return (SMB_EBUSY); 290165951Sjhb 291162289Sjhb sc->isbusy = 1; 292162289Sjhb /* Disable Interrupt in slave part. */ 29343166Snsouch#ifndef ENABLE_ALART 294165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 0); 29543166Snsouch#endif 296162289Sjhb /* Reset INTR Flag to prepare INTR. */ 297165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTSTS, 298165951Sjhb PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR | 299165951Sjhb PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL); 300162289Sjhb return (0); 30143166Snsouch} 30243166Snsouch 30343166Snsouchstatic int 304165951Sjhbintsmb_intr(struct intsmb_softc *sc) 30543166Snsouch{ 306165951Sjhb int status, tmp; 307162289Sjhb 308165951Sjhb status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 309162289Sjhb if (status & PIIX4_SMBHSTSTAT_BUSY) 310162289Sjhb return (1); 311162289Sjhb 312162289Sjhb if (status & (PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR | 313162289Sjhb PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL)) { 314162289Sjhb 315165951Sjhb tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 316165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, 317162289Sjhb tmp & ~PIIX4_SMBHSTCNT_INTREN); 318162289Sjhb if (sc->isbusy) { 319162289Sjhb sc->isbusy = 0; 320162289Sjhb wakeup(sc); 32149064Snsouch } 322162289Sjhb return (0); 32343166Snsouch } 324162289Sjhb return (1); /* Not Completed */ 32543166Snsouch} 326162289Sjhb 32743166Snsouchstatic int 328165951Sjhbintsmb_slvintr(struct intsmb_softc *sc) 32943166Snsouch{ 330165951Sjhb int status; 331162289Sjhb 332165951Sjhb status = bus_read_1(sc->io_res, PIIX4_SMBSLVSTS); 333162289Sjhb if (status & PIIX4_SMBSLVSTS_BUSY) 334165951Sjhb return (1); 335165951Sjhb if (status & PIIX4_SMBSLVSTS_ALART) 336165951Sjhb intsmb_alrintr(sc); 337165951Sjhb else if (status & ~(PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 338162289Sjhb | PIIX4_SMBSLVSTS_SDW1)) { 33943166Snsouch } 340162289Sjhb 341162289Sjhb /* Reset Status Register */ 342165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVSTS, 343162289Sjhb PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 | 344162289Sjhb PIIX4_SMBSLVSTS_SDW1 | PIIX4_SMBSLVSTS_SLV); 345165951Sjhb return (0); 34643166Snsouch} 34743166Snsouch 348162289Sjhbstatic void 349165951Sjhbintsmb_alrintr(struct intsmb_softc *sc) 35043166Snsouch{ 35143166Snsouch int slvcnt; 35243288Sdillon#ifdef ENABLE_ALART 35343288Sdillon int error; 354165951Sjhb uint8_t addr; 35543288Sdillon#endif 35643288Sdillon 357162289Sjhb /* Stop generating INTR from ALART. */ 358165951Sjhb slvcnt = bus_read_1(sc->io_res, PIIX4_SMBSLVCNT); 35943166Snsouch#ifdef ENABLE_ALART 360165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 361162289Sjhb slvcnt & ~PIIX4_SMBSLVCNT_ALTEN); 36243166Snsouch#endif 36343166Snsouch DELAY(5); 364162289Sjhb 365162289Sjhb /* Ask bus who asserted it and then ask it what's the matter. */ 36643166Snsouch#ifdef ENABLE_ALART 367165951Sjhb error = intsmb_free(sc); 368165951Sjhb if (error) 369165951Sjhb return; 370162289Sjhb 371165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, SMBALTRESP | LSB); 372165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 1); 373165951Sjhb error = intsmb_stop_poll(sc); 374165951Sjhb if (error) 375165951Sjhb device_printf(sc->dev, "ALART: ERROR\n"); 376165951Sjhb else { 377165951Sjhb addr = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 378165951Sjhb device_printf(sc->dev, "ALART_RESPONSE: 0x%x\n", addr); 379165951Sjhb } 38043166Snsouch 381162289Sjhb /* Re-enable INTR from ALART. */ 382165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 383162289Sjhb slvcnt | PIIX4_SMBSLVCNT_ALTEN); 38443166Snsouch DELAY(5); 38543166Snsouch#endif 386162289Sjhb} 38743166Snsouch 38843166Snsouchstatic void 389165951Sjhbintsmb_start(struct intsmb_softc *sc, unsigned char cmd, int nointr) 39043166Snsouch{ 39143166Snsouch unsigned char tmp; 392162289Sjhb 393165951Sjhb INTSMB_LOCK_ASSERT(sc); 394165951Sjhb tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 395162289Sjhb tmp &= 0xe0; 39643166Snsouch tmp |= cmd; 397162289Sjhb tmp |= PIIX4_SMBHSTCNT_START; 398162289Sjhb 399162289Sjhb /* While not in autoconfiguration enable interrupts. */ 400197128Savg if (!sc->poll && !cold && !nointr) 401162289Sjhb tmp |= PIIX4_SMBHSTCNT_INTREN; 402165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp); 40343166Snsouch} 40443166Snsouch 405165951Sjhbstatic int 406189882Savgintsmb_error(device_t dev, int status) 407165951Sjhb{ 408165951Sjhb int error = 0; 409165951Sjhb 410165951Sjhb if (status & PIIX4_SMBHSTSTAT_ERR) 411165951Sjhb error |= SMB_EBUSERR; 412165951Sjhb if (status & PIIX4_SMBHSTSTAT_BUSC) 413165951Sjhb error |= SMB_ECOLLI; 414165951Sjhb if (status & PIIX4_SMBHSTSTAT_FAIL) 415165951Sjhb error |= SMB_ENOACK; 416189882Savg 417189882Savg if (error != 0 && bootverbose) 418189882Savg device_printf(dev, "error = %d, status = %#x\n", error, status); 419189882Savg 420165951Sjhb return (error); 421165951Sjhb} 422165951Sjhb 423162289Sjhb/* 424162289Sjhb * Polling Code. 425162289Sjhb * 426162289Sjhb * Polling is not encouraged because it requires waiting for the 427162289Sjhb * device if it is busy. 428162289Sjhb * (29063505.pdf from Intel) But during boot, interrupt cannot be used, so use 429162289Sjhb * polling code then. 43043166Snsouch */ 431162289Sjhbstatic int 432165951Sjhbintsmb_stop_poll(struct intsmb_softc *sc) 433162289Sjhb{ 434165951Sjhb int error, i, status, tmp; 43543166Snsouch 436165951Sjhb INTSMB_LOCK_ASSERT(sc); 437165951Sjhb 438165951Sjhb /* First, wait for busy to be set. */ 439162289Sjhb for (i = 0; i < 0x7fff; i++) 440165951Sjhb if (bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & 441162289Sjhb PIIX4_SMBHSTSTAT_BUSY) 442162289Sjhb break; 443162289Sjhb 444165951Sjhb /* Wait for busy to clear. */ 445162289Sjhb for (i = 0; i < 0x7fff; i++) { 446165951Sjhb status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 447162289Sjhb if (!(status & PIIX4_SMBHSTSTAT_BUSY)) { 448162289Sjhb sc->isbusy = 0; 449189882Savg error = intsmb_error(sc->dev, status); 450162289Sjhb return (error); 45143166Snsouch } 45243166Snsouch } 453162289Sjhb 454165951Sjhb /* Timed out waiting for busy to clear. */ 455162289Sjhb sc->isbusy = 0; 456165951Sjhb tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 457165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp & ~PIIX4_SMBHSTCNT_INTREN); 458165951Sjhb return (SMB_ETIMEOUT); 45943166Snsouch} 460162289Sjhb 46143166Snsouch/* 462162289Sjhb * Wait for completion and return result. 46343166Snsouch */ 464162289Sjhbstatic int 465165951Sjhbintsmb_stop(struct intsmb_softc *sc) 466162289Sjhb{ 467165951Sjhb int error, status; 468162289Sjhb 469165951Sjhb INTSMB_LOCK_ASSERT(sc); 470165951Sjhb 471197128Savg if (sc->poll || cold) 472162289Sjhb /* So that it can use device during device probe on SMBus. */ 473165951Sjhb return (intsmb_stop_poll(sc)); 474162289Sjhb 475172667Sjhb error = msleep(sc, &sc->lock, PWAIT | PCATCH, "SMBWAI", hz / 8); 476165951Sjhb if (error == 0) { 477165951Sjhb status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 478162289Sjhb if (!(status & PIIX4_SMBHSTSTAT_BUSY)) { 479189882Savg error = intsmb_error(sc->dev, status); 480162289Sjhb if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR)) 481165951Sjhb device_printf(sc->dev, "unknown cause why?\n"); 48243166Snsouch#ifdef ENABLE_ALART 483165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 484162289Sjhb PIIX4_SMBSLVCNT_ALTEN); 48543166Snsouch#endif 486162289Sjhb return (error); 48743166Snsouch } 48843166Snsouch } 489162289Sjhb 490162289Sjhb /* Timeout Procedure. */ 491162289Sjhb sc->isbusy = 0; 492162289Sjhb 493162289Sjhb /* Re-enable supressed interrupt from slave part. */ 494165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN); 495165951Sjhb if (error == EWOULDBLOCK) 496165951Sjhb return (SMB_ETIMEOUT); 497165951Sjhb else 498165951Sjhb return (SMB_EABORT); 49943166Snsouch} 50043166Snsouch 50143166Snsouchstatic int 50243166Snsouchintsmb_quick(device_t dev, u_char slave, int how) 50343166Snsouch{ 504162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 505165951Sjhb int error; 506162289Sjhb u_char data; 507162289Sjhb 508162289Sjhb data = slave; 509162289Sjhb 510162289Sjhb /* Quick command is part of Address, I think. */ 511162289Sjhb switch(how) { 512162289Sjhb case SMB_QWRITE: 513162289Sjhb data &= ~LSB; 51443166Snsouch break; 515162289Sjhb case SMB_QREAD: 516162289Sjhb data |= LSB; 517162289Sjhb break; 518162289Sjhb default: 519165951Sjhb return (EINVAL); 520162289Sjhb } 521165951Sjhb 522165951Sjhb INTSMB_LOCK(sc); 523165951Sjhb error = intsmb_free(sc); 524165951Sjhb if (error) { 525165951Sjhb INTSMB_UNLOCK(sc); 526165951Sjhb return (error); 527162289Sjhb } 528165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, data); 529165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_QUICK, 0); 530165951Sjhb error = intsmb_stop(sc); 531165951Sjhb INTSMB_UNLOCK(sc); 532162289Sjhb return (error); 53343166Snsouch} 53443166Snsouch 53543166Snsouchstatic int 53643166Snsouchintsmb_sendb(device_t dev, u_char slave, char byte) 53743166Snsouch{ 538162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 539162289Sjhb int error; 540162289Sjhb 541165951Sjhb INTSMB_LOCK(sc); 542165951Sjhb error = intsmb_free(sc); 543165951Sjhb if (error) { 544165951Sjhb INTSMB_UNLOCK(sc); 545165951Sjhb return (error); 546162289Sjhb } 547165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 548165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, byte); 549165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0); 550165951Sjhb error = intsmb_stop(sc); 551165951Sjhb INTSMB_UNLOCK(sc); 552162289Sjhb return (error); 55343166Snsouch} 554162289Sjhb 55543166Snsouchstatic int 55643166Snsouchintsmb_recvb(device_t dev, u_char slave, char *byte) 55743166Snsouch{ 558162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 559162289Sjhb int error; 560162289Sjhb 561165951Sjhb INTSMB_LOCK(sc); 562165951Sjhb error = intsmb_free(sc); 563165951Sjhb if (error) { 564165951Sjhb INTSMB_UNLOCK(sc); 565165951Sjhb return (error); 566165951Sjhb } 567165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 568165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0); 569165951Sjhb error = intsmb_stop(sc); 570165951Sjhb if (error == 0) { 57143166Snsouch#ifdef RECV_IS_IN_CMD 572165951Sjhb /* 573165951Sjhb * Linux SMBus stuff also troubles 574165951Sjhb * Because Intel's datasheet does not make clear. 575165951Sjhb */ 576165951Sjhb *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTCMD); 57743166Snsouch#else 578165951Sjhb *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 57943166Snsouch#endif 580162289Sjhb } 581165951Sjhb INTSMB_UNLOCK(sc); 582162289Sjhb return (error); 58343166Snsouch} 584162289Sjhb 58543166Snsouchstatic int 58643166Snsouchintsmb_writeb(device_t dev, u_char slave, char cmd, char byte) 58743166Snsouch{ 588162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 589162289Sjhb int error; 590162289Sjhb 591165951Sjhb INTSMB_LOCK(sc); 592165951Sjhb error = intsmb_free(sc); 593165951Sjhb if (error) { 594165951Sjhb INTSMB_UNLOCK(sc); 595165951Sjhb return (error); 596162289Sjhb } 597165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 598165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 599165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, byte); 600165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0); 601165951Sjhb error = intsmb_stop(sc); 602165951Sjhb INTSMB_UNLOCK(sc); 603162289Sjhb return (error); 60443166Snsouch} 605162289Sjhb 60643166Snsouchstatic int 60743166Snsouchintsmb_writew(device_t dev, u_char slave, char cmd, short word) 60843166Snsouch{ 609162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 610162289Sjhb int error; 611162289Sjhb 612165951Sjhb INTSMB_LOCK(sc); 613165951Sjhb error = intsmb_free(sc); 614165951Sjhb if (error) { 615165951Sjhb INTSMB_UNLOCK(sc); 616165951Sjhb return (error); 617162289Sjhb } 618165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 619165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 620165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, word & 0xff); 621165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (word >> 8) & 0xff); 622165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 623165951Sjhb error = intsmb_stop(sc); 624165951Sjhb INTSMB_UNLOCK(sc); 625162289Sjhb return (error); 62643166Snsouch} 62743166Snsouch 62843166Snsouchstatic int 62943166Snsouchintsmb_readb(device_t dev, u_char slave, char cmd, char *byte) 63043166Snsouch{ 631162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 632162289Sjhb int error; 633162289Sjhb 634165951Sjhb INTSMB_LOCK(sc); 635165951Sjhb error = intsmb_free(sc); 636165951Sjhb if (error) { 637165951Sjhb INTSMB_UNLOCK(sc); 638165951Sjhb return (error); 639162289Sjhb } 640165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 641165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 642165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0); 643165951Sjhb error = intsmb_stop(sc); 644165951Sjhb if (error == 0) 645165951Sjhb *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 646165951Sjhb INTSMB_UNLOCK(sc); 647162289Sjhb return (error); 64843166Snsouch} 649165951Sjhb 65043166Snsouchstatic int 65143166Snsouchintsmb_readw(device_t dev, u_char slave, char cmd, short *word) 65243166Snsouch{ 653162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 654162289Sjhb int error; 655162289Sjhb 656165951Sjhb INTSMB_LOCK(sc); 657165951Sjhb error = intsmb_free(sc); 658165951Sjhb if (error) { 659165951Sjhb INTSMB_UNLOCK(sc); 660165951Sjhb return (error); 661162289Sjhb } 662165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 663165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 664165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 665165951Sjhb error = intsmb_stop(sc); 666165951Sjhb if (error == 0) { 667165951Sjhb *word = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 668165951Sjhb *word |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8; 669165951Sjhb } 670165951Sjhb INTSMB_UNLOCK(sc); 671162289Sjhb return (error); 67243166Snsouch} 673162289Sjhb 67443166Snsouch/* 67543166Snsouch * Data sheet claims that it implements all function, but also claims 67643166Snsouch * that it implements 7 function and not mention PCALL. So I don't know 67743166Snsouch * whether it will work. 67843166Snsouch */ 67943166Snsouchstatic int 68043166Snsouchintsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata) 68143166Snsouch{ 68243166Snsouch#ifdef PROCCALL_TEST 683162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 684162289Sjhb int error; 685162289Sjhb 686165951Sjhb INTSMB_LOCK(sc); 687165951Sjhb error = intsmb_free(sc); 688165951Sjhb if (error) { 689165951Sjhb INTSMB_UNLOCK(sc); 690165951Sjhb return (error); 691162289Sjhb } 692165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 693165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 694165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, sdata & 0xff); 695165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (sdata & 0xff) >> 8); 696165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 697165951Sjhb error = intsmb_stop(sc); 698165951Sjhb if (error == 0) { 699165951Sjhb *rdata = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 700165951Sjhb *rdata |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8; 701162289Sjhb } 702165951Sjhb INTSMB_UNLOCK(sc); 703162289Sjhb return (error); 70443166Snsouch#else 705165951Sjhb return (SMB_ENOTSUPP); 70643166Snsouch#endif 70743166Snsouch} 708162289Sjhb 70943166Snsouchstatic int 71043166Snsouchintsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) 71143166Snsouch{ 712162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 713162289Sjhb int error, i; 714162289Sjhb 715162289Sjhb if (count > SMBBLOCKTRANS_MAX || count == 0) 716165951Sjhb return (SMB_EINVAL); 717162289Sjhb 718165951Sjhb INTSMB_LOCK(sc); 719165951Sjhb error = intsmb_free(sc); 720165951Sjhb if (error) { 721165951Sjhb INTSMB_UNLOCK(sc); 722165951Sjhb return (error); 723162289Sjhb } 724165951Sjhb 725165951Sjhb /* Reset internal array index. */ 726165951Sjhb bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 727165951Sjhb 728165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 729165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 730165951Sjhb for (i = 0; i < count; i++) 731165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBBLKDAT, buf[i]); 732165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, count); 733165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0); 734165951Sjhb error = intsmb_stop(sc); 735165951Sjhb INTSMB_UNLOCK(sc); 736162289Sjhb return (error); 73743166Snsouch} 73843166Snsouch 73943166Snsouchstatic int 740162234Sjhbintsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf) 74143166Snsouch{ 742162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 743162289Sjhb int error, i; 744162234Sjhb u_char data, nread; 745162289Sjhb 746162289Sjhb if (*count > SMBBLOCKTRANS_MAX || *count == 0) 747165951Sjhb return (SMB_EINVAL); 748162289Sjhb 749165951Sjhb INTSMB_LOCK(sc); 750165951Sjhb error = intsmb_free(sc); 751165951Sjhb if (error) { 752165951Sjhb INTSMB_UNLOCK(sc); 753165951Sjhb return (error); 754165951Sjhb } 755165951Sjhb 756165951Sjhb /* Reset internal array index. */ 757165951Sjhb bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 758165951Sjhb 759165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 760165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 761165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, *count); 762165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0); 763165951Sjhb error = intsmb_stop(sc); 764165951Sjhb if (error == 0) { 765165951Sjhb nread = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 766165951Sjhb if (nread != 0 && nread <= SMBBLOCKTRANS_MAX) { 767165951Sjhb for (i = 0; i < nread; i++) { 768165951Sjhb data = bus_read_1(sc->io_res, PIIX4_SMBBLKDAT); 769165951Sjhb if (i < *count) 770165951Sjhb buf[i] = data; 77143166Snsouch } 772165951Sjhb *count = nread; 773165951Sjhb } else 774165951Sjhb error = EIO; 77543166Snsouch } 776165951Sjhb INTSMB_UNLOCK(sc); 777162289Sjhb return (error); 77843166Snsouch} 77943166Snsouch 780165951Sjhbstatic devclass_t intsmb_devclass; 78143166Snsouch 782165951Sjhbstatic device_method_t intsmb_methods[] = { 783165951Sjhb /* Device interface */ 784165951Sjhb DEVMETHOD(device_probe, intsmb_probe), 785165951Sjhb DEVMETHOD(device_attach, intsmb_attach), 786165951Sjhb DEVMETHOD(device_detach, intsmb_detach), 78746651Speter 788165951Sjhb /* Bus interface */ 789165951Sjhb DEVMETHOD(bus_print_child, bus_generic_print_child), 79043166Snsouch 791165951Sjhb /* SMBus interface */ 792165951Sjhb DEVMETHOD(smbus_callback, intsmb_callback), 793165951Sjhb DEVMETHOD(smbus_quick, intsmb_quick), 794165951Sjhb DEVMETHOD(smbus_sendb, intsmb_sendb), 795165951Sjhb DEVMETHOD(smbus_recvb, intsmb_recvb), 796165951Sjhb DEVMETHOD(smbus_writeb, intsmb_writeb), 797165951Sjhb DEVMETHOD(smbus_writew, intsmb_writew), 798165951Sjhb DEVMETHOD(smbus_readb, intsmb_readb), 799165951Sjhb DEVMETHOD(smbus_readw, intsmb_readw), 800165951Sjhb DEVMETHOD(smbus_pcall, intsmb_pcall), 801165951Sjhb DEVMETHOD(smbus_bwrite, intsmb_bwrite), 802165951Sjhb DEVMETHOD(smbus_bread, intsmb_bread), 803162289Sjhb 804165951Sjhb { 0, 0 } 805165951Sjhb}; 80643166Snsouch 807165951Sjhbstatic driver_t intsmb_driver = { 808165951Sjhb "intsmb", 809165951Sjhb intsmb_methods, 810165951Sjhb sizeof(struct intsmb_softc), 811165951Sjhb}; 812162289Sjhb 813165951SjhbDRIVER_MODULE(intsmb, pci, intsmb_driver, intsmb_devclass, 0, 0); 814162289SjhbDRIVER_MODULE(smbus, intsmb, smbus_driver, smbus_devclass, 0, 0); 815165951SjhbMODULE_DEPEND(intsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 816165951SjhbMODULE_VERSION(intsmb, 1); 817