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