intpm.c revision 168870
1169689Skan/*- 2169689Skan * Copyright (c) 1998, 1999 Takanori Watanabe 3169689Skan * All rights reserved. 4169689Skan * 5169689Skan * Redistribution and use in source and binary forms, with or without 6169689Skan * modification, are permitted provided that the following conditions 7169689Skan * are met: 8169689Skan * 1. Redistributions of source code must retain the above copyright 9169689Skan * notice, this list of conditions and the following disclaimer. 10169689Skan * 2. Redistributions in binary form must reproduce the above copyright 11169689Skan * notice, this list of conditions and the following disclaimer in the 12169689Skan * documentation and/or other materials provided with the distribution. 13169689Skan * 14169689Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15169689Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16169689Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17169689Skan * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18169689Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19169689Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20169689Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21169689Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22169689Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23169689Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24169689Skan * SUCH DAMAGE. 25169689Skan */ 26169689Skan 27169689Skan#include <sys/cdefs.h> 28169689Skan__FBSDID("$FreeBSD: head/sys/pci/intpm.c 168870 2007-04-19 17:14:06Z jhb $"); 29169689Skan 30169689Skan#include <sys/param.h> 31169689Skan#include <sys/systm.h> 32169689Skan#include <sys/bus.h> 33169689Skan#include <sys/kernel.h> 34169689Skan#include <sys/lock.h> 35169689Skan#include <sys/module.h> 36169689Skan#include <sys/mutex.h> 37169689Skan#include <sys/rman.h> 38169689Skan#include <machine/bus.h> 39169689Skan#include <dev/smbus/smbconf.h> 40169689Skan 41169689Skan#include "smbus_if.h" 42169689Skan 43169689Skan#include <dev/pci/pcireg.h> 44169689Skan#include <dev/pci/pcivar.h> 45169689Skan#include <pci/intpmreg.h> 46169689Skan 47169689Skan#include "opt_intpm.h" 48169689Skan 49169689Skanstruct intsmb_softc { 50169689Skan device_t dev; 51169689Skan struct resource *io_res; 52169689Skan struct resource *irq_res; 53169689Skan void *irq_hand; 54169689Skan device_t smbus; 55169689Skan int isbusy; 56169689Skan struct mtx lock; 57169689Skan}; 58169689Skan 59169689Skan#define INTSMB_LOCK(sc) mtx_lock(&(sc)->lock) 60169689Skan#define INTSMB_UNLOCK(sc) mtx_unlock(&(sc)->lock) 61169689Skan#define INTSMB_LOCK_ASSERT(sc) mtx_assert(&(sc)->lock, MA_OWNED) 62169689Skan 63169689Skanstatic int intsmb_probe(device_t); 64169689Skanstatic int intsmb_attach(device_t); 65169689Skanstatic int intsmb_detach(device_t); 66169689Skanstatic int intsmb_intr(struct intsmb_softc *sc); 67169689Skanstatic int intsmb_slvintr(struct intsmb_softc *sc); 68169689Skanstatic void intsmb_alrintr(struct intsmb_softc *sc); 69169689Skanstatic int intsmb_callback(device_t dev, int index, void *data); 70169689Skanstatic int intsmb_quick(device_t dev, u_char slave, int how); 71169689Skanstatic int intsmb_sendb(device_t dev, u_char slave, char byte); 72169689Skanstatic int intsmb_recvb(device_t dev, u_char slave, char *byte); 73169689Skanstatic int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte); 74169689Skanstatic int intsmb_writew(device_t dev, u_char slave, char cmd, short word); 75169689Skanstatic int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte); 76169689Skanstatic int intsmb_readw(device_t dev, u_char slave, char cmd, short *word); 77169689Skanstatic int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata); 78169689Skanstatic int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf); 79169689Skanstatic int intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf); 80169689Skanstatic void intsmb_start(struct intsmb_softc *sc, u_char cmd, int nointr); 81169689Skanstatic int intsmb_stop(struct intsmb_softc *sc); 82169689Skanstatic int intsmb_stop_poll(struct intsmb_softc *sc); 83169689Skanstatic int intsmb_free(struct intsmb_softc *sc); 84169689Skanstatic void intsmb_rawintr(void *arg); 85169689Skan 86169689Skanstatic int 87169689Skanintsmb_probe(device_t dev) 88169689Skan{ 89169689Skan 90169689Skan switch (pci_get_devid(dev)) { 91169689Skan case 0x71138086: /* Intel 82371AB */ 92169689Skan case 0x719b8086: /* Intel 82443MX */ 93169689Skan#if 0 94169689Skan /* Not a good idea yet, this stops isab0 functioning */ 95169689Skan case 0x02001166: /* ServerWorks OSB4 */ 96169689Skan#endif 97169689Skan device_set_desc(dev, "Intel PIIX4 SMBUS Interface"); 98169689Skan break; 99169689Skan default: 100169689Skan return (ENXIO); 101169689Skan } 102169689Skan 103169689Skan return (BUS_PROBE_DEFAULT); 104169689Skan} 105169689Skan 106169689Skanstatic int 107169689Skanintsmb_attach(device_t dev) 108169689Skan{ 109169689Skan struct intsmb_softc *sc = device_get_softc(dev); 110169689Skan int error, rid, value; 111169689Skan char *str; 112169689Skan 113169689Skan mtx_init(&sc->lock, device_get_nameunit(dev), "intsmb", MTX_DEF); 114169689Skan 115169689Skan rid = PCI_BASE_ADDR_SMB; 116169689Skan sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 117169689Skan RF_ACTIVE); 118169689Skan if (sc->io_res == NULL) { 119169689Skan device_printf(dev, "Could not allocate I/O space\n"); 120169689Skan error = ENXIO; 121169689Skan goto fail; 122169689Skan } 123169689Skan 124169689Skan#ifndef NO_CHANGE_PCICONF 125169689Skan pci_write_config(dev, PCIR_INTLINE, 0x9, 1); 126169689Skan pci_write_config(dev, PCI_HST_CFG_SMB, 127169689Skan PCI_INTR_SMB_IRQ9 | PCI_INTR_SMB_ENABLE, 1); 128169689Skan#endif 129169689Skan value = pci_read_config(dev, PCI_HST_CFG_SMB, 1); 130169689Skan switch (value & 0xe) { 131169689Skan case PCI_INTR_SMB_SMI: 132169689Skan str = "SMI"; 133169689Skan break; 134169689Skan case PCI_INTR_SMB_IRQ9: 135169689Skan str = "IRQ 9"; 136169689Skan break; 137169689Skan default: 138169689Skan str = "BOGUS"; 139169689Skan } 140169689Skan device_printf(dev, "intr %s %s ", str, 141169689Skan (value & 1) ? "enabled" : "disabled"); 142169689Skan printf("revision %d\n", pci_read_config(dev, PCI_REVID_SMB, 1)); 143169689Skan 144169689Skan if ((value & 0xe) != PCI_INTR_SMB_IRQ9) { 145169689Skan device_printf(dev, "Unsupported interrupt mode\n"); 146169689Skan error = ENXIO; 147169689Skan goto fail; 148169689Skan } 149169689Skan 150169689Skan /* Force IRQ 9. */ 151169689Skan rid = 0; 152169689Skan bus_set_resource(dev, SYS_RES_IRQ, rid, 9, 1); 153169689Skan sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 154169689Skan RF_SHAREABLE | RF_ACTIVE); 155169689Skan if (sc->irq_res == NULL) { 156169689Skan device_printf(dev, "Could not allocate irq\n"); 157169689Skan error = ENXIO; 158169689Skan goto fail; 159169689Skan } 160169689Skan 161169689Skan error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC, NULL, 162169689Skan intsmb_rawintr, sc, &sc->irq_hand); 163169689Skan if (error) { 164169689Skan device_printf(dev, "Failed to map intr\n"); 165169689Skan goto fail; 166169689Skan } 167169689Skan 168169689Skan value = pci_read_config(dev, PCI_BASE_ADDR_PM, 4); 169169689Skan device_printf(dev, "PM %s %x\n", (value & 1) ? "I/O mapped" : "Memory", 170169689Skan value & 0xfffe); 171169689Skan 172169689Skan sc->isbusy = 0; 173169689Skan sc->smbus = device_add_child(dev, "smbus", -1); 174169689Skan if (sc->smbus == NULL) { 175169689Skan error = ENXIO; 176169689Skan goto fail; 177169689Skan } 178169689Skan 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