intpm.c revision 306140
1139735Simp/*- 2129198Scognet * Copyright (c) 1998, 1999 Takanori Watanabe 3129198Scognet * All rights reserved. 4129198Scognet * 5129198Scognet * Redistribution and use in source and binary forms, with or without 6129198Scognet * modification, are permitted provided that the following conditions 7129198Scognet * are met: 8129198Scognet * 1. Redistributions of source code must retain the above copyright 9129198Scognet * notice, this list of conditions and the following disclaimer. 10129198Scognet * 2. Redistributions in binary form must reproduce the above copyright 11129198Scognet * notice, this list of conditions and the following disclaimer in the 12129198Scognet * documentation and/or other materials provided with the distribution. 13129198Scognet * 14129198Scognet * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15129198Scognet * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16129198Scognet * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17129198Scognet * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18129198Scognet * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19129198Scognet * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20129198Scognet * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21129198Scognet * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22129198Scognet * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23129198Scognet * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24129198Scognet * SUCH DAMAGE. 25129198Scognet */ 26129198Scognet 27129198Scognet#include <sys/cdefs.h> 28129198Scognet__FBSDID("$FreeBSD: stable/11/sys/dev/intpm/intpm.c 306140 2016-09-21 19:51:59Z avg $"); 29129198Scognet 30129198Scognet#include <sys/param.h> 31129198Scognet#include <sys/systm.h> 32129198Scognet#include <sys/bus.h> 33129198Scognet#include <sys/kernel.h> 34129198Scognet#include <sys/lock.h> 35129198Scognet#include <sys/module.h> 36129198Scognet#include <sys/mutex.h> 37129198Scognet#include <sys/rman.h> 38129198Scognet#include <machine/bus.h> 39129198Scognet#include <dev/smbus/smbconf.h> 40129198Scognet 41129198Scognet#include "smbus_if.h" 42129198Scognet 43129198Scognet#include <dev/pci/pcireg.h> 44129198Scognet#include <dev/pci/pcivar.h> 45129198Scognet#include <dev/intpm/intpmreg.h> 46129198Scognet 47129198Scognet#include "opt_intpm.h" 48129198Scognet 49140310Scognetstruct intsmb_softc { 50146597Scognet device_t dev; 51129198Scognet struct resource *io_res; 52129198Scognet struct resource *irq_res; 53129198Scognet void *irq_hand; 54129198Scognet device_t smbus; 55129198Scognet int io_rid; 56129198Scognet int isbusy; 57129198Scognet int cfg_irq9; 58129198Scognet int sb8xx; 59129198Scognet int poll; 60129198Scognet struct mtx lock; 61129198Scognet}; 62129198Scognet 63129198Scognet#define INTSMB_LOCK(sc) mtx_lock(&(sc)->lock) 64129198Scognet#define INTSMB_UNLOCK(sc) mtx_unlock(&(sc)->lock) 65129198Scognet#define INTSMB_LOCK_ASSERT(sc) mtx_assert(&(sc)->lock, MA_OWNED) 66129198Scognet 67129198Scognetstatic int intsmb_probe(device_t); 68129198Scognetstatic int intsmb_attach(device_t); 69129198Scognetstatic int intsmb_detach(device_t); 70129198Scognetstatic int intsmb_intr(struct intsmb_softc *sc); 71129198Scognetstatic int intsmb_slvintr(struct intsmb_softc *sc); 72129198Scognetstatic void intsmb_alrintr(struct intsmb_softc *sc); 73129198Scognetstatic int intsmb_callback(device_t dev, int index, void *data); 74129198Scognetstatic int intsmb_quick(device_t dev, u_char slave, int how); 75129198Scognetstatic int intsmb_sendb(device_t dev, u_char slave, char byte); 76129198Scognetstatic int intsmb_recvb(device_t dev, u_char slave, char *byte); 77129198Scognetstatic int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte); 78129198Scognetstatic int intsmb_writew(device_t dev, u_char slave, char cmd, short word); 79129198Scognetstatic int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte); 80129198Scognetstatic int intsmb_readw(device_t dev, u_char slave, char cmd, short *word); 81129198Scognetstatic int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata); 82129198Scognetstatic int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf); 83129198Scognetstatic int intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf); 84129198Scognetstatic void intsmb_start(struct intsmb_softc *sc, u_char cmd, int nointr); 85129198Scognetstatic int intsmb_stop(struct intsmb_softc *sc); 86135644Scognetstatic int intsmb_stop_poll(struct intsmb_softc *sc); 87135644Scognetstatic int intsmb_free(struct intsmb_softc *sc); 88135644Scognetstatic void intsmb_rawintr(void *arg); 89146597Scognet 90135644Scognetstatic int 91135644Scognetintsmb_probe(device_t dev) 92129198Scognet{ 93135644Scognet 94135644Scognet switch (pci_get_devid(dev)) { 95135644Scognet case 0x71138086: /* Intel 82371AB */ 96156191Scognet case 0x719b8086: /* Intel 82443MX */ 97156191Scognet#if 0 98147591Scognet /* Not a good idea yet, this stops isab0 functioning */ 99135644Scognet case 0x02001166: /* ServerWorks OSB4 */ 100129198Scognet#endif 101129198Scognet device_set_desc(dev, "Intel PIIX4 SMBUS Interface"); 102147591Scognet break; 103147591Scognet case 0x43721002: 104147591Scognet device_set_desc(dev, "ATI IXP400 SMBus Controller"); 105146597Scognet break; 106146597Scognet case 0x43851002: 107146597Scognet device_set_desc(dev, "AMD SB600/7xx/8xx/9xx SMBus Controller"); 108146597Scognet break; 109146597Scognet case 0x780b1022: /* AMD FCH */ 110146597Scognet if (pci_get_revid(dev) < 0x40) 111146597Scognet return (ENXIO); 112147591Scognet device_set_desc(dev, "AMD FCH SMBus Controller"); 113147591Scognet break; 114147591Scognet default: 115147591Scognet return (ENXIO); 116147591Scognet } 117147591Scognet 118147591Scognet return (BUS_PROBE_DEFAULT); 119147591Scognet} 120147591Scognet 121147591Scognetstatic uint8_t 122147591Scognetsb8xx_pmio_read(struct resource *res, uint8_t reg) 123129198Scognet{ 124129198Scognet bus_write_1(res, 0, reg); /* Index */ 125129198Scognet return (bus_read_1(res, 1)); /* Data */ 126129198Scognet} 127137758Scognet 128140349Scognetstatic int 129137758Scognetsb8xx_attach(device_t dev) 130137760Scognet{ 131135644Scognet static const int AMDSB_PMIO_INDEX = 0xcd6; 132129198Scognet static const int AMDSB_PMIO_WIDTH = 2; 133129198Scognet static const int AMDSB8_SMBUS_ADDR = 0x2c; 134129198Scognet static const int AMDSB8_SMBUS_EN = 0x01; 135129198Scognet static const int AMDSB8_SMBUS_ADDR_MASK = ~0x1fu; 136129198Scognet static const int AMDSB_SMBIO_WIDTH = 0x14; 137129198Scognet static const int AMDSB_SMBUS_CFG = 0x10; 138129198Scognet static const int AMDSB_SMBUS_IRQ = 0x01; 139129198Scognet static const int AMDSB_SMBUS_REV_MASK = ~0x0fu; 140129198Scognet static const int AMDSB_SMBUS_REV_SHIFT = 4; 141129198Scognet static const int AMDSB_IO_RID = 0; 142129198Scognet 143129198Scognet struct intsmb_softc *sc; 144129198Scognet struct resource *res; 145129198Scognet uint16_t addr; 146129198Scognet uint8_t cfg; 147129198Scognet int rid; 148129198Scognet int rc; 149129198Scognet 150129198Scognet sc = device_get_softc(dev); 151129198Scognet rid = AMDSB_IO_RID; 152129198Scognet rc = bus_set_resource(dev, SYS_RES_IOPORT, rid, AMDSB_PMIO_INDEX, 153129198Scognet AMDSB_PMIO_WIDTH); 154129198Scognet if (rc != 0) { 155129198Scognet device_printf(dev, "bus_set_resource for PM IO failed\n"); 156129198Scognet return (ENXIO); 157129198Scognet } 158129198Scognet res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 159129198Scognet RF_ACTIVE | RF_SHAREABLE); 160129198Scognet if (res == NULL) { 161129198Scognet device_printf(dev, "bus_alloc_resource for PM IO failed\n"); 162129198Scognet return (ENXIO); 163129198Scognet } 164129198Scognet 165129198Scognet addr = sb8xx_pmio_read(res, AMDSB8_SMBUS_ADDR + 1); 166129198Scognet addr <<= 8; 167129198Scognet addr |= sb8xx_pmio_read(res, AMDSB8_SMBUS_ADDR); 168129198Scognet 169129198Scognet bus_release_resource(dev, SYS_RES_IOPORT, rid, res); 170129198Scognet bus_delete_resource(dev, SYS_RES_IOPORT, rid); 171129198Scognet 172129198Scognet if ((addr & AMDSB8_SMBUS_EN) == 0) { 173129198Scognet device_printf(dev, "SB8xx SMBus not enabled\n"); 174129198Scognet return (ENXIO); 175129198Scognet } 176129198Scognet 177129198Scognet addr &= AMDSB8_SMBUS_ADDR_MASK; 178129198Scognet sc->io_rid = AMDSB_IO_RID; 179129198Scognet rc = bus_set_resource(dev, SYS_RES_IOPORT, sc->io_rid, addr, 180129198Scognet AMDSB_SMBIO_WIDTH); 181129198Scognet if (rc != 0) { 182129198Scognet device_printf(dev, "bus_set_resource for SMBus IO failed\n"); 183129198Scognet return (ENXIO); 184129198Scognet } 185129198Scognet if (res == NULL) { 186129198Scognet device_printf(dev, "bus_alloc_resource for SMBus IO failed\n"); 187147591Scognet return (ENXIO); 188146597Scognet } 189146597Scognet sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->io_rid, 190146597Scognet RF_ACTIVE | RF_SHAREABLE); 191146597Scognet cfg = bus_read_1(sc->io_res, AMDSB_SMBUS_CFG); 192146597Scognet 193147591Scognet sc->poll = 1; 194150860Scognet device_printf(dev, "intr %s disabled ", 195150860Scognet (cfg & AMDSB_SMBUS_IRQ) != 0 ? "IRQ" : "SMI"); 196146597Scognet printf("revision %d\n", 197147591Scognet (cfg & AMDSB_SMBUS_REV_MASK) >> AMDSB_SMBUS_REV_SHIFT); 198147591Scognet 199147591Scognet return (0); 200147591Scognet} 201147591Scognet 202147591Scognetstatic void 203146597Scognetintsmb_release_resources(device_t dev) 204146597Scognet{ 205146597Scognet struct intsmb_softc *sc = device_get_softc(dev); 206147591Scognet 207146597Scognet if (sc->smbus) 208146597Scognet device_delete_child(dev, sc->smbus); 209146597Scognet if (sc->irq_hand) 210146597Scognet bus_teardown_intr(dev, sc->irq_res, sc->irq_hand); 211146597Scognet if (sc->irq_res) 212146597Scognet bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 213147591Scognet if (sc->io_res) 214146597Scognet bus_release_resource(dev, SYS_RES_IOPORT, sc->io_rid, 215146597Scognet sc->io_res); 216146597Scognet mtx_destroy(&sc->lock); 217146597Scognet} 218129198Scognet 219129198Scognetstatic int 220129198Scognetintsmb_attach(device_t dev) 221135644Scognet{ 222135644Scognet struct intsmb_softc *sc = device_get_softc(dev); 223129198Scognet int error, rid, value; 224129198Scognet int intr; 225129198Scognet char *str; 226129198Scognet 227129198Scognet sc->dev = dev; 228129198Scognet 229129198Scognet mtx_init(&sc->lock, device_get_nameunit(dev), "intsmb", MTX_DEF); 230129198Scognet 231129198Scognet sc->cfg_irq9 = 0; 232129198Scognet switch (pci_get_devid(dev)) { 233129198Scognet#ifndef NO_CHANGE_PCICONF 234129198Scognet case 0x71138086: /* Intel 82371AB */ 235129198Scognet case 0x719b8086: /* Intel 82443MX */ 236129198Scognet /* Changing configuration is allowed. */ 237140313Scognet sc->cfg_irq9 = 1; 238143294Smux break; 239143284Smux#endif 240129198Scognet case 0x43851002: 241140313Scognet if (pci_get_revid(dev) >= 0x40) 242129198Scognet sc->sb8xx = 1; 243129198Scognet break; 244129198Scognet case 0x780b1022: 245129198Scognet sc->sb8xx = 1; 246129198Scognet break; 247129198Scognet } 248129198Scognet 249129198Scognet if (sc->sb8xx) { 250129198Scognet error = sb8xx_attach(dev); 251129198Scognet if (error != 0) 252129198Scognet goto fail; 253129198Scognet else 254129198Scognet goto no_intr; 255129198Scognet } 256129198Scognet 257135644Scognet sc->io_rid = PCI_BASE_ADDR_SMB; 258129198Scognet sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->io_rid, 259129198Scognet RF_ACTIVE); 260129198Scognet if (sc->io_res == NULL) { 261129198Scognet device_printf(dev, "Could not allocate I/O space\n"); 262129198Scognet error = ENXIO; 263129198Scognet goto fail; 264129198Scognet } 265129198Scognet 266129198Scognet if (sc->cfg_irq9) { 267129198Scognet pci_write_config(dev, PCIR_INTLINE, 0x9, 1); 268129198Scognet pci_write_config(dev, PCI_HST_CFG_SMB, 269129198Scognet PCI_INTR_SMB_IRQ9 | PCI_INTR_SMB_ENABLE, 1); 270129198Scognet } 271134934Sscottl value = pci_read_config(dev, PCI_HST_CFG_SMB, 1); 272134934Sscottl sc->poll = (value & PCI_INTR_SMB_ENABLE) == 0; 273134934Sscottl intr = value & PCI_INTR_SMB_MASK; 274134934Sscottl switch (intr) { 275134934Sscottl case PCI_INTR_SMB_SMI: 276129198Scognet str = "SMI"; 277129198Scognet break; 278129198Scognet case PCI_INTR_SMB_IRQ9: 279129198Scognet str = "IRQ 9"; 280129198Scognet break; 281129198Scognet case PCI_INTR_SMB_IRQ_PCI: 282129198Scognet str = "PCI IRQ"; 283129198Scognet break; 284129198Scognet default: 285129198Scognet str = "BOGUS"; 286129198Scognet } 287129198Scognet 288129198Scognet device_printf(dev, "intr %s %s ", str, 289129198Scognet sc->poll == 0 ? "enabled" : "disabled"); 290143294Smux printf("revision %d\n", pci_read_config(dev, PCI_REVID_SMB, 1)); 291143284Smux 292140313Scognet if (!sc->poll && intr == PCI_INTR_SMB_SMI) { 293129198Scognet device_printf(dev, 294129198Scognet "using polling mode when configured interrupt is SMI\n"); 295129198Scognet sc->poll = 1; 296129198Scognet } 297129198Scognet 298129198Scognet if (sc->poll) 299140680Scognet goto no_intr; 300140313Scognet 301140680Scognet if (intr != PCI_INTR_SMB_IRQ9 && intr != PCI_INTR_SMB_IRQ_PCI) { 302140313Scognet device_printf(dev, "Unsupported interrupt mode\n"); 303129198Scognet error = ENXIO; 304129198Scognet goto fail; 305129198Scognet } 306129198Scognet 307129198Scognet /* Force IRQ 9. */ 308129198Scognet rid = 0; 309129198Scognet if (sc->cfg_irq9) 310129198Scognet bus_set_resource(dev, SYS_RES_IRQ, rid, 9, 1); 311129198Scognet 312129198Scognet sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 313129198Scognet RF_SHAREABLE | RF_ACTIVE); 314129198Scognet if (sc->irq_res == NULL) { 315129198Scognet device_printf(dev, "Could not allocate irq\n"); 316129198Scognet error = ENXIO; 317129198Scognet goto fail; 318129198Scognet } 319129198Scognet 320129198Scognet error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 321129198Scognet NULL, intsmb_rawintr, sc, &sc->irq_hand); 322129198Scognet if (error) { 323129198Scognet device_printf(dev, "Failed to map intr\n"); 324129198Scognet goto fail; 325143294Smux } 326140313Scognet 327129198Scognetno_intr: 328129198Scognet sc->isbusy = 0; 329129198Scognet sc->smbus = device_add_child(dev, "smbus", -1); 330129198Scognet if (sc->smbus == NULL) { 331129198Scognet device_printf(dev, "failed to add smbus child\n"); 332129198Scognet error = ENXIO; 333129198Scognet goto fail; 334129198Scognet } 335129198Scognet error = device_probe_and_attach(sc->smbus); 336129198Scognet if (error) { 337129198Scognet device_printf(dev, "failed to probe+attach smbus child\n"); 338140680Scognet goto fail; 339140313Scognet } 340140680Scognet 341129198Scognet#ifdef ENABLE_ALART 342146597Scognet /* Enable Arart */ 343140313Scognet bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN); 344143294Smux#endif 345129198Scognet return (0); 346140313Scognet 347129198Scognetfail: 348135644Scognet intsmb_release_resources(dev); 349129198Scognet return (error); 350129198Scognet} 351143294Smux 352143284Smuxstatic int 353140313Scognetintsmb_detach(device_t dev) 354129198Scognet{ 355129198Scognet int error; 356129198Scognet 357129198Scognet error = bus_generic_detach(dev); 358129198Scognet if (error) { 359129198Scognet device_printf(dev, "bus detach failed\n"); 360129198Scognet return (error); 361129198Scognet } 362129198Scognet 363129198Scognet intsmb_release_resources(dev); 364135644Scognet return (0); 365146597Scognet} 366129198Scognet 367143294Smuxstatic void 368129198Scognetintsmb_rawintr(void *arg) 369129198Scognet{ 370129198Scognet struct intsmb_softc *sc = arg; 371129198Scognet 372129198Scognet INTSMB_LOCK(sc); 373129198Scognet intsmb_intr(sc); 374129198Scognet intsmb_slvintr(sc); 375129198Scognet INTSMB_UNLOCK(sc); 376129198Scognet} 377129198Scognet 378129198Scognetstatic int 379129198Scognetintsmb_callback(device_t dev, int index, void *data) 380135644Scognet{ 381129198Scognet int error = 0; 382129198Scognet 383129198Scognet switch (index) { 384129198Scognet case SMB_REQUEST_BUS: 385129198Scognet break; 386129198Scognet case SMB_RELEASE_BUS: 387129198Scognet break; 388129198Scognet default: 389129198Scognet error = SMB_EINVAL; 390129198Scognet } 391146597Scognet 392143671Sjmg return (error); 393143671Sjmg} 394143671Sjmg 395143671Sjmg/* Counterpart of smbtx_smb_free(). */ 396135644Scognetstatic int 397143671Sjmgintsmb_free(struct intsmb_softc *sc) 398143671Sjmg{ 399143671Sjmg 400135644Scognet INTSMB_LOCK_ASSERT(sc); 401129198Scognet if ((bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & PIIX4_SMBHSTSTAT_BUSY) || 402129198Scognet#ifdef ENABLE_ALART 403129198Scognet (bus_read_1(sc->io_res, PIIX4_SMBSLVSTS) & PIIX4_SMBSLVSTS_BUSY) || 404129198Scognet#endif 405129198Scognet sc->isbusy) 406129198Scognet return (SMB_EBUSY); 407129198Scognet 408129198Scognet sc->isbusy = 1; 409129198Scognet /* Disable Interrupt in slave part. */ 410129198Scognet#ifndef ENABLE_ALART 411129198Scognet bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 0); 412129198Scognet#endif 413140313Scognet /* Reset INTR Flag to prepare INTR. */ 414140313Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTSTS, 415146597Scognet PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR | 416140313Scognet PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL); 417140313Scognet return (0); 418129198Scognet} 419129198Scognet 420129198Scognetstatic int 421156191Scognetintsmb_intr(struct intsmb_softc *sc) 422156191Scognet{ 423156191Scognet int status, tmp; 424156191Scognet 425156191Scognet status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 426156191Scognet if (status & PIIX4_SMBHSTSTAT_BUSY) 427156191Scognet return (1); 428156191Scognet 429156191Scognet if (status & (PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR | 430156191Scognet PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL)) { 431156191Scognet 432156191Scognet tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 433156191Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, 434156191Scognet tmp & ~PIIX4_SMBHSTCNT_INTREN); 435156191Scognet if (sc->isbusy) { 436156191Scognet sc->isbusy = 0; 437156191Scognet wakeup(sc); 438129198Scognet } 439129198Scognet return (0); 440129198Scognet } 441129198Scognet return (1); /* Not Completed */ 442129198Scognet} 443129198Scognet 444129198Scognetstatic int 445129198Scognetintsmb_slvintr(struct intsmb_softc *sc) 446129198Scognet{ 447129198Scognet int status; 448156191Scognet 449156191Scognet status = bus_read_1(sc->io_res, PIIX4_SMBSLVSTS); 450156191Scognet if (status & PIIX4_SMBSLVSTS_BUSY) 451156191Scognet return (1); 452156191Scognet if (status & PIIX4_SMBSLVSTS_ALART) 453156191Scognet intsmb_alrintr(sc); 454129198Scognet else if (status & ~(PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 455129198Scognet | PIIX4_SMBSLVSTS_SDW1)) { 456129198Scognet } 457129198Scognet 458129198Scognet /* Reset Status Register */ 459135644Scognet bus_write_1(sc->io_res, PIIX4_SMBSLVSTS, 460146597Scognet PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 | 461143294Smux PIIX4_SMBSLVSTS_SDW1 | PIIX4_SMBSLVSTS_SLV); 462129198Scognet return (0); 463129198Scognet} 464129198Scognet 465129198Scognetstatic void 466129198Scognetintsmb_alrintr(struct intsmb_softc *sc) 467129198Scognet{ 468129198Scognet int slvcnt; 469129198Scognet#ifdef ENABLE_ALART 470147591Scognet int error; 471140349Scognet uint8_t addr; 472137758Scognet#endif 473137760Scognet 474129198Scognet /* Stop generating INTR from ALART. */ 475129198Scognet slvcnt = bus_read_1(sc->io_res, PIIX4_SMBSLVCNT); 476129198Scognet#ifdef ENABLE_ALART 477129198Scognet bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 478129198Scognet slvcnt & ~PIIX4_SMBSLVCNT_ALTEN); 479129198Scognet#endif 480129198Scognet DELAY(5); 481129198Scognet 482129198Scognet /* Ask bus who asserted it and then ask it what's the matter. */ 483129198Scognet#ifdef ENABLE_ALART 484129198Scognet error = intsmb_free(sc); 485129198Scognet if (error) 486129198Scognet return; 487140313Scognet 488140313Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTADD, SMBALTRESP | LSB); 489140313Scognet intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 1); 490129198Scognet error = intsmb_stop_poll(sc); 491129198Scognet if (error) 492129198Scognet device_printf(sc->dev, "ALART: ERROR\n"); 493129198Scognet else { 494129198Scognet addr = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 495129198Scognet device_printf(sc->dev, "ALART_RESPONSE: 0x%x\n", addr); 496129198Scognet } 497147591Scognet 498129198Scognet /* Re-enable INTR from ALART. */ 499129198Scognet bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 500129198Scognet slvcnt | PIIX4_SMBSLVCNT_ALTEN); 501129198Scognet DELAY(5); 502129198Scognet#endif 503129198Scognet} 504135644Scognet 505129198Scognetstatic void 506129198Scognetintsmb_start(struct intsmb_softc *sc, unsigned char cmd, int nointr) 507129198Scognet{ 508129198Scognet unsigned char tmp; 509132514Scognet 510129198Scognet INTSMB_LOCK_ASSERT(sc); 511129198Scognet tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 512129198Scognet tmp &= 0xe0; 513129198Scognet tmp |= cmd; 514129198Scognet tmp |= PIIX4_SMBHSTCNT_START; 515129198Scognet 516135644Scognet /* While not in autoconfiguration enable interrupts. */ 517135644Scognet if (!sc->poll && !cold && !nointr) 518129198Scognet tmp |= PIIX4_SMBHSTCNT_INTREN; 519129198Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp); 520129198Scognet} 521129198Scognet 522129198Scognetstatic int 523129198Scognetintsmb_error(device_t dev, int status) 524135644Scognet{ 525129198Scognet int error = 0; 526129198Scognet 527129198Scognet if (status & PIIX4_SMBHSTSTAT_ERR) 528129198Scognet error |= SMB_EBUSERR; 529129198Scognet if (status & PIIX4_SMBHSTSTAT_BUSC) 530135644Scognet error |= SMB_ECOLLI; 531129198Scognet if (status & PIIX4_SMBHSTSTAT_FAIL) 532129198Scognet error |= SMB_ENOACK; 533135644Scognet 534135644Scognet if (error != 0 && bootverbose) 535135644Scognet device_printf(dev, "error = %d, status = %#x\n", error, status); 536135644Scognet 537135644Scognet return (error); 538135644Scognet} 539135644Scognet 540135644Scognet/* 541135644Scognet * Polling Code. 542135644Scognet * 543135644Scognet * Polling is not encouraged because it requires waiting for the 544135644Scognet * device if it is busy. 545135644Scognet * (29063505.pdf from Intel) But during boot, interrupt cannot be used, so use 546135644Scognet * polling code then. 547129198Scognet */ 548129198Scognetstatic int 549129198Scognetintsmb_stop_poll(struct intsmb_softc *sc) 550129198Scognet{ 551129198Scognet int error, i, status, tmp; 552129198Scognet 553129198Scognet INTSMB_LOCK_ASSERT(sc); 554129198Scognet 555129198Scognet /* First, wait for busy to be set. */ 556129198Scognet for (i = 0; i < 0x7fff; i++) 557129198Scognet if (bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & 558129198Scognet PIIX4_SMBHSTSTAT_BUSY) 559129198Scognet break; 560129198Scognet 561129198Scognet /* Wait for busy to clear. */ 562129198Scognet for (i = 0; i < 0x7fff; i++) { 563129198Scognet status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 564129198Scognet if (!(status & PIIX4_SMBHSTSTAT_BUSY)) { 565129198Scognet sc->isbusy = 0; 566129198Scognet error = intsmb_error(sc->dev, status); 567137760Scognet return (error); 568137760Scognet } 569137760Scognet } 570137760Scognet 571137760Scognet /* Timed out waiting for busy to clear. */ 572137760Scognet sc->isbusy = 0; 573140349Scognet tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 574137760Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp & ~PIIX4_SMBHSTCNT_INTREN); 575137760Scognet return (SMB_ETIMEOUT); 576137760Scognet} 577129198Scognet 578129198Scognet/* 579129198Scognet * Wait for completion and return result. 580135644Scognet */ 581135644Scognetstatic int 582135644Scognetintsmb_stop(struct intsmb_softc *sc) 583129198Scognet{ 584129198Scognet int error, status; 585129198Scognet 586129198Scognet INTSMB_LOCK_ASSERT(sc); 587129198Scognet 588129198Scognet if (sc->poll || cold) 589129198Scognet /* So that it can use device during device probe on SMBus. */ 590129198Scognet return (intsmb_stop_poll(sc)); 591129198Scognet 592129198Scognet error = msleep(sc, &sc->lock, PWAIT | PCATCH, "SMBWAI", hz / 8); 593129198Scognet if (error == 0) { 594129198Scognet status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 595129198Scognet if (!(status & PIIX4_SMBHSTSTAT_BUSY)) { 596129198Scognet error = intsmb_error(sc->dev, status); 597129198Scognet if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR)) 598129198Scognet device_printf(sc->dev, "unknown cause why?\n"); 599129198Scognet#ifdef ENABLE_ALART 600140682Scognet bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 601140682Scognet PIIX4_SMBSLVCNT_ALTEN); 602140682Scognet#endif 603140682Scognet return (error); 604140682Scognet } 605140682Scognet } 606140682Scognet 607140682Scognet /* Timeout Procedure. */ 608140682Scognet sc->isbusy = 0; 609143063Sjoerg 610140682Scognet /* Re-enable suppressed interrupt from slave part. */ 611140682Scognet bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN); 612140682Scognet if (error == EWOULDBLOCK) 613140682Scognet return (SMB_ETIMEOUT); 614140682Scognet else 615143671Sjmg return (SMB_EABORT); 616143671Sjmg} 617140682Scognet 618140682Scognetstatic int 619140682Scognetintsmb_quick(device_t dev, u_char slave, int how) 620140682Scognet{ 621140682Scognet struct intsmb_softc *sc = device_get_softc(dev); 622140682Scognet int error; 623140682Scognet u_char data; 624140682Scognet 625140682Scognet data = slave; 626140682Scognet 627140682Scognet /* Quick command is part of Address, I think. */ 628140682Scognet switch(how) { 629143294Smux case SMB_QWRITE: 630143284Smux data &= ~LSB; 631140682Scognet break; 632140682Scognet case SMB_QREAD: 633140682Scognet data |= LSB; 634140682Scognet break; 635140682Scognet default: 636129198Scognet return (SMB_EINVAL); 637129198Scognet } 638129198Scognet 639129198Scognet INTSMB_LOCK(sc); 640129198Scognet error = intsmb_free(sc); 641129198Scognet if (error) { 642129198Scognet INTSMB_UNLOCK(sc); 643143063Sjoerg return (error); 644129198Scognet } 645129198Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTADD, data); 646129198Scognet intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_QUICK, 0); 647129198Scognet error = intsmb_stop(sc); 648137760Scognet INTSMB_UNLOCK(sc); 649129198Scognet return (error); 650129198Scognet} 651129198Scognet 652135644Scognetstatic int 653135644Scognetintsmb_sendb(device_t dev, u_char slave, char byte) 654135644Scognet{ 655146597Scognet struct intsmb_softc *sc = device_get_softc(dev); 656129198Scognet int error; 657129198Scognet 658129198Scognet INTSMB_LOCK(sc); 659129198Scognet error = intsmb_free(sc); 660129198Scognet if (error) { 661146597Scognet INTSMB_UNLOCK(sc); 662129198Scognet return (error); 663137758Scognet } 664137760Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 665146597Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, byte); 666146597Scognet intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0); 667129198Scognet error = intsmb_stop(sc); 668129198Scognet INTSMB_UNLOCK(sc); 669129198Scognet return (error); 670129198Scognet} 671129198Scognet 672129198Scognetstatic int 673129198Scognetintsmb_recvb(device_t dev, u_char slave, char *byte) 674129198Scognet{ 675129198Scognet struct intsmb_softc *sc = device_get_softc(dev); 676129198Scognet int error; 677129198Scognet 678135644Scognet INTSMB_LOCK(sc); 679129198Scognet error = intsmb_free(sc); 680129198Scognet if (error) { 681143294Smux INTSMB_UNLOCK(sc); 682143284Smux return (error); 683140313Scognet } 684129198Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 685129198Scognet intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0); 686129198Scognet error = intsmb_stop(sc); 687140310Scognet if (error == 0) { 688140310Scognet#ifdef RECV_IS_IN_CMD 689140310Scognet /* 690140310Scognet * Linux SMBus stuff also troubles 691140310Scognet * Because Intel's datasheet does not make clear. 692140310Scognet */ 693140310Scognet *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTCMD); 694140310Scognet#else 695140310Scognet *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 696140310Scognet#endif 697140349Scognet } 698140349Scognet INTSMB_UNLOCK(sc); 699140349Scognet return (error); 700146597Scognet} 701140310Scognet 702140310Scognetstatic int 703140310Scognetintsmb_writeb(device_t dev, u_char slave, char cmd, char byte) 704140310Scognet{ 705140310Scognet struct intsmb_softc *sc = device_get_softc(dev); 706140310Scognet int error; 707140310Scognet 708140310Scognet INTSMB_LOCK(sc); 709140310Scognet error = intsmb_free(sc); 710140310Scognet if (error) { 711146597Scognet INTSMB_UNLOCK(sc); 712140310Scognet return (error); 713140310Scognet } 714140310Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 715140310Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 716140310Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, byte); 717140310Scognet intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0); 718140310Scognet error = intsmb_stop(sc); 719140310Scognet INTSMB_UNLOCK(sc); 720143294Smux return (error); 721143284Smux} 722140310Scognet 723140310Scognetstatic int 724140310Scognetintsmb_writew(device_t dev, u_char slave, char cmd, short word) 725129198Scognet{ 726129198Scognet struct intsmb_softc *sc = device_get_softc(dev); 727129198Scognet int error; 728129198Scognet 729129198Scognet INTSMB_LOCK(sc); 730129198Scognet error = intsmb_free(sc); 731129198Scognet if (error) { 732129198Scognet INTSMB_UNLOCK(sc); 733129198Scognet return (error); 734143063Sjoerg } 735129198Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 736129198Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 737129198Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, word & 0xff); 738129198Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (word >> 8) & 0xff); 739137760Scognet intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 740129198Scognet error = intsmb_stop(sc); 741129198Scognet INTSMB_UNLOCK(sc); 742137758Scognet return (error); 743129198Scognet} 744129198Scognet 745129198Scognetstatic int 746135644Scognetintsmb_readb(device_t dev, u_char slave, char cmd, char *byte) 747135644Scognet{ 748135644Scognet struct intsmb_softc *sc = device_get_softc(dev); 749146597Scognet int error; 750129198Scognet 751129198Scognet INTSMB_LOCK(sc); 752138683Scognet error = intsmb_free(sc); 753138683Scognet if (error) { 754137758Scognet INTSMB_UNLOCK(sc); 755137758Scognet return (error); 756137758Scognet } 757129198Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 758137760Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 759137760Scognet intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0); 760129198Scognet error = intsmb_stop(sc); 761129198Scognet if (error == 0) 762129198Scognet *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 763129198Scognet INTSMB_UNLOCK(sc); 764129198Scognet return (error); 765129198Scognet} 766129198Scognet 767129198Scognetstatic int 768129198Scognetintsmb_readw(device_t dev, u_char slave, char cmd, short *word) 769129198Scognet{ 770129198Scognet struct intsmb_softc *sc = device_get_softc(dev); 771137760Scognet int error; 772129198Scognet 773146597Scognet INTSMB_LOCK(sc); 774129198Scognet error = intsmb_free(sc); 775129198Scognet if (error) { 776129198Scognet INTSMB_UNLOCK(sc); 777129198Scognet return (error); 778129198Scognet } 779129198Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 780129198Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 781129198Scognet intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 782129198Scognet error = intsmb_stop(sc); 783129198Scognet if (error == 0) { 784129198Scognet *word = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 785129198Scognet *word |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8; 786129198Scognet } 787129198Scognet INTSMB_UNLOCK(sc); 788143294Smux return (error); 789143284Smux} 790129198Scognet 791129198Scognet/* 792129198Scognet * Data sheet claims that it implements all function, but also claims 793129198Scognet * that it implements 7 function and not mention PCALL. So I don't know 794135644Scognet * whether it will work. 795129198Scognet */ 796129198Scognetstatic int 797143655Sjmgintsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata) 798150893Scognet{ 799135644Scognet#ifdef PROCCALL_TEST 800129198Scognet struct intsmb_softc *sc = device_get_softc(dev); 801129198Scognet int error; 802129198Scognet 803147591Scognet INTSMB_LOCK(sc); 804135644Scognet error = intsmb_free(sc); 805135644Scognet if (error) { 806135644Scognet INTSMB_UNLOCK(sc); 807135644Scognet return (error); 808135644Scognet } 809159107Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 810159107Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 811159107Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, sdata & 0xff); 812159107Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (sdata & 0xff) >> 8); 813159107Scognet intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 814159107Scognet error = intsmb_stop(sc); 815159107Scognet if (error == 0) { 816135644Scognet *rdata = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 817159107Scognet *rdata |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8; 818159107Scognet } 819135644Scognet INTSMB_UNLOCK(sc); 820135644Scognet return (error); 821129198Scognet#else 822143655Sjmg return (SMB_ENOTSUPP); 823129198Scognet#endif 824135644Scognet} 825135644Scognet 826135644Scognetstatic int 827135644Scognetintsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) 828135644Scognet{ 829159107Scognet struct intsmb_softc *sc = device_get_softc(dev); 830129198Scognet int error, i; 831150893Scognet 832129198Scognet if (count > SMBBLOCKTRANS_MAX || count == 0) 833150860Scognet return (SMB_EINVAL); 834146597Scognet 835146597Scognet INTSMB_LOCK(sc); 836146597Scognet error = intsmb_free(sc); 837143294Smux if (error) { 838135644Scognet INTSMB_UNLOCK(sc); 839135644Scognet return (error); 840135644Scognet } 841135644Scognet 842135644Scognet /* Reset internal array index. */ 843135644Scognet bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 844135644Scognet 845147591Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 846147591Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 847135644Scognet for (i = 0; i < count; i++) 848135644Scognet bus_write_1(sc->io_res, PIIX4_SMBBLKDAT, buf[i]); 849135644Scognet bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, count); 850135644Scognet intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0); 851135644Scognet error = intsmb_stop(sc); 852135644Scognet INTSMB_UNLOCK(sc); 853135644Scognet return (error); 854135644Scognet} 855135644Scognet 856135644Scognetstatic int 857135644Scognetintsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf) 858135644Scognet{ 859135644Scognet struct intsmb_softc *sc = device_get_softc(dev); 860135644Scognet int error, i; 861135644Scognet u_char data, nread; 862135644Scognet 863135644Scognet if (*count > SMBBLOCKTRANS_MAX || *count == 0) 864135644Scognet return (SMB_EINVAL); 865135644Scognet 866129198Scognet INTSMB_LOCK(sc); 867135644Scognet error = intsmb_free(sc); 868129198Scognet if (error) { 869 INTSMB_UNLOCK(sc); 870 return (error); 871 } 872 873 /* Reset internal array index. */ 874 bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 875 876 bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 877 bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 878 bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, *count); 879 intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0); 880 error = intsmb_stop(sc); 881 if (error == 0) { 882 nread = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 883 if (nread != 0 && nread <= SMBBLOCKTRANS_MAX) { 884 for (i = 0; i < nread; i++) { 885 data = bus_read_1(sc->io_res, PIIX4_SMBBLKDAT); 886 if (i < *count) 887 buf[i] = data; 888 } 889 *count = nread; 890 } else 891 error = SMB_EBUSERR; 892 } 893 INTSMB_UNLOCK(sc); 894 return (error); 895} 896 897static devclass_t intsmb_devclass; 898 899static device_method_t intsmb_methods[] = { 900 /* Device interface */ 901 DEVMETHOD(device_probe, intsmb_probe), 902 DEVMETHOD(device_attach, intsmb_attach), 903 DEVMETHOD(device_detach, intsmb_detach), 904 905 /* SMBus interface */ 906 DEVMETHOD(smbus_callback, intsmb_callback), 907 DEVMETHOD(smbus_quick, intsmb_quick), 908 DEVMETHOD(smbus_sendb, intsmb_sendb), 909 DEVMETHOD(smbus_recvb, intsmb_recvb), 910 DEVMETHOD(smbus_writeb, intsmb_writeb), 911 DEVMETHOD(smbus_writew, intsmb_writew), 912 DEVMETHOD(smbus_readb, intsmb_readb), 913 DEVMETHOD(smbus_readw, intsmb_readw), 914 DEVMETHOD(smbus_pcall, intsmb_pcall), 915 DEVMETHOD(smbus_bwrite, intsmb_bwrite), 916 DEVMETHOD(smbus_bread, intsmb_bread), 917 918 DEVMETHOD_END 919}; 920 921static driver_t intsmb_driver = { 922 "intsmb", 923 intsmb_methods, 924 sizeof(struct intsmb_softc), 925}; 926 927DRIVER_MODULE_ORDERED(intsmb, pci, intsmb_driver, intsmb_devclass, 0, 0, 928 SI_ORDER_ANY); 929DRIVER_MODULE(smbus, intsmb, smbus_driver, smbus_devclass, 0, 0); 930MODULE_DEPEND(intsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 931MODULE_VERSION(intsmb, 1); 932