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