intpm.c revision 234043
187866Ssheldonh/*- 287866Ssheldonh * Copyright (c) 1998, 1999 Takanori Watanabe 387866Ssheldonh * All rights reserved. 487866Ssheldonh * 587866Ssheldonh * Redistribution and use in source and binary forms, with or without 687866Ssheldonh * modification, are permitted provided that the following conditions 787866Ssheldonh * are met: 887866Ssheldonh * 1. Redistributions of source code must retain the above copyright 987866Ssheldonh * notice, this list of conditions and the following disclaimer. 1087866Ssheldonh * 2. Redistributions in binary form must reproduce the above copyright 1187866Ssheldonh * notice, this list of conditions and the following disclaimer in the 1287866Ssheldonh * documentation and/or other materials provided with the distribution. 1387866Ssheldonh * 1487866Ssheldonh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1587866Ssheldonh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1687866Ssheldonh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1787866Ssheldonh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1887866Ssheldonh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1987866Ssheldonh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2087866Ssheldonh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2187866Ssheldonh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2287866Ssheldonh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2387866Ssheldonh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2487866Ssheldonh * SUCH DAMAGE. 2587866Ssheldonh */ 2687866Ssheldonh 2787866Ssheldonh#include <sys/cdefs.h> 2887866Ssheldonh__FBSDID("$FreeBSD: head/sys/pci/intpm.c 234043 2012-04-08 20:48:39Z avg $"); 2987866Ssheldonh 3087866Ssheldonh#include <sys/param.h> 3187866Ssheldonh#include <sys/systm.h> 3287866Ssheldonh#include <sys/bus.h> 33150802Sbp#include <sys/kernel.h> 3487866Ssheldonh#include <sys/lock.h> 3587866Ssheldonh#include <sys/module.h> 3687866Ssheldonh#include <sys/mutex.h> 3787866Ssheldonh#include <sys/rman.h> 3887866Ssheldonh#include <machine/bus.h> 3987866Ssheldonh#include <dev/smbus/smbconf.h> 4087866Ssheldonh 4187866Ssheldonh#include "smbus_if.h" 4287866Ssheldonh 4387866Ssheldonh#include <dev/pci/pcireg.h> 4487866Ssheldonh#include <dev/pci/pcivar.h> 4587866Ssheldonh#include <pci/intpmreg.h> 4687866Ssheldonh 4787866Ssheldonh#include "opt_intpm.h" 4887866Ssheldonh 4987866Ssheldonhstruct intsmb_softc { 5087866Ssheldonh device_t dev; 5187866Ssheldonh struct resource *io_res; 5287866Ssheldonh struct resource *irq_res; 5387866Ssheldonh void *irq_hand; 5487866Ssheldonh device_t smbus; 5587866Ssheldonh int isbusy; 5687866Ssheldonh int cfg_irq9; 5787866Ssheldonh int poll; 5887866Ssheldonh struct mtx lock; 5987866Ssheldonh}; 6087866Ssheldonh 61150802Sbp#define INTSMB_LOCK(sc) mtx_lock(&(sc)->lock) 62150802Sbp#define INTSMB_UNLOCK(sc) mtx_unlock(&(sc)->lock) 63150802Sbp#define INTSMB_LOCK_ASSERT(sc) mtx_assert(&(sc)->lock, MA_OWNED) 6487866Ssheldonh 6587866Ssheldonhstatic int intsmb_probe(device_t); 6687866Ssheldonhstatic int intsmb_attach(device_t); 6787866Ssheldonhstatic int intsmb_detach(device_t); 6887866Ssheldonhstatic int intsmb_intr(struct intsmb_softc *sc); 6987866Ssheldonhstatic int intsmb_slvintr(struct intsmb_softc *sc); 7087866Ssheldonhstatic void intsmb_alrintr(struct intsmb_softc *sc); 7187866Ssheldonhstatic int intsmb_callback(device_t dev, int index, void *data); 7287866Ssheldonhstatic int intsmb_quick(device_t dev, u_char slave, int how); 7387866Ssheldonhstatic int intsmb_sendb(device_t dev, u_char slave, char byte); 7487866Ssheldonhstatic int intsmb_recvb(device_t dev, u_char slave, char *byte); 7587866Ssheldonhstatic int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte); 7687866Ssheldonhstatic int intsmb_writew(device_t dev, u_char slave, char cmd, short word); 7787866Ssheldonhstatic int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte); 7887866Ssheldonhstatic int intsmb_readw(device_t dev, u_char slave, char cmd, short *word); 7987866Ssheldonhstatic int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata); 8087866Ssheldonhstatic int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf); 8187866Ssheldonhstatic int intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf); 8287866Ssheldonhstatic void intsmb_start(struct intsmb_softc *sc, u_char cmd, int nointr); 8387866Ssheldonhstatic int intsmb_stop(struct intsmb_softc *sc); 8487866Ssheldonhstatic int intsmb_stop_poll(struct intsmb_softc *sc); 8587866Ssheldonhstatic int intsmb_free(struct intsmb_softc *sc); 8687866Ssheldonhstatic void intsmb_rawintr(void *arg); 8787866Ssheldonh 8887866Ssheldonhstatic int 8987866Ssheldonhintsmb_probe(device_t dev) 9087866Ssheldonh{ 9187866Ssheldonh 9287866Ssheldonh switch (pci_get_devid(dev)) { 9387866Ssheldonh case 0x71138086: /* Intel 82371AB */ 9487866Ssheldonh case 0x719b8086: /* Intel 82443MX */ 9587866Ssheldonh#if 0 9687866Ssheldonh /* Not a good idea yet, this stops isab0 functioning */ 9787866Ssheldonh case 0x02001166: /* ServerWorks OSB4 */ 9887866Ssheldonh#endif 9987866Ssheldonh device_set_desc(dev, "Intel PIIX4 SMBUS Interface"); 10087866Ssheldonh break; 10187866Ssheldonh case 0x43851002: 10287866Ssheldonh /* SB800 and newer can not be configured in a compatible way. */ 10387866Ssheldonh if (pci_get_revid(dev) >= 0x40) 10487866Ssheldonh return (ENXIO); 10587866Ssheldonh device_set_desc(dev, "AMD SB600/700/710/750 SMBus Controller"); 10687866Ssheldonh /* XXX Maybe force polling right here? */ 10787866Ssheldonh break; 10887866Ssheldonh default: 10987866Ssheldonh return (ENXIO); 11087866Ssheldonh } 11187866Ssheldonh 11287866Ssheldonh return (BUS_PROBE_DEFAULT); 11387866Ssheldonh} 11487866Ssheldonh 11587866Ssheldonhstatic int 11687866Ssheldonhintsmb_attach(device_t dev) 11787866Ssheldonh{ 118150802Sbp struct intsmb_softc *sc = device_get_softc(dev); 11987866Ssheldonh int error, rid, value; 12087866Ssheldonh int intr; 12187866Ssheldonh char *str; 12287866Ssheldonh 12387866Ssheldonh sc->dev = dev; 12487866Ssheldonh 12587866Ssheldonh mtx_init(&sc->lock, device_get_nameunit(dev), "intsmb", MTX_DEF); 12687866Ssheldonh 12787866Ssheldonh sc->cfg_irq9 = 0; 12887866Ssheldonh#ifndef NO_CHANGE_PCICONF 12987866Ssheldonh switch (pci_get_devid(dev)) { 130150802Sbp case 0x71138086: /* Intel 82371AB */ 13187866Ssheldonh case 0x719b8086: /* Intel 82443MX */ 13287866Ssheldonh /* Changing configuration is allowed. */ 13387866Ssheldonh sc->cfg_irq9 = 1; 13487866Ssheldonh break; 13587866Ssheldonh } 13687866Ssheldonh#endif 13787866Ssheldonh 13887866Ssheldonh rid = PCI_BASE_ADDR_SMB; 13987866Ssheldonh sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 14087866Ssheldonh RF_ACTIVE); 14187866Ssheldonh if (sc->io_res == NULL) { 14287866Ssheldonh device_printf(dev, "Could not allocate I/O space\n"); 14387866Ssheldonh error = ENXIO; 14487866Ssheldonh goto fail; 14587866Ssheldonh } 14687866Ssheldonh 14787866Ssheldonh if (sc->cfg_irq9) { 14887866Ssheldonh pci_write_config(dev, PCIR_INTLINE, 0x9, 1); 14987866Ssheldonh pci_write_config(dev, PCI_HST_CFG_SMB, 15087866Ssheldonh PCI_INTR_SMB_IRQ9 | PCI_INTR_SMB_ENABLE, 1); 15187866Ssheldonh } 15287866Ssheldonh value = pci_read_config(dev, PCI_HST_CFG_SMB, 1); 15387866Ssheldonh sc->poll = (value & PCI_INTR_SMB_ENABLE) == 0; 15487866Ssheldonh intr = value & PCI_INTR_SMB_MASK; 15587866Ssheldonh switch (intr) { 15687866Ssheldonh case PCI_INTR_SMB_SMI: 15787866Ssheldonh str = "SMI"; 15887866Ssheldonh break; 15987866Ssheldonh case PCI_INTR_SMB_IRQ9: 16087866Ssheldonh str = "IRQ 9"; 16187866Ssheldonh break; 16287866Ssheldonh case PCI_INTR_SMB_IRQ_PCI: 16387866Ssheldonh str = "PCI IRQ"; 16487866Ssheldonh break; 16587866Ssheldonh default: 16687866Ssheldonh str = "BOGUS"; 16787866Ssheldonh } 16887866Ssheldonh 16987866Ssheldonh device_printf(dev, "intr %s %s ", str, 17087866Ssheldonh sc->poll == 0 ? "enabled" : "disabled"); 17187866Ssheldonh printf("revision %d\n", pci_read_config(dev, PCI_REVID_SMB, 1)); 17287866Ssheldonh 17387866Ssheldonh if (!sc->poll && intr == PCI_INTR_SMB_SMI) { 17487866Ssheldonh device_printf(dev, 17587866Ssheldonh "using polling mode when configured interrupt is SMI\n"); 17687866Ssheldonh sc->poll = 1; 17787866Ssheldonh } 17887866Ssheldonh 17987866Ssheldonh if (sc->poll) 18087866Ssheldonh goto no_intr; 18187866Ssheldonh 18287866Ssheldonh if (intr != PCI_INTR_SMB_IRQ9 && intr != PCI_INTR_SMB_IRQ_PCI) { 18387866Ssheldonh device_printf(dev, "Unsupported interrupt mode\n"); 18487866Ssheldonh error = ENXIO; 18587866Ssheldonh goto fail; 18687866Ssheldonh } 18787866Ssheldonh 18887866Ssheldonh /* Force IRQ 9. */ 18987866Ssheldonh rid = 0; 19087866Ssheldonh if (sc->cfg_irq9) 19187866Ssheldonh bus_set_resource(dev, SYS_RES_IRQ, rid, 9, 1); 19287866Ssheldonh 19387866Ssheldonh sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 19487866Ssheldonh RF_SHAREABLE | RF_ACTIVE); 19587866Ssheldonh if (sc->irq_res == NULL) { 196 device_printf(dev, "Could not allocate irq\n"); 197 error = ENXIO; 198 goto fail; 199 } 200 201 error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 202 NULL, intsmb_rawintr, sc, &sc->irq_hand); 203 if (error) { 204 device_printf(dev, "Failed to map intr\n"); 205 goto fail; 206 } 207 208no_intr: 209 sc->isbusy = 0; 210 sc->smbus = device_add_child(dev, "smbus", -1); 211 if (sc->smbus == NULL) { 212 error = ENXIO; 213 goto fail; 214 } 215 error = device_probe_and_attach(sc->smbus); 216 if (error) 217 goto fail; 218 219#ifdef ENABLE_ALART 220 /* Enable Arart */ 221 bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN); 222#endif 223 return (0); 224 225fail: 226 intsmb_detach(dev); 227 return (error); 228} 229 230static int 231intsmb_detach(device_t dev) 232{ 233 struct intsmb_softc *sc = device_get_softc(dev); 234 int error; 235 236 error = bus_generic_detach(dev); 237 if (error) 238 return (error); 239 240 if (sc->smbus) 241 device_delete_child(dev, sc->smbus); 242 if (sc->irq_hand) 243 bus_teardown_intr(dev, sc->irq_res, sc->irq_hand); 244 if (sc->irq_res) 245 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 246 if (sc->io_res) 247 bus_release_resource(dev, SYS_RES_IOPORT, PCI_BASE_ADDR_SMB, 248 sc->io_res); 249 mtx_destroy(&sc->lock); 250 return (0); 251} 252 253static void 254intsmb_rawintr(void *arg) 255{ 256 struct intsmb_softc *sc = arg; 257 258 INTSMB_LOCK(sc); 259 intsmb_intr(sc); 260 intsmb_slvintr(sc); 261 INTSMB_UNLOCK(sc); 262} 263 264static int 265intsmb_callback(device_t dev, int index, void *data) 266{ 267 int error = 0; 268 269 switch (index) { 270 case SMB_REQUEST_BUS: 271 break; 272 case SMB_RELEASE_BUS: 273 break; 274 default: 275 error = SMB_EINVAL; 276 } 277 278 return (error); 279} 280 281/* Counterpart of smbtx_smb_free(). */ 282static int 283intsmb_free(struct intsmb_softc *sc) 284{ 285 286 INTSMB_LOCK_ASSERT(sc); 287 if ((bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & PIIX4_SMBHSTSTAT_BUSY) || 288#ifdef ENABLE_ALART 289 (bus_read_1(sc->io_res, PIIX4_SMBSLVSTS) & PIIX4_SMBSLVSTS_BUSY) || 290#endif 291 sc->isbusy) 292 return (SMB_EBUSY); 293 294 sc->isbusy = 1; 295 /* Disable Interrupt in slave part. */ 296#ifndef ENABLE_ALART 297 bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 0); 298#endif 299 /* Reset INTR Flag to prepare INTR. */ 300 bus_write_1(sc->io_res, PIIX4_SMBHSTSTS, 301 PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR | 302 PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL); 303 return (0); 304} 305 306static int 307intsmb_intr(struct intsmb_softc *sc) 308{ 309 int status, tmp; 310 311 status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 312 if (status & PIIX4_SMBHSTSTAT_BUSY) 313 return (1); 314 315 if (status & (PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR | 316 PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL)) { 317 318 tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 319 bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, 320 tmp & ~PIIX4_SMBHSTCNT_INTREN); 321 if (sc->isbusy) { 322 sc->isbusy = 0; 323 wakeup(sc); 324 } 325 return (0); 326 } 327 return (1); /* Not Completed */ 328} 329 330static int 331intsmb_slvintr(struct intsmb_softc *sc) 332{ 333 int status; 334 335 status = bus_read_1(sc->io_res, PIIX4_SMBSLVSTS); 336 if (status & PIIX4_SMBSLVSTS_BUSY) 337 return (1); 338 if (status & PIIX4_SMBSLVSTS_ALART) 339 intsmb_alrintr(sc); 340 else if (status & ~(PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 341 | PIIX4_SMBSLVSTS_SDW1)) { 342 } 343 344 /* Reset Status Register */ 345 bus_write_1(sc->io_res, PIIX4_SMBSLVSTS, 346 PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 | 347 PIIX4_SMBSLVSTS_SDW1 | PIIX4_SMBSLVSTS_SLV); 348 return (0); 349} 350 351static void 352intsmb_alrintr(struct intsmb_softc *sc) 353{ 354 int slvcnt; 355#ifdef ENABLE_ALART 356 int error; 357 uint8_t addr; 358#endif 359 360 /* Stop generating INTR from ALART. */ 361 slvcnt = bus_read_1(sc->io_res, PIIX4_SMBSLVCNT); 362#ifdef ENABLE_ALART 363 bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 364 slvcnt & ~PIIX4_SMBSLVCNT_ALTEN); 365#endif 366 DELAY(5); 367 368 /* Ask bus who asserted it and then ask it what's the matter. */ 369#ifdef ENABLE_ALART 370 error = intsmb_free(sc); 371 if (error) 372 return; 373 374 bus_write_1(sc->io_res, PIIX4_SMBHSTADD, SMBALTRESP | LSB); 375 intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 1); 376 error = intsmb_stop_poll(sc); 377 if (error) 378 device_printf(sc->dev, "ALART: ERROR\n"); 379 else { 380 addr = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 381 device_printf(sc->dev, "ALART_RESPONSE: 0x%x\n", addr); 382 } 383 384 /* Re-enable INTR from ALART. */ 385 bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 386 slvcnt | PIIX4_SMBSLVCNT_ALTEN); 387 DELAY(5); 388#endif 389} 390 391static void 392intsmb_start(struct intsmb_softc *sc, unsigned char cmd, int nointr) 393{ 394 unsigned char tmp; 395 396 INTSMB_LOCK_ASSERT(sc); 397 tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 398 tmp &= 0xe0; 399 tmp |= cmd; 400 tmp |= PIIX4_SMBHSTCNT_START; 401 402 /* While not in autoconfiguration enable interrupts. */ 403 if (!sc->poll && !cold && !nointr) 404 tmp |= PIIX4_SMBHSTCNT_INTREN; 405 bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp); 406} 407 408static int 409intsmb_error(device_t dev, int status) 410{ 411 int error = 0; 412 413 if (status & PIIX4_SMBHSTSTAT_ERR) 414 error |= SMB_EBUSERR; 415 if (status & PIIX4_SMBHSTSTAT_BUSC) 416 error |= SMB_ECOLLI; 417 if (status & PIIX4_SMBHSTSTAT_FAIL) 418 error |= SMB_ENOACK; 419 420 if (error != 0 && bootverbose) 421 device_printf(dev, "error = %d, status = %#x\n", error, status); 422 423 return (error); 424} 425 426/* 427 * Polling Code. 428 * 429 * Polling is not encouraged because it requires waiting for the 430 * device if it is busy. 431 * (29063505.pdf from Intel) But during boot, interrupt cannot be used, so use 432 * polling code then. 433 */ 434static int 435intsmb_stop_poll(struct intsmb_softc *sc) 436{ 437 int error, i, status, tmp; 438 439 INTSMB_LOCK_ASSERT(sc); 440 441 /* First, wait for busy to be set. */ 442 for (i = 0; i < 0x7fff; i++) 443 if (bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & 444 PIIX4_SMBHSTSTAT_BUSY) 445 break; 446 447 /* Wait for busy to clear. */ 448 for (i = 0; i < 0x7fff; i++) { 449 status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 450 if (!(status & PIIX4_SMBHSTSTAT_BUSY)) { 451 sc->isbusy = 0; 452 error = intsmb_error(sc->dev, status); 453 return (error); 454 } 455 } 456 457 /* Timed out waiting for busy to clear. */ 458 sc->isbusy = 0; 459 tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 460 bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp & ~PIIX4_SMBHSTCNT_INTREN); 461 return (SMB_ETIMEOUT); 462} 463 464/* 465 * Wait for completion and return result. 466 */ 467static int 468intsmb_stop(struct intsmb_softc *sc) 469{ 470 int error, status; 471 472 INTSMB_LOCK_ASSERT(sc); 473 474 if (sc->poll || cold) 475 /* So that it can use device during device probe on SMBus. */ 476 return (intsmb_stop_poll(sc)); 477 478 error = msleep(sc, &sc->lock, PWAIT | PCATCH, "SMBWAI", hz / 8); 479 if (error == 0) { 480 status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 481 if (!(status & PIIX4_SMBHSTSTAT_BUSY)) { 482 error = intsmb_error(sc->dev, status); 483 if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR)) 484 device_printf(sc->dev, "unknown cause why?\n"); 485#ifdef ENABLE_ALART 486 bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 487 PIIX4_SMBSLVCNT_ALTEN); 488#endif 489 return (error); 490 } 491 } 492 493 /* Timeout Procedure. */ 494 sc->isbusy = 0; 495 496 /* Re-enable supressed interrupt from slave part. */ 497 bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN); 498 if (error == EWOULDBLOCK) 499 return (SMB_ETIMEOUT); 500 else 501 return (SMB_EABORT); 502} 503 504static int 505intsmb_quick(device_t dev, u_char slave, int how) 506{ 507 struct intsmb_softc *sc = device_get_softc(dev); 508 int error; 509 u_char data; 510 511 data = slave; 512 513 /* Quick command is part of Address, I think. */ 514 switch(how) { 515 case SMB_QWRITE: 516 data &= ~LSB; 517 break; 518 case SMB_QREAD: 519 data |= LSB; 520 break; 521 default: 522 return (SMB_EINVAL); 523 } 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, data); 532 intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_QUICK, 0); 533 error = intsmb_stop(sc); 534 INTSMB_UNLOCK(sc); 535 return (error); 536} 537 538static int 539intsmb_sendb(device_t dev, u_char slave, char byte) 540{ 541 struct intsmb_softc *sc = device_get_softc(dev); 542 int error; 543 544 INTSMB_LOCK(sc); 545 error = intsmb_free(sc); 546 if (error) { 547 INTSMB_UNLOCK(sc); 548 return (error); 549 } 550 bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 551 bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, byte); 552 intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0); 553 error = intsmb_stop(sc); 554 INTSMB_UNLOCK(sc); 555 return (error); 556} 557 558static int 559intsmb_recvb(device_t dev, u_char slave, char *byte) 560{ 561 struct intsmb_softc *sc = device_get_softc(dev); 562 int error; 563 564 INTSMB_LOCK(sc); 565 error = intsmb_free(sc); 566 if (error) { 567 INTSMB_UNLOCK(sc); 568 return (error); 569 } 570 bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 571 intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0); 572 error = intsmb_stop(sc); 573 if (error == 0) { 574#ifdef RECV_IS_IN_CMD 575 /* 576 * Linux SMBus stuff also troubles 577 * Because Intel's datasheet does not make clear. 578 */ 579 *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTCMD); 580#else 581 *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 582#endif 583 } 584 INTSMB_UNLOCK(sc); 585 return (error); 586} 587 588static int 589intsmb_writeb(device_t dev, u_char slave, char cmd, char byte) 590{ 591 struct intsmb_softc *sc = device_get_softc(dev); 592 int error; 593 594 INTSMB_LOCK(sc); 595 error = intsmb_free(sc); 596 if (error) { 597 INTSMB_UNLOCK(sc); 598 return (error); 599 } 600 bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 601 bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 602 bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, byte); 603 intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0); 604 error = intsmb_stop(sc); 605 INTSMB_UNLOCK(sc); 606 return (error); 607} 608 609static int 610intsmb_writew(device_t dev, u_char slave, char cmd, short word) 611{ 612 struct intsmb_softc *sc = device_get_softc(dev); 613 int error; 614 615 INTSMB_LOCK(sc); 616 error = intsmb_free(sc); 617 if (error) { 618 INTSMB_UNLOCK(sc); 619 return (error); 620 } 621 bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 622 bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 623 bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, word & 0xff); 624 bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (word >> 8) & 0xff); 625 intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 626 error = intsmb_stop(sc); 627 INTSMB_UNLOCK(sc); 628 return (error); 629} 630 631static int 632intsmb_readb(device_t dev, u_char slave, char cmd, char *byte) 633{ 634 struct intsmb_softc *sc = device_get_softc(dev); 635 int error; 636 637 INTSMB_LOCK(sc); 638 error = intsmb_free(sc); 639 if (error) { 640 INTSMB_UNLOCK(sc); 641 return (error); 642 } 643 bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 644 bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 645 intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0); 646 error = intsmb_stop(sc); 647 if (error == 0) 648 *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 649 INTSMB_UNLOCK(sc); 650 return (error); 651} 652 653static int 654intsmb_readw(device_t dev, u_char slave, char cmd, short *word) 655{ 656 struct intsmb_softc *sc = device_get_softc(dev); 657 int error; 658 659 INTSMB_LOCK(sc); 660 error = intsmb_free(sc); 661 if (error) { 662 INTSMB_UNLOCK(sc); 663 return (error); 664 } 665 bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 666 bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 667 intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 668 error = intsmb_stop(sc); 669 if (error == 0) { 670 *word = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 671 *word |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8; 672 } 673 INTSMB_UNLOCK(sc); 674 return (error); 675} 676 677/* 678 * Data sheet claims that it implements all function, but also claims 679 * that it implements 7 function and not mention PCALL. So I don't know 680 * whether it will work. 681 */ 682static int 683intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata) 684{ 685#ifdef PROCCALL_TEST 686 struct intsmb_softc *sc = device_get_softc(dev); 687 int error; 688 689 INTSMB_LOCK(sc); 690 error = intsmb_free(sc); 691 if (error) { 692 INTSMB_UNLOCK(sc); 693 return (error); 694 } 695 bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 696 bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 697 bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, sdata & 0xff); 698 bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (sdata & 0xff) >> 8); 699 intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 700 error = intsmb_stop(sc); 701 if (error == 0) { 702 *rdata = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 703 *rdata |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8; 704 } 705 INTSMB_UNLOCK(sc); 706 return (error); 707#else 708 return (SMB_ENOTSUPP); 709#endif 710} 711 712static int 713intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) 714{ 715 struct intsmb_softc *sc = device_get_softc(dev); 716 int error, i; 717 718 if (count > SMBBLOCKTRANS_MAX || count == 0) 719 return (SMB_EINVAL); 720 721 INTSMB_LOCK(sc); 722 error = intsmb_free(sc); 723 if (error) { 724 INTSMB_UNLOCK(sc); 725 return (error); 726 } 727 728 /* Reset internal array index. */ 729 bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 730 731 bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 732 bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 733 for (i = 0; i < count; i++) 734 bus_write_1(sc->io_res, PIIX4_SMBBLKDAT, buf[i]); 735 bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, count); 736 intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0); 737 error = intsmb_stop(sc); 738 INTSMB_UNLOCK(sc); 739 return (error); 740} 741 742static int 743intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf) 744{ 745 struct intsmb_softc *sc = device_get_softc(dev); 746 int error, i; 747 u_char data, nread; 748 749 if (*count > SMBBLOCKTRANS_MAX || *count == 0) 750 return (SMB_EINVAL); 751 752 INTSMB_LOCK(sc); 753 error = intsmb_free(sc); 754 if (error) { 755 INTSMB_UNLOCK(sc); 756 return (error); 757 } 758 759 /* Reset internal array index. */ 760 bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 761 762 bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 763 bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 764 bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, *count); 765 intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0); 766 error = intsmb_stop(sc); 767 if (error == 0) { 768 nread = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 769 if (nread != 0 && nread <= SMBBLOCKTRANS_MAX) { 770 for (i = 0; i < nread; i++) { 771 data = bus_read_1(sc->io_res, PIIX4_SMBBLKDAT); 772 if (i < *count) 773 buf[i] = data; 774 } 775 *count = nread; 776 } else 777 error = SMB_EBUSERR; 778 } 779 INTSMB_UNLOCK(sc); 780 return (error); 781} 782 783static devclass_t intsmb_devclass; 784 785static device_method_t intsmb_methods[] = { 786 /* Device interface */ 787 DEVMETHOD(device_probe, intsmb_probe), 788 DEVMETHOD(device_attach, intsmb_attach), 789 DEVMETHOD(device_detach, intsmb_detach), 790 791 /* SMBus interface */ 792 DEVMETHOD(smbus_callback, intsmb_callback), 793 DEVMETHOD(smbus_quick, intsmb_quick), 794 DEVMETHOD(smbus_sendb, intsmb_sendb), 795 DEVMETHOD(smbus_recvb, intsmb_recvb), 796 DEVMETHOD(smbus_writeb, intsmb_writeb), 797 DEVMETHOD(smbus_writew, intsmb_writew), 798 DEVMETHOD(smbus_readb, intsmb_readb), 799 DEVMETHOD(smbus_readw, intsmb_readw), 800 DEVMETHOD(smbus_pcall, intsmb_pcall), 801 DEVMETHOD(smbus_bwrite, intsmb_bwrite), 802 DEVMETHOD(smbus_bread, intsmb_bread), 803 804 DEVMETHOD_END 805}; 806 807static driver_t intsmb_driver = { 808 "intsmb", 809 intsmb_methods, 810 sizeof(struct intsmb_softc), 811}; 812 813DRIVER_MODULE(intsmb, pci, intsmb_driver, intsmb_devclass, 0, 0); 814DRIVER_MODULE(smbus, intsmb, smbus_driver, smbus_devclass, 0, 0); 815MODULE_DEPEND(intsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 816MODULE_VERSION(intsmb, 1); 817