intpm.c revision 227843
1/*- 2 * Copyright (c) 1998, 1999 Takanori Watanabe 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sys/pci/intpm.c 227843 2011-11-22 21:28:20Z marius $"); 29 30#include <sys/param.h> 31#include <sys/systm.h> 32#include <sys/bus.h> 33#include <sys/kernel.h> 34#include <sys/lock.h> 35#include <sys/module.h> 36#include <sys/mutex.h> 37#include <sys/rman.h> 38#include <machine/bus.h> 39#include <dev/smbus/smbconf.h> 40 41#include "smbus_if.h" 42 43#include <dev/pci/pcireg.h> 44#include <dev/pci/pcivar.h> 45#include <pci/intpmreg.h> 46 47#include "opt_intpm.h" 48 49struct intsmb_softc { 50 device_t dev; 51 struct resource *io_res; 52 struct resource *irq_res; 53 void *irq_hand; 54 device_t smbus; 55 int isbusy; 56 int cfg_irq9; 57 int poll; 58 struct mtx lock; 59}; 60 61#define INTSMB_LOCK(sc) mtx_lock(&(sc)->lock) 62#define INTSMB_UNLOCK(sc) mtx_unlock(&(sc)->lock) 63#define INTSMB_LOCK_ASSERT(sc) mtx_assert(&(sc)->lock, MA_OWNED) 64 65static int intsmb_probe(device_t); 66static int intsmb_attach(device_t); 67static int intsmb_detach(device_t); 68static int intsmb_intr(struct intsmb_softc *sc); 69static int intsmb_slvintr(struct intsmb_softc *sc); 70static void intsmb_alrintr(struct intsmb_softc *sc); 71static int intsmb_callback(device_t dev, int index, void *data); 72static int intsmb_quick(device_t dev, u_char slave, int how); 73static int intsmb_sendb(device_t dev, u_char slave, char byte); 74static int intsmb_recvb(device_t dev, u_char slave, char *byte); 75static int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte); 76static int intsmb_writew(device_t dev, u_char slave, char cmd, short word); 77static int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte); 78static int intsmb_readw(device_t dev, u_char slave, char cmd, short *word); 79static int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata); 80static int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf); 81static int intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf); 82static void intsmb_start(struct intsmb_softc *sc, u_char cmd, int nointr); 83static int intsmb_stop(struct intsmb_softc *sc); 84static int intsmb_stop_poll(struct intsmb_softc *sc); 85static int intsmb_free(struct intsmb_softc *sc); 86static void intsmb_rawintr(void *arg); 87 88static int 89intsmb_probe(device_t dev) 90{ 91 92 switch (pci_get_devid(dev)) { 93 case 0x71138086: /* Intel 82371AB */ 94 case 0x719b8086: /* Intel 82443MX */ 95#if 0 96 /* Not a good idea yet, this stops isab0 functioning */ 97 case 0x02001166: /* ServerWorks OSB4 */ 98#endif 99 device_set_desc(dev, "Intel PIIX4 SMBUS Interface"); 100 break; 101 case 0x43851002: 102 device_set_desc(dev, "AMD SB600/700/710/750 SMBus Controller"); 103 /* XXX Maybe force polling right here? */ 104 break; 105 default: 106 return (ENXIO); 107 } 108 109 return (BUS_PROBE_DEFAULT); 110} 111 112static int 113intsmb_attach(device_t dev) 114{ 115 struct intsmb_softc *sc = device_get_softc(dev); 116 int error, rid, value; 117 int intr; 118 char *str; 119 120 sc->dev = dev; 121 122 mtx_init(&sc->lock, device_get_nameunit(dev), "intsmb", MTX_DEF); 123 124 sc->cfg_irq9 = 0; 125#ifndef NO_CHANGE_PCICONF 126 switch (pci_get_devid(dev)) { 127 case 0x71138086: /* Intel 82371AB */ 128 case 0x719b8086: /* Intel 82443MX */ 129 /* Changing configuration is allowed. */ 130 sc->cfg_irq9 = 1; 131 break; 132 } 133#endif 134 135 rid = PCI_BASE_ADDR_SMB; 136 sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 137 RF_ACTIVE); 138 if (sc->io_res == NULL) { 139 device_printf(dev, "Could not allocate I/O space\n"); 140 error = ENXIO; 141 goto fail; 142 } 143 144 if (sc->cfg_irq9) { 145 pci_write_config(dev, PCIR_INTLINE, 0x9, 1); 146 pci_write_config(dev, PCI_HST_CFG_SMB, 147 PCI_INTR_SMB_IRQ9 | PCI_INTR_SMB_ENABLE, 1); 148 } 149 value = pci_read_config(dev, PCI_HST_CFG_SMB, 1); 150 sc->poll = (value & PCI_INTR_SMB_ENABLE) == 0; 151 intr = value & PCI_INTR_SMB_MASK; 152 switch (intr) { 153 case PCI_INTR_SMB_SMI: 154 str = "SMI"; 155 break; 156 case PCI_INTR_SMB_IRQ9: 157 str = "IRQ 9"; 158 break; 159 case PCI_INTR_SMB_IRQ_PCI: 160 str = "PCI IRQ"; 161 break; 162 default: 163 str = "BOGUS"; 164 } 165 166 device_printf(dev, "intr %s %s ", str, 167 sc->poll == 0 ? "enabled" : "disabled"); 168 printf("revision %d\n", pci_read_config(dev, PCI_REVID_SMB, 1)); 169 170 if (!sc->poll && intr == PCI_INTR_SMB_SMI) { 171 device_printf(dev, 172 "using polling mode when configured interrupt is SMI\n"); 173 sc->poll = 1; 174 } 175 176 if (sc->poll) 177 goto no_intr; 178 179 if (intr != PCI_INTR_SMB_IRQ9 && intr != PCI_INTR_SMB_IRQ_PCI) { 180 device_printf(dev, "Unsupported interrupt mode\n"); 181 error = ENXIO; 182 goto fail; 183 } 184 185 /* Force IRQ 9. */ 186 rid = 0; 187 if (sc->cfg_irq9) 188 bus_set_resource(dev, SYS_RES_IRQ, rid, 9, 1); 189 190 sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 191 RF_SHAREABLE | RF_ACTIVE); 192 if (sc->irq_res == NULL) { 193 device_printf(dev, "Could not allocate irq\n"); 194 error = ENXIO; 195 goto fail; 196 } 197 198 error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 199 NULL, intsmb_rawintr, sc, &sc->irq_hand); 200 if (error) { 201 device_printf(dev, "Failed to map intr\n"); 202 goto fail; 203 } 204 205no_intr: 206 sc->isbusy = 0; 207 sc->smbus = device_add_child(dev, "smbus", -1); 208 if (sc->smbus == NULL) { 209 error = ENXIO; 210 goto fail; 211 } 212 error = device_probe_and_attach(sc->smbus); 213 if (error) 214 goto fail; 215 216#ifdef ENABLE_ALART 217 /* Enable Arart */ 218 bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN); 219#endif 220 return (0); 221 222fail: 223 intsmb_detach(dev); 224 return (error); 225} 226 227static int 228intsmb_detach(device_t dev) 229{ 230 struct intsmb_softc *sc = device_get_softc(dev); 231 int error; 232 233 error = bus_generic_detach(dev); 234 if (error) 235 return (error); 236 237 if (sc->smbus) 238 device_delete_child(dev, sc->smbus); 239 if (sc->irq_hand) 240 bus_teardown_intr(dev, sc->irq_res, sc->irq_hand); 241 if (sc->irq_res) 242 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 243 if (sc->io_res) 244 bus_release_resource(dev, SYS_RES_IOPORT, PCI_BASE_ADDR_SMB, 245 sc->io_res); 246 mtx_destroy(&sc->lock); 247 return (0); 248} 249 250static void 251intsmb_rawintr(void *arg) 252{ 253 struct intsmb_softc *sc = arg; 254 255 INTSMB_LOCK(sc); 256 intsmb_intr(sc); 257 intsmb_slvintr(sc); 258 INTSMB_UNLOCK(sc); 259} 260 261static int 262intsmb_callback(device_t dev, int index, void *data) 263{ 264 int error = 0; 265 266 switch (index) { 267 case SMB_REQUEST_BUS: 268 break; 269 case SMB_RELEASE_BUS: 270 break; 271 default: 272 error = EINVAL; 273 } 274 275 return (error); 276} 277 278/* Counterpart of smbtx_smb_free(). */ 279static int 280intsmb_free(struct intsmb_softc *sc) 281{ 282 283 INTSMB_LOCK_ASSERT(sc); 284 if ((bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & PIIX4_SMBHSTSTAT_BUSY) || 285#ifdef ENABLE_ALART 286 (bus_read_1(sc->io_res, PIIX4_SMBSLVSTS) & PIIX4_SMBSLVSTS_BUSY) || 287#endif 288 sc->isbusy) 289 return (SMB_EBUSY); 290 291 sc->isbusy = 1; 292 /* Disable Interrupt in slave part. */ 293#ifndef ENABLE_ALART 294 bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 0); 295#endif 296 /* Reset INTR Flag to prepare INTR. */ 297 bus_write_1(sc->io_res, PIIX4_SMBHSTSTS, 298 PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR | 299 PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL); 300 return (0); 301} 302 303static int 304intsmb_intr(struct intsmb_softc *sc) 305{ 306 int status, tmp; 307 308 status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 309 if (status & PIIX4_SMBHSTSTAT_BUSY) 310 return (1); 311 312 if (status & (PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR | 313 PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL)) { 314 315 tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 316 bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, 317 tmp & ~PIIX4_SMBHSTCNT_INTREN); 318 if (sc->isbusy) { 319 sc->isbusy = 0; 320 wakeup(sc); 321 } 322 return (0); 323 } 324 return (1); /* Not Completed */ 325} 326 327static int 328intsmb_slvintr(struct intsmb_softc *sc) 329{ 330 int status; 331 332 status = bus_read_1(sc->io_res, PIIX4_SMBSLVSTS); 333 if (status & PIIX4_SMBSLVSTS_BUSY) 334 return (1); 335 if (status & PIIX4_SMBSLVSTS_ALART) 336 intsmb_alrintr(sc); 337 else if (status & ~(PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 338 | PIIX4_SMBSLVSTS_SDW1)) { 339 } 340 341 /* Reset Status Register */ 342 bus_write_1(sc->io_res, PIIX4_SMBSLVSTS, 343 PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 | 344 PIIX4_SMBSLVSTS_SDW1 | PIIX4_SMBSLVSTS_SLV); 345 return (0); 346} 347 348static void 349intsmb_alrintr(struct intsmb_softc *sc) 350{ 351 int slvcnt; 352#ifdef ENABLE_ALART 353 int error; 354 uint8_t addr; 355#endif 356 357 /* Stop generating INTR from ALART. */ 358 slvcnt = bus_read_1(sc->io_res, PIIX4_SMBSLVCNT); 359#ifdef ENABLE_ALART 360 bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 361 slvcnt & ~PIIX4_SMBSLVCNT_ALTEN); 362#endif 363 DELAY(5); 364 365 /* Ask bus who asserted it and then ask it what's the matter. */ 366#ifdef ENABLE_ALART 367 error = intsmb_free(sc); 368 if (error) 369 return; 370 371 bus_write_1(sc->io_res, PIIX4_SMBHSTADD, SMBALTRESP | LSB); 372 intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 1); 373 error = intsmb_stop_poll(sc); 374 if (error) 375 device_printf(sc->dev, "ALART: ERROR\n"); 376 else { 377 addr = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 378 device_printf(sc->dev, "ALART_RESPONSE: 0x%x\n", addr); 379 } 380 381 /* Re-enable INTR from ALART. */ 382 bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 383 slvcnt | PIIX4_SMBSLVCNT_ALTEN); 384 DELAY(5); 385#endif 386} 387 388static void 389intsmb_start(struct intsmb_softc *sc, unsigned char cmd, int nointr) 390{ 391 unsigned char tmp; 392 393 INTSMB_LOCK_ASSERT(sc); 394 tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 395 tmp &= 0xe0; 396 tmp |= cmd; 397 tmp |= PIIX4_SMBHSTCNT_START; 398 399 /* While not in autoconfiguration enable interrupts. */ 400 if (!sc->poll && !cold && !nointr) 401 tmp |= PIIX4_SMBHSTCNT_INTREN; 402 bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp); 403} 404 405static int 406intsmb_error(device_t dev, int status) 407{ 408 int error = 0; 409 410 if (status & PIIX4_SMBHSTSTAT_ERR) 411 error |= SMB_EBUSERR; 412 if (status & PIIX4_SMBHSTSTAT_BUSC) 413 error |= SMB_ECOLLI; 414 if (status & PIIX4_SMBHSTSTAT_FAIL) 415 error |= SMB_ENOACK; 416 417 if (error != 0 && bootverbose) 418 device_printf(dev, "error = %d, status = %#x\n", error, status); 419 420 return (error); 421} 422 423/* 424 * Polling Code. 425 * 426 * Polling is not encouraged because it requires waiting for the 427 * device if it is busy. 428 * (29063505.pdf from Intel) But during boot, interrupt cannot be used, so use 429 * polling code then. 430 */ 431static int 432intsmb_stop_poll(struct intsmb_softc *sc) 433{ 434 int error, i, status, tmp; 435 436 INTSMB_LOCK_ASSERT(sc); 437 438 /* First, wait for busy to be set. */ 439 for (i = 0; i < 0x7fff; i++) 440 if (bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & 441 PIIX4_SMBHSTSTAT_BUSY) 442 break; 443 444 /* Wait for busy to clear. */ 445 for (i = 0; i < 0x7fff; i++) { 446 status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 447 if (!(status & PIIX4_SMBHSTSTAT_BUSY)) { 448 sc->isbusy = 0; 449 error = intsmb_error(sc->dev, status); 450 return (error); 451 } 452 } 453 454 /* Timed out waiting for busy to clear. */ 455 sc->isbusy = 0; 456 tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 457 bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp & ~PIIX4_SMBHSTCNT_INTREN); 458 return (SMB_ETIMEOUT); 459} 460 461/* 462 * Wait for completion and return result. 463 */ 464static int 465intsmb_stop(struct intsmb_softc *sc) 466{ 467 int error, status; 468 469 INTSMB_LOCK_ASSERT(sc); 470 471 if (sc->poll || cold) 472 /* So that it can use device during device probe on SMBus. */ 473 return (intsmb_stop_poll(sc)); 474 475 error = msleep(sc, &sc->lock, PWAIT | PCATCH, "SMBWAI", hz / 8); 476 if (error == 0) { 477 status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 478 if (!(status & PIIX4_SMBHSTSTAT_BUSY)) { 479 error = intsmb_error(sc->dev, status); 480 if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR)) 481 device_printf(sc->dev, "unknown cause why?\n"); 482#ifdef ENABLE_ALART 483 bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 484 PIIX4_SMBSLVCNT_ALTEN); 485#endif 486 return (error); 487 } 488 } 489 490 /* Timeout Procedure. */ 491 sc->isbusy = 0; 492 493 /* Re-enable supressed interrupt from slave part. */ 494 bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN); 495 if (error == EWOULDBLOCK) 496 return (SMB_ETIMEOUT); 497 else 498 return (SMB_EABORT); 499} 500 501static int 502intsmb_quick(device_t dev, u_char slave, int how) 503{ 504 struct intsmb_softc *sc = device_get_softc(dev); 505 int error; 506 u_char data; 507 508 data = slave; 509 510 /* Quick command is part of Address, I think. */ 511 switch(how) { 512 case SMB_QWRITE: 513 data &= ~LSB; 514 break; 515 case SMB_QREAD: 516 data |= LSB; 517 break; 518 default: 519 return (EINVAL); 520 } 521 522 INTSMB_LOCK(sc); 523 error = intsmb_free(sc); 524 if (error) { 525 INTSMB_UNLOCK(sc); 526 return (error); 527 } 528 bus_write_1(sc->io_res, PIIX4_SMBHSTADD, data); 529 intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_QUICK, 0); 530 error = intsmb_stop(sc); 531 INTSMB_UNLOCK(sc); 532 return (error); 533} 534 535static int 536intsmb_sendb(device_t dev, u_char slave, char byte) 537{ 538 struct intsmb_softc *sc = device_get_softc(dev); 539 int error; 540 541 INTSMB_LOCK(sc); 542 error = intsmb_free(sc); 543 if (error) { 544 INTSMB_UNLOCK(sc); 545 return (error); 546 } 547 bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 548 bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, byte); 549 intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0); 550 error = intsmb_stop(sc); 551 INTSMB_UNLOCK(sc); 552 return (error); 553} 554 555static int 556intsmb_recvb(device_t dev, u_char slave, char *byte) 557{ 558 struct intsmb_softc *sc = device_get_softc(dev); 559 int error; 560 561 INTSMB_LOCK(sc); 562 error = intsmb_free(sc); 563 if (error) { 564 INTSMB_UNLOCK(sc); 565 return (error); 566 } 567 bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 568 intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0); 569 error = intsmb_stop(sc); 570 if (error == 0) { 571#ifdef RECV_IS_IN_CMD 572 /* 573 * Linux SMBus stuff also troubles 574 * Because Intel's datasheet does not make clear. 575 */ 576 *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTCMD); 577#else 578 *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 579#endif 580 } 581 INTSMB_UNLOCK(sc); 582 return (error); 583} 584 585static int 586intsmb_writeb(device_t dev, u_char slave, char cmd, char byte) 587{ 588 struct intsmb_softc *sc = device_get_softc(dev); 589 int error; 590 591 INTSMB_LOCK(sc); 592 error = intsmb_free(sc); 593 if (error) { 594 INTSMB_UNLOCK(sc); 595 return (error); 596 } 597 bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 598 bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 599 bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, byte); 600 intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0); 601 error = intsmb_stop(sc); 602 INTSMB_UNLOCK(sc); 603 return (error); 604} 605 606static int 607intsmb_writew(device_t dev, u_char slave, char cmd, short word) 608{ 609 struct intsmb_softc *sc = device_get_softc(dev); 610 int error; 611 612 INTSMB_LOCK(sc); 613 error = intsmb_free(sc); 614 if (error) { 615 INTSMB_UNLOCK(sc); 616 return (error); 617 } 618 bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 619 bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 620 bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, word & 0xff); 621 bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (word >> 8) & 0xff); 622 intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 623 error = intsmb_stop(sc); 624 INTSMB_UNLOCK(sc); 625 return (error); 626} 627 628static int 629intsmb_readb(device_t dev, u_char slave, char cmd, char *byte) 630{ 631 struct intsmb_softc *sc = device_get_softc(dev); 632 int error; 633 634 INTSMB_LOCK(sc); 635 error = intsmb_free(sc); 636 if (error) { 637 INTSMB_UNLOCK(sc); 638 return (error); 639 } 640 bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 641 bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 642 intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0); 643 error = intsmb_stop(sc); 644 if (error == 0) 645 *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 646 INTSMB_UNLOCK(sc); 647 return (error); 648} 649 650static int 651intsmb_readw(device_t dev, u_char slave, char cmd, short *word) 652{ 653 struct intsmb_softc *sc = device_get_softc(dev); 654 int error; 655 656 INTSMB_LOCK(sc); 657 error = intsmb_free(sc); 658 if (error) { 659 INTSMB_UNLOCK(sc); 660 return (error); 661 } 662 bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 663 bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 664 intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 665 error = intsmb_stop(sc); 666 if (error == 0) { 667 *word = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 668 *word |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8; 669 } 670 INTSMB_UNLOCK(sc); 671 return (error); 672} 673 674/* 675 * Data sheet claims that it implements all function, but also claims 676 * that it implements 7 function and not mention PCALL. So I don't know 677 * whether it will work. 678 */ 679static int 680intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata) 681{ 682#ifdef PROCCALL_TEST 683 struct intsmb_softc *sc = device_get_softc(dev); 684 int error; 685 686 INTSMB_LOCK(sc); 687 error = intsmb_free(sc); 688 if (error) { 689 INTSMB_UNLOCK(sc); 690 return (error); 691 } 692 bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 693 bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 694 bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, sdata & 0xff); 695 bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (sdata & 0xff) >> 8); 696 intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 697 error = intsmb_stop(sc); 698 if (error == 0) { 699 *rdata = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 700 *rdata |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8; 701 } 702 INTSMB_UNLOCK(sc); 703 return (error); 704#else 705 return (SMB_ENOTSUPP); 706#endif 707} 708 709static int 710intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) 711{ 712 struct intsmb_softc *sc = device_get_softc(dev); 713 int error, i; 714 715 if (count > SMBBLOCKTRANS_MAX || count == 0) 716 return (SMB_EINVAL); 717 718 INTSMB_LOCK(sc); 719 error = intsmb_free(sc); 720 if (error) { 721 INTSMB_UNLOCK(sc); 722 return (error); 723 } 724 725 /* Reset internal array index. */ 726 bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 727 728 bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 729 bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 730 for (i = 0; i < count; i++) 731 bus_write_1(sc->io_res, PIIX4_SMBBLKDAT, buf[i]); 732 bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, count); 733 intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0); 734 error = intsmb_stop(sc); 735 INTSMB_UNLOCK(sc); 736 return (error); 737} 738 739static int 740intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf) 741{ 742 struct intsmb_softc *sc = device_get_softc(dev); 743 int error, i; 744 u_char data, nread; 745 746 if (*count > SMBBLOCKTRANS_MAX || *count == 0) 747 return (SMB_EINVAL); 748 749 INTSMB_LOCK(sc); 750 error = intsmb_free(sc); 751 if (error) { 752 INTSMB_UNLOCK(sc); 753 return (error); 754 } 755 756 /* Reset internal array index. */ 757 bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 758 759 bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 760 bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 761 bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, *count); 762 intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0); 763 error = intsmb_stop(sc); 764 if (error == 0) { 765 nread = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 766 if (nread != 0 && nread <= SMBBLOCKTRANS_MAX) { 767 for (i = 0; i < nread; i++) { 768 data = bus_read_1(sc->io_res, PIIX4_SMBBLKDAT); 769 if (i < *count) 770 buf[i] = data; 771 } 772 *count = nread; 773 } else 774 error = EIO; 775 } 776 INTSMB_UNLOCK(sc); 777 return (error); 778} 779 780static devclass_t intsmb_devclass; 781 782static device_method_t intsmb_methods[] = { 783 /* Device interface */ 784 DEVMETHOD(device_probe, intsmb_probe), 785 DEVMETHOD(device_attach, intsmb_attach), 786 DEVMETHOD(device_detach, intsmb_detach), 787 788 /* SMBus interface */ 789 DEVMETHOD(smbus_callback, intsmb_callback), 790 DEVMETHOD(smbus_quick, intsmb_quick), 791 DEVMETHOD(smbus_sendb, intsmb_sendb), 792 DEVMETHOD(smbus_recvb, intsmb_recvb), 793 DEVMETHOD(smbus_writeb, intsmb_writeb), 794 DEVMETHOD(smbus_writew, intsmb_writew), 795 DEVMETHOD(smbus_readb, intsmb_readb), 796 DEVMETHOD(smbus_readw, intsmb_readw), 797 DEVMETHOD(smbus_pcall, intsmb_pcall), 798 DEVMETHOD(smbus_bwrite, intsmb_bwrite), 799 DEVMETHOD(smbus_bread, intsmb_bread), 800 801 DEVMETHOD_END 802}; 803 804static driver_t intsmb_driver = { 805 "intsmb", 806 intsmb_methods, 807 sizeof(struct intsmb_softc), 808}; 809 810DRIVER_MODULE(intsmb, pci, intsmb_driver, intsmb_devclass, 0, 0); 811DRIVER_MODULE(smbus, intsmb, smbus_driver, smbus_devclass, 0, 0); 812MODULE_DEPEND(intsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 813MODULE_VERSION(intsmb, 1); 814