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$"); 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; 101234677Savg case 0x43721002: 102234677Savg device_set_desc(dev, "ATI IXP400 SMBus Controller"); 103234677Savg break; 104197128Savg case 0x43851002: 105234213Savg /* SB800 and newer can not be configured in a compatible way. */ 106234213Savg if (pci_get_revid(dev) >= 0x40) 107234213Savg return (ENXIO); 108197128Savg device_set_desc(dev, "AMD SB600/700/710/750 SMBus Controller"); 109197128Savg /* XXX Maybe force polling right here? */ 110197128Savg break; 111165951Sjhb default: 112165951Sjhb return (ENXIO); 113165951Sjhb } 11443166Snsouch 115165951Sjhb return (BUS_PROBE_DEFAULT); 116165951Sjhb} 117162289Sjhb 118165951Sjhbstatic int 119165951Sjhbintsmb_attach(device_t dev) 120165951Sjhb{ 121165951Sjhb struct intsmb_softc *sc = device_get_softc(dev); 122165951Sjhb int error, rid, value; 123197128Savg int intr; 124165951Sjhb char *str; 125162289Sjhb 126178972Sjhb sc->dev = dev; 127178972Sjhb 128165951Sjhb mtx_init(&sc->lock, device_get_nameunit(dev), "intsmb", MTX_DEF); 12943166Snsouch 130197128Savg sc->cfg_irq9 = 0; 131197128Savg#ifndef NO_CHANGE_PCICONF 132197128Savg switch (pci_get_devid(dev)) { 133197128Savg case 0x71138086: /* Intel 82371AB */ 134197128Savg case 0x719b8086: /* Intel 82443MX */ 135197128Savg /* Changing configuration is allowed. */ 136197128Savg sc->cfg_irq9 = 1; 137197128Savg break; 138197128Savg } 139197128Savg#endif 140197128Savg 141165951Sjhb rid = PCI_BASE_ADDR_SMB; 142165951Sjhb sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 143165951Sjhb RF_ACTIVE); 144165951Sjhb if (sc->io_res == NULL) { 145165951Sjhb device_printf(dev, "Could not allocate I/O space\n"); 146165951Sjhb error = ENXIO; 147165951Sjhb goto fail; 148165951Sjhb } 14943166Snsouch 150197128Savg if (sc->cfg_irq9) { 151197128Savg pci_write_config(dev, PCIR_INTLINE, 0x9, 1); 152197128Savg pci_write_config(dev, PCI_HST_CFG_SMB, 153197128Savg PCI_INTR_SMB_IRQ9 | PCI_INTR_SMB_ENABLE, 1); 154197128Savg } 155165951Sjhb value = pci_read_config(dev, PCI_HST_CFG_SMB, 1); 156197128Savg sc->poll = (value & PCI_INTR_SMB_ENABLE) == 0; 157197128Savg intr = value & PCI_INTR_SMB_MASK; 158197128Savg switch (intr) { 159165951Sjhb case PCI_INTR_SMB_SMI: 160165951Sjhb str = "SMI"; 161165951Sjhb break; 162165951Sjhb case PCI_INTR_SMB_IRQ9: 163165951Sjhb str = "IRQ 9"; 164165951Sjhb break; 165197128Savg case PCI_INTR_SMB_IRQ_PCI: 166197128Savg str = "PCI IRQ"; 167197128Savg break; 168165951Sjhb default: 169165951Sjhb str = "BOGUS"; 170165951Sjhb } 171197128Savg 172165951Sjhb device_printf(dev, "intr %s %s ", str, 173197128Savg sc->poll == 0 ? "enabled" : "disabled"); 174168870Sjhb printf("revision %d\n", pci_read_config(dev, PCI_REVID_SMB, 1)); 17543166Snsouch 176197325Savg if (!sc->poll && intr == PCI_INTR_SMB_SMI) { 177197325Savg device_printf(dev, 178197325Savg "using polling mode when configured interrupt is SMI\n"); 179197325Savg sc->poll = 1; 180197325Savg } 181197325Savg 182197128Savg if (sc->poll) 183197128Savg goto no_intr; 184197128Savg 185197128Savg if (intr != PCI_INTR_SMB_IRQ9 && intr != PCI_INTR_SMB_IRQ_PCI) { 186165951Sjhb device_printf(dev, "Unsupported interrupt mode\n"); 187165951Sjhb error = ENXIO; 188165951Sjhb goto fail; 189165951Sjhb } 19046651Speter 191165951Sjhb /* Force IRQ 9. */ 192165951Sjhb rid = 0; 193197128Savg if (sc->cfg_irq9) 194197128Savg bus_set_resource(dev, SYS_RES_IRQ, rid, 9, 1); 195197128Savg 196165951Sjhb sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 197165951Sjhb RF_SHAREABLE | RF_ACTIVE); 198165951Sjhb if (sc->irq_res == NULL) { 199165951Sjhb device_printf(dev, "Could not allocate irq\n"); 200165951Sjhb error = ENXIO; 201165951Sjhb goto fail; 202165951Sjhb } 20343166Snsouch 204179622Sjhb error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 205179622Sjhb NULL, intsmb_rawintr, sc, &sc->irq_hand); 206165951Sjhb if (error) { 207165951Sjhb device_printf(dev, "Failed to map intr\n"); 208165951Sjhb goto fail; 209165951Sjhb } 210162289Sjhb 211197128Savgno_intr: 212165951Sjhb sc->isbusy = 0; 213165951Sjhb sc->smbus = device_add_child(dev, "smbus", -1); 214165951Sjhb if (sc->smbus == NULL) { 215165951Sjhb error = ENXIO; 216165951Sjhb goto fail; 217165951Sjhb } 218165951Sjhb error = device_probe_and_attach(sc->smbus); 219165951Sjhb if (error) 220165951Sjhb goto fail; 221162289Sjhb 222165951Sjhb#ifdef ENABLE_ALART 223165951Sjhb /* Enable Arart */ 224165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN); 225165951Sjhb#endif 226165951Sjhb return (0); 22743166Snsouch 228165951Sjhbfail: 229165951Sjhb intsmb_detach(dev); 230165951Sjhb return (error); 231165951Sjhb} 232165951Sjhb 233162289Sjhbstatic int 234165951Sjhbintsmb_detach(device_t dev) 23543166Snsouch{ 236162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 237165951Sjhb int error; 238162289Sjhb 239165951Sjhb error = bus_generic_detach(dev); 240165951Sjhb if (error) 241165951Sjhb return (error); 242162289Sjhb 243165951Sjhb if (sc->smbus) 244165951Sjhb device_delete_child(dev, sc->smbus); 245165951Sjhb if (sc->irq_hand) 246165951Sjhb bus_teardown_intr(dev, sc->irq_res, sc->irq_hand); 247165951Sjhb if (sc->irq_res) 248165951Sjhb bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 249165951Sjhb if (sc->io_res) 250165951Sjhb bus_release_resource(dev, SYS_RES_IOPORT, PCI_BASE_ADDR_SMB, 251165951Sjhb sc->io_res); 252165951Sjhb mtx_destroy(&sc->lock); 253165951Sjhb return (0); 25443166Snsouch} 255165951Sjhb 256165951Sjhbstatic void 257165951Sjhbintsmb_rawintr(void *arg) 25843166Snsouch{ 259165951Sjhb struct intsmb_softc *sc = arg; 260162289Sjhb 261165951Sjhb INTSMB_LOCK(sc); 262165951Sjhb intsmb_intr(sc); 263165951Sjhb intsmb_slvintr(sc); 264165951Sjhb INTSMB_UNLOCK(sc); 26543166Snsouch} 26643166Snsouch 267162289Sjhbstatic int 268162234Sjhbintsmb_callback(device_t dev, int index, void *data) 26943166Snsouch{ 27043166Snsouch int error = 0; 271162289Sjhb 27243166Snsouch switch (index) { 27343166Snsouch case SMB_REQUEST_BUS: 27443166Snsouch break; 27543166Snsouch case SMB_RELEASE_BUS: 27643166Snsouch break; 27743166Snsouch default: 278234215Savg error = SMB_EINVAL; 27943166Snsouch } 280162289Sjhb 28143166Snsouch return (error); 28243166Snsouch} 283162289Sjhb 284162289Sjhb/* Counterpart of smbtx_smb_free(). */ 285162289Sjhbstatic int 286165951Sjhbintsmb_free(struct intsmb_softc *sc) 287162289Sjhb{ 288162289Sjhb 289165951Sjhb INTSMB_LOCK_ASSERT(sc); 290165951Sjhb if ((bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & PIIX4_SMBHSTSTAT_BUSY) || 29143166Snsouch#ifdef ENABLE_ALART 292165951Sjhb (bus_read_1(sc->io_res, PIIX4_SMBSLVSTS) & PIIX4_SMBSLVSTS_BUSY) || 29343166Snsouch#endif 294162289Sjhb sc->isbusy) 295165951Sjhb return (SMB_EBUSY); 296165951Sjhb 297162289Sjhb sc->isbusy = 1; 298162289Sjhb /* Disable Interrupt in slave part. */ 29943166Snsouch#ifndef ENABLE_ALART 300165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 0); 30143166Snsouch#endif 302162289Sjhb /* Reset INTR Flag to prepare INTR. */ 303165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTSTS, 304165951Sjhb PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR | 305165951Sjhb PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL); 306162289Sjhb return (0); 30743166Snsouch} 30843166Snsouch 30943166Snsouchstatic int 310165951Sjhbintsmb_intr(struct intsmb_softc *sc) 31143166Snsouch{ 312165951Sjhb int status, tmp; 313162289Sjhb 314165951Sjhb status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 315162289Sjhb if (status & PIIX4_SMBHSTSTAT_BUSY) 316162289Sjhb return (1); 317162289Sjhb 318162289Sjhb if (status & (PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR | 319162289Sjhb PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL)) { 320162289Sjhb 321165951Sjhb tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 322165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, 323162289Sjhb tmp & ~PIIX4_SMBHSTCNT_INTREN); 324162289Sjhb if (sc->isbusy) { 325162289Sjhb sc->isbusy = 0; 326162289Sjhb wakeup(sc); 32749064Snsouch } 328162289Sjhb return (0); 32943166Snsouch } 330162289Sjhb return (1); /* Not Completed */ 33143166Snsouch} 332162289Sjhb 33343166Snsouchstatic int 334165951Sjhbintsmb_slvintr(struct intsmb_softc *sc) 33543166Snsouch{ 336165951Sjhb int status; 337162289Sjhb 338165951Sjhb status = bus_read_1(sc->io_res, PIIX4_SMBSLVSTS); 339162289Sjhb if (status & PIIX4_SMBSLVSTS_BUSY) 340165951Sjhb return (1); 341165951Sjhb if (status & PIIX4_SMBSLVSTS_ALART) 342165951Sjhb intsmb_alrintr(sc); 343165951Sjhb else if (status & ~(PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 344162289Sjhb | PIIX4_SMBSLVSTS_SDW1)) { 34543166Snsouch } 346162289Sjhb 347162289Sjhb /* Reset Status Register */ 348165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVSTS, 349162289Sjhb PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 | 350162289Sjhb PIIX4_SMBSLVSTS_SDW1 | PIIX4_SMBSLVSTS_SLV); 351165951Sjhb return (0); 35243166Snsouch} 35343166Snsouch 354162289Sjhbstatic void 355165951Sjhbintsmb_alrintr(struct intsmb_softc *sc) 35643166Snsouch{ 35743166Snsouch int slvcnt; 35843288Sdillon#ifdef ENABLE_ALART 35943288Sdillon int error; 360165951Sjhb uint8_t addr; 36143288Sdillon#endif 36243288Sdillon 363162289Sjhb /* Stop generating INTR from ALART. */ 364165951Sjhb slvcnt = bus_read_1(sc->io_res, PIIX4_SMBSLVCNT); 36543166Snsouch#ifdef ENABLE_ALART 366165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 367162289Sjhb slvcnt & ~PIIX4_SMBSLVCNT_ALTEN); 36843166Snsouch#endif 36943166Snsouch DELAY(5); 370162289Sjhb 371162289Sjhb /* Ask bus who asserted it and then ask it what's the matter. */ 37243166Snsouch#ifdef ENABLE_ALART 373165951Sjhb error = intsmb_free(sc); 374165951Sjhb if (error) 375165951Sjhb return; 376162289Sjhb 377165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, SMBALTRESP | LSB); 378165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 1); 379165951Sjhb error = intsmb_stop_poll(sc); 380165951Sjhb if (error) 381165951Sjhb device_printf(sc->dev, "ALART: ERROR\n"); 382165951Sjhb else { 383165951Sjhb addr = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 384165951Sjhb device_printf(sc->dev, "ALART_RESPONSE: 0x%x\n", addr); 385165951Sjhb } 38643166Snsouch 387162289Sjhb /* Re-enable INTR from ALART. */ 388165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 389162289Sjhb slvcnt | PIIX4_SMBSLVCNT_ALTEN); 39043166Snsouch DELAY(5); 39143166Snsouch#endif 392162289Sjhb} 39343166Snsouch 39443166Snsouchstatic void 395165951Sjhbintsmb_start(struct intsmb_softc *sc, unsigned char cmd, int nointr) 39643166Snsouch{ 39743166Snsouch unsigned char tmp; 398162289Sjhb 399165951Sjhb INTSMB_LOCK_ASSERT(sc); 400165951Sjhb tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 401162289Sjhb tmp &= 0xe0; 40243166Snsouch tmp |= cmd; 403162289Sjhb tmp |= PIIX4_SMBHSTCNT_START; 404162289Sjhb 405162289Sjhb /* While not in autoconfiguration enable interrupts. */ 406197128Savg if (!sc->poll && !cold && !nointr) 407162289Sjhb tmp |= PIIX4_SMBHSTCNT_INTREN; 408165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp); 40943166Snsouch} 41043166Snsouch 411165951Sjhbstatic int 412189882Savgintsmb_error(device_t dev, int status) 413165951Sjhb{ 414165951Sjhb int error = 0; 415165951Sjhb 416165951Sjhb if (status & PIIX4_SMBHSTSTAT_ERR) 417165951Sjhb error |= SMB_EBUSERR; 418165951Sjhb if (status & PIIX4_SMBHSTSTAT_BUSC) 419165951Sjhb error |= SMB_ECOLLI; 420165951Sjhb if (status & PIIX4_SMBHSTSTAT_FAIL) 421165951Sjhb error |= SMB_ENOACK; 422189882Savg 423189882Savg if (error != 0 && bootverbose) 424189882Savg device_printf(dev, "error = %d, status = %#x\n", error, status); 425189882Savg 426165951Sjhb return (error); 427165951Sjhb} 428165951Sjhb 429162289Sjhb/* 430162289Sjhb * Polling Code. 431162289Sjhb * 432162289Sjhb * Polling is not encouraged because it requires waiting for the 433162289Sjhb * device if it is busy. 434162289Sjhb * (29063505.pdf from Intel) But during boot, interrupt cannot be used, so use 435162289Sjhb * polling code then. 43643166Snsouch */ 437162289Sjhbstatic int 438165951Sjhbintsmb_stop_poll(struct intsmb_softc *sc) 439162289Sjhb{ 440165951Sjhb int error, i, status, tmp; 44143166Snsouch 442165951Sjhb INTSMB_LOCK_ASSERT(sc); 443165951Sjhb 444165951Sjhb /* First, wait for busy to be set. */ 445162289Sjhb for (i = 0; i < 0x7fff; i++) 446165951Sjhb if (bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & 447162289Sjhb PIIX4_SMBHSTSTAT_BUSY) 448162289Sjhb break; 449162289Sjhb 450165951Sjhb /* Wait for busy to clear. */ 451162289Sjhb for (i = 0; i < 0x7fff; i++) { 452165951Sjhb status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 453162289Sjhb if (!(status & PIIX4_SMBHSTSTAT_BUSY)) { 454162289Sjhb sc->isbusy = 0; 455189882Savg error = intsmb_error(sc->dev, status); 456162289Sjhb return (error); 45743166Snsouch } 45843166Snsouch } 459162289Sjhb 460165951Sjhb /* Timed out waiting for busy to clear. */ 461162289Sjhb sc->isbusy = 0; 462165951Sjhb tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 463165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp & ~PIIX4_SMBHSTCNT_INTREN); 464165951Sjhb return (SMB_ETIMEOUT); 46543166Snsouch} 466162289Sjhb 46743166Snsouch/* 468162289Sjhb * Wait for completion and return result. 46943166Snsouch */ 470162289Sjhbstatic int 471165951Sjhbintsmb_stop(struct intsmb_softc *sc) 472162289Sjhb{ 473165951Sjhb int error, status; 474162289Sjhb 475165951Sjhb INTSMB_LOCK_ASSERT(sc); 476165951Sjhb 477197128Savg if (sc->poll || cold) 478162289Sjhb /* So that it can use device during device probe on SMBus. */ 479165951Sjhb return (intsmb_stop_poll(sc)); 480162289Sjhb 481172667Sjhb error = msleep(sc, &sc->lock, PWAIT | PCATCH, "SMBWAI", hz / 8); 482165951Sjhb if (error == 0) { 483165951Sjhb status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 484162289Sjhb if (!(status & PIIX4_SMBHSTSTAT_BUSY)) { 485189882Savg error = intsmb_error(sc->dev, status); 486162289Sjhb if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR)) 487165951Sjhb device_printf(sc->dev, "unknown cause why?\n"); 48843166Snsouch#ifdef ENABLE_ALART 489165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 490162289Sjhb PIIX4_SMBSLVCNT_ALTEN); 49143166Snsouch#endif 492162289Sjhb return (error); 49343166Snsouch } 49443166Snsouch } 495162289Sjhb 496162289Sjhb /* Timeout Procedure. */ 497162289Sjhb sc->isbusy = 0; 498162289Sjhb 499162289Sjhb /* Re-enable supressed interrupt from slave part. */ 500165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN); 501165951Sjhb if (error == EWOULDBLOCK) 502165951Sjhb return (SMB_ETIMEOUT); 503165951Sjhb else 504165951Sjhb return (SMB_EABORT); 50543166Snsouch} 50643166Snsouch 50743166Snsouchstatic int 50843166Snsouchintsmb_quick(device_t dev, u_char slave, int how) 50943166Snsouch{ 510162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 511165951Sjhb int error; 512162289Sjhb u_char data; 513162289Sjhb 514162289Sjhb data = slave; 515162289Sjhb 516162289Sjhb /* Quick command is part of Address, I think. */ 517162289Sjhb switch(how) { 518162289Sjhb case SMB_QWRITE: 519162289Sjhb data &= ~LSB; 52043166Snsouch break; 521162289Sjhb case SMB_QREAD: 522162289Sjhb data |= LSB; 523162289Sjhb break; 524162289Sjhb default: 525234215Savg return (SMB_EINVAL); 526162289Sjhb } 527165951Sjhb 528165951Sjhb INTSMB_LOCK(sc); 529165951Sjhb error = intsmb_free(sc); 530165951Sjhb if (error) { 531165951Sjhb INTSMB_UNLOCK(sc); 532165951Sjhb return (error); 533162289Sjhb } 534165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, data); 535165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_QUICK, 0); 536165951Sjhb error = intsmb_stop(sc); 537165951Sjhb INTSMB_UNLOCK(sc); 538162289Sjhb return (error); 53943166Snsouch} 54043166Snsouch 54143166Snsouchstatic int 54243166Snsouchintsmb_sendb(device_t dev, u_char slave, char byte) 54343166Snsouch{ 544162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 545162289Sjhb int error; 546162289Sjhb 547165951Sjhb INTSMB_LOCK(sc); 548165951Sjhb error = intsmb_free(sc); 549165951Sjhb if (error) { 550165951Sjhb INTSMB_UNLOCK(sc); 551165951Sjhb return (error); 552162289Sjhb } 553165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 554165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, byte); 555165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0); 556165951Sjhb error = intsmb_stop(sc); 557165951Sjhb INTSMB_UNLOCK(sc); 558162289Sjhb return (error); 55943166Snsouch} 560162289Sjhb 56143166Snsouchstatic int 56243166Snsouchintsmb_recvb(device_t dev, u_char slave, char *byte) 56343166Snsouch{ 564162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 565162289Sjhb int error; 566162289Sjhb 567165951Sjhb INTSMB_LOCK(sc); 568165951Sjhb error = intsmb_free(sc); 569165951Sjhb if (error) { 570165951Sjhb INTSMB_UNLOCK(sc); 571165951Sjhb return (error); 572165951Sjhb } 573165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 574165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0); 575165951Sjhb error = intsmb_stop(sc); 576165951Sjhb if (error == 0) { 57743166Snsouch#ifdef RECV_IS_IN_CMD 578165951Sjhb /* 579165951Sjhb * Linux SMBus stuff also troubles 580165951Sjhb * Because Intel's datasheet does not make clear. 581165951Sjhb */ 582165951Sjhb *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTCMD); 58343166Snsouch#else 584165951Sjhb *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 58543166Snsouch#endif 586162289Sjhb } 587165951Sjhb INTSMB_UNLOCK(sc); 588162289Sjhb return (error); 58943166Snsouch} 590162289Sjhb 59143166Snsouchstatic int 59243166Snsouchintsmb_writeb(device_t dev, u_char slave, char cmd, char byte) 59343166Snsouch{ 594162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 595162289Sjhb int error; 596162289Sjhb 597165951Sjhb INTSMB_LOCK(sc); 598165951Sjhb error = intsmb_free(sc); 599165951Sjhb if (error) { 600165951Sjhb INTSMB_UNLOCK(sc); 601165951Sjhb return (error); 602162289Sjhb } 603165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 604165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 605165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, byte); 606165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0); 607165951Sjhb error = intsmb_stop(sc); 608165951Sjhb INTSMB_UNLOCK(sc); 609162289Sjhb return (error); 61043166Snsouch} 611162289Sjhb 61243166Snsouchstatic int 61343166Snsouchintsmb_writew(device_t dev, u_char slave, char cmd, short word) 61443166Snsouch{ 615162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 616162289Sjhb int error; 617162289Sjhb 618165951Sjhb INTSMB_LOCK(sc); 619165951Sjhb error = intsmb_free(sc); 620165951Sjhb if (error) { 621165951Sjhb INTSMB_UNLOCK(sc); 622165951Sjhb return (error); 623162289Sjhb } 624165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 625165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 626165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, word & 0xff); 627165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (word >> 8) & 0xff); 628165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 629165951Sjhb error = intsmb_stop(sc); 630165951Sjhb INTSMB_UNLOCK(sc); 631162289Sjhb return (error); 63243166Snsouch} 63343166Snsouch 63443166Snsouchstatic int 63543166Snsouchintsmb_readb(device_t dev, u_char slave, char cmd, char *byte) 63643166Snsouch{ 637162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 638162289Sjhb int error; 639162289Sjhb 640165951Sjhb INTSMB_LOCK(sc); 641165951Sjhb error = intsmb_free(sc); 642165951Sjhb if (error) { 643165951Sjhb INTSMB_UNLOCK(sc); 644165951Sjhb return (error); 645162289Sjhb } 646165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 647165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 648165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0); 649165951Sjhb error = intsmb_stop(sc); 650165951Sjhb if (error == 0) 651165951Sjhb *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 652165951Sjhb INTSMB_UNLOCK(sc); 653162289Sjhb return (error); 65443166Snsouch} 655165951Sjhb 65643166Snsouchstatic int 65743166Snsouchintsmb_readw(device_t dev, u_char slave, char cmd, short *word) 65843166Snsouch{ 659162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 660162289Sjhb int error; 661162289Sjhb 662165951Sjhb INTSMB_LOCK(sc); 663165951Sjhb error = intsmb_free(sc); 664165951Sjhb if (error) { 665165951Sjhb INTSMB_UNLOCK(sc); 666165951Sjhb return (error); 667162289Sjhb } 668165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 669165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 670165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 671165951Sjhb error = intsmb_stop(sc); 672165951Sjhb if (error == 0) { 673165951Sjhb *word = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 674165951Sjhb *word |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8; 675165951Sjhb } 676165951Sjhb INTSMB_UNLOCK(sc); 677162289Sjhb return (error); 67843166Snsouch} 679162289Sjhb 68043166Snsouch/* 68143166Snsouch * Data sheet claims that it implements all function, but also claims 68243166Snsouch * that it implements 7 function and not mention PCALL. So I don't know 68343166Snsouch * whether it will work. 68443166Snsouch */ 68543166Snsouchstatic int 68643166Snsouchintsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata) 68743166Snsouch{ 68843166Snsouch#ifdef PROCCALL_TEST 689162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 690162289Sjhb int error; 691162289Sjhb 692165951Sjhb INTSMB_LOCK(sc); 693165951Sjhb error = intsmb_free(sc); 694165951Sjhb if (error) { 695165951Sjhb INTSMB_UNLOCK(sc); 696165951Sjhb return (error); 697162289Sjhb } 698165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 699165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 700165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, sdata & 0xff); 701165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (sdata & 0xff) >> 8); 702165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 703165951Sjhb error = intsmb_stop(sc); 704165951Sjhb if (error == 0) { 705165951Sjhb *rdata = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 706165951Sjhb *rdata |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8; 707162289Sjhb } 708165951Sjhb INTSMB_UNLOCK(sc); 709162289Sjhb return (error); 71043166Snsouch#else 711165951Sjhb return (SMB_ENOTSUPP); 71243166Snsouch#endif 71343166Snsouch} 714162289Sjhb 71543166Snsouchstatic int 71643166Snsouchintsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) 71743166Snsouch{ 718162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 719162289Sjhb int error, i; 720162289Sjhb 721162289Sjhb if (count > SMBBLOCKTRANS_MAX || count == 0) 722165951Sjhb return (SMB_EINVAL); 723162289Sjhb 724165951Sjhb INTSMB_LOCK(sc); 725165951Sjhb error = intsmb_free(sc); 726165951Sjhb if (error) { 727165951Sjhb INTSMB_UNLOCK(sc); 728165951Sjhb return (error); 729162289Sjhb } 730165951Sjhb 731165951Sjhb /* Reset internal array index. */ 732165951Sjhb bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 733165951Sjhb 734165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 735165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 736165951Sjhb for (i = 0; i < count; i++) 737165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBBLKDAT, buf[i]); 738165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, count); 739165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0); 740165951Sjhb error = intsmb_stop(sc); 741165951Sjhb INTSMB_UNLOCK(sc); 742162289Sjhb return (error); 74343166Snsouch} 74443166Snsouch 74543166Snsouchstatic int 746162234Sjhbintsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf) 74743166Snsouch{ 748162289Sjhb struct intsmb_softc *sc = device_get_softc(dev); 749162289Sjhb int error, i; 750162234Sjhb u_char data, nread; 751162289Sjhb 752162289Sjhb if (*count > SMBBLOCKTRANS_MAX || *count == 0) 753165951Sjhb return (SMB_EINVAL); 754162289Sjhb 755165951Sjhb INTSMB_LOCK(sc); 756165951Sjhb error = intsmb_free(sc); 757165951Sjhb if (error) { 758165951Sjhb INTSMB_UNLOCK(sc); 759165951Sjhb return (error); 760165951Sjhb } 761165951Sjhb 762165951Sjhb /* Reset internal array index. */ 763165951Sjhb bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 764165951Sjhb 765165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 766165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 767165951Sjhb bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, *count); 768165951Sjhb intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0); 769165951Sjhb error = intsmb_stop(sc); 770165951Sjhb if (error == 0) { 771165951Sjhb nread = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 772165951Sjhb if (nread != 0 && nread <= SMBBLOCKTRANS_MAX) { 773165951Sjhb for (i = 0; i < nread; i++) { 774165951Sjhb data = bus_read_1(sc->io_res, PIIX4_SMBBLKDAT); 775165951Sjhb if (i < *count) 776165951Sjhb buf[i] = data; 77743166Snsouch } 778165951Sjhb *count = nread; 779165951Sjhb } else 780234215Savg error = SMB_EBUSERR; 78143166Snsouch } 782165951Sjhb INTSMB_UNLOCK(sc); 783162289Sjhb return (error); 78443166Snsouch} 78543166Snsouch 786165951Sjhbstatic devclass_t intsmb_devclass; 78743166Snsouch 788165951Sjhbstatic device_method_t intsmb_methods[] = { 789165951Sjhb /* Device interface */ 790165951Sjhb DEVMETHOD(device_probe, intsmb_probe), 791165951Sjhb DEVMETHOD(device_attach, intsmb_attach), 792165951Sjhb DEVMETHOD(device_detach, intsmb_detach), 79346651Speter 794165951Sjhb /* SMBus interface */ 795165951Sjhb DEVMETHOD(smbus_callback, intsmb_callback), 796165951Sjhb DEVMETHOD(smbus_quick, intsmb_quick), 797165951Sjhb DEVMETHOD(smbus_sendb, intsmb_sendb), 798165951Sjhb DEVMETHOD(smbus_recvb, intsmb_recvb), 799165951Sjhb DEVMETHOD(smbus_writeb, intsmb_writeb), 800165951Sjhb DEVMETHOD(smbus_writew, intsmb_writew), 801165951Sjhb DEVMETHOD(smbus_readb, intsmb_readb), 802165951Sjhb DEVMETHOD(smbus_readw, intsmb_readw), 803165951Sjhb DEVMETHOD(smbus_pcall, intsmb_pcall), 804165951Sjhb DEVMETHOD(smbus_bwrite, intsmb_bwrite), 805165951Sjhb DEVMETHOD(smbus_bread, intsmb_bread), 806162289Sjhb 807229093Shselasky DEVMETHOD_END 808165951Sjhb}; 80943166Snsouch 810165951Sjhbstatic driver_t intsmb_driver = { 811165951Sjhb "intsmb", 812165951Sjhb intsmb_methods, 813165951Sjhb sizeof(struct intsmb_softc), 814165951Sjhb}; 815162289Sjhb 816165951SjhbDRIVER_MODULE(intsmb, pci, intsmb_driver, intsmb_devclass, 0, 0); 817162289SjhbDRIVER_MODULE(smbus, intsmb, smbus_driver, smbus_devclass, 0, 0); 818165951SjhbMODULE_DEPEND(intsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 819165951SjhbMODULE_VERSION(intsmb, 1); 820