1 /*- 2 * Copyright (c) 2000 Matthew C. Forman 3 * 4 * Based (heavily) on alpm.c which is: 5 * 6 * Copyright (c) 1998, 1999 Nicolas Souchu 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 /* 32 * Power management function/SMBus function support for the AMD 756 chip. 33 */ 34 35 #include <sys/cdefs.h> 36 __FBSDID("$FreeBSD$"); 37 38 #include <sys/param.h> 39 #include <sys/bus.h> 40 #include <sys/kernel.h> 41 #include <sys/lock.h> 42 #include <sys/module.h> 43 #include <sys/mutex.h> 44 #include <sys/systm.h> 45 46 #include <machine/bus.h> 47 #include <machine/resource.h> 48 #include <sys/rman.h> 49 50 #include <dev/pci/pcivar.h> 51 #include <dev/pci/pcireg.h> 52 53 #include <dev/smbus/smbconf.h> 54 #include "smbus_if.h" 55 56 #define AMDPM_DEBUG(x) if (amdpm_debug) (x) 57 58 #ifdef DEBUG 59 static int amdpm_debug = 1; 60 #else 61 static int amdpm_debug = 0; 62 #endif 63 64 #define AMDPM_VENDORID_AMD 0x1022 65 #define AMDPM_DEVICEID_AMD756PM 0x740b 66 #define AMDPM_DEVICEID_AMD766PM 0x7413 67 #define AMDPM_DEVICEID_AMD768PM 0x7443 68 #define AMDPM_DEVICEID_AMD8111PM 0x746B 69 70 /* nVidia nForce chipset */ 71 #define AMDPM_VENDORID_NVIDIA 0x10de 72 #define AMDPM_DEVICEID_NF_SMB 0x01b4 73 74 /* PCI Configuration space registers */ 75 #define AMDPCI_PMBASE 0x58 76 #define NFPCI_PMBASE 0x14 77 78 #define AMDPCI_GEN_CONFIG_PM 0x41 79 #define AMDPCI_PMIOEN (1<<7) 80 81 #define AMDPCI_SCIINT_CONFIG_PM 0x42 82 #define AMDPCI_SCISEL_IRQ11 11 83 84 #define AMDPCI_REVID 0x08 85 86 /* 87 * I/O registers. 88 * Base address programmed via AMDPCI_PMBASE. 89 */ 90 91 #define AMDSMB_GLOBAL_STATUS (0x00) 92 #define AMDSMB_GS_TO_STS (1<<5) 93 #define AMDSMB_GS_HCYC_STS (1<<4) 94 #define AMDSMB_GS_HST_STS (1<<3) 95 #define AMDSMB_GS_PRERR_STS (1<<2) 96 #define AMDSMB_GS_COL_STS (1<<1) 97 #define AMDSMB_GS_ABRT_STS (1<<0) 98 #define AMDSMB_GS_CLEAR_STS (AMDSMB_GS_TO_STS|AMDSMB_GS_HCYC_STS|AMDSMB_GS_PRERR_STS|AMDSMB_GS_COL_STS|AMDSMB_GS_ABRT_STS) 99 100 #define AMDSMB_GLOBAL_ENABLE (0x02) 101 #define AMDSMB_GE_ABORT (1<<5) 102 #define AMDSMB_GE_HCYC_EN (1<<4) 103 #define AMDSMB_GE_HOST_STC (1<<3) 104 #define AMDSMB_GE_CYC_QUICK 0 105 #define AMDSMB_GE_CYC_BYTE 1 106 #define AMDSMB_GE_CYC_BDATA 2 107 #define AMDSMB_GE_CYC_WDATA 3 108 #define AMDSMB_GE_CYC_PROCCALL 4 109 #define AMDSMB_GE_CYC_BLOCK 5 110 111 #define LSB 0x1 /* XXX: Better name: Read/Write? */ 112 113 #define AMDSMB_HSTADDR (0x04) 114 #define AMDSMB_HSTDATA (0x06) 115 #define AMDSMB_HSTCMD (0x08) 116 #define AMDSMB_HSTDFIFO (0x09) 117 #define AMDSMB_HSLVDATA (0x0A) 118 #define AMDSMB_HSLVDA (0x0C) 119 #define AMDSMB_HSLVDDR (0x0E) 120 #define AMDSMB_SNPADDR (0x0F) 121 122 struct amdpm_softc { 123 int base; 124 int rid; 125 struct resource *res; 126 device_t smbus; 127 struct mtx lock; 128 }; 129 130 #define AMDPM_LOCK(amdpm) mtx_lock(&(amdpm)->lock) 131 #define AMDPM_UNLOCK(amdpm) mtx_unlock(&(amdpm)->lock) 132 #define AMDPM_LOCK_ASSERT(amdpm) mtx_assert(&(amdpm)->lock, MA_OWNED) 133 134 #define AMDPM_SMBINB(amdpm,register) \ 135 (bus_read_1(amdpm->res, register)) 136 #define AMDPM_SMBOUTB(amdpm,register,value) \ 137 (bus_write_1(amdpm->res, register, value)) 138 #define AMDPM_SMBINW(amdpm,register) \ 139 (bus_read_2(amdpm->res, register)) 140 #define AMDPM_SMBOUTW(amdpm,register,value) \ 141 (bus_write_2(amdpm->res, register, value)) 142 143 static int amdpm_detach(device_t dev); 144 145 static int 146 amdpm_probe(device_t dev) 147 { 148 u_long base; 149 u_int16_t vid; 150 u_int16_t did; 151 152 vid = pci_get_vendor(dev); 153 did = pci_get_device(dev); 154 if ((vid == AMDPM_VENDORID_AMD) && 155 ((did == AMDPM_DEVICEID_AMD756PM) || 156 (did == AMDPM_DEVICEID_AMD766PM) || 157 (did == AMDPM_DEVICEID_AMD768PM) || 158 (did == AMDPM_DEVICEID_AMD8111PM))) { 159 device_set_desc(dev, "AMD 756/766/768/8111 Power Management Controller"); 160 161 /* 162 * We have to do this, since the BIOS won't give us the 163 * resource info (not mine, anyway). 164 */ 165 base = pci_read_config(dev, AMDPCI_PMBASE, 4); 166 base &= 0xff00; 167 bus_set_resource(dev, SYS_RES_IOPORT, AMDPCI_PMBASE, 168 base+0xe0, 32); 169 return (BUS_PROBE_DEFAULT); 170 } 171 172 if ((vid == AMDPM_VENDORID_NVIDIA) && 173 (did == AMDPM_DEVICEID_NF_SMB)) { 174 device_set_desc(dev, "nForce SMBus Controller"); 175 176 /* 177 * We have to do this, since the BIOS won't give us the 178 * resource info (not mine, anyway). 179 */ 180 base = pci_read_config(dev, NFPCI_PMBASE, 4); 181 base &= 0xff00; 182 bus_set_resource(dev, SYS_RES_IOPORT, NFPCI_PMBASE, 183 base, 32); 184 185 return (BUS_PROBE_DEFAULT); 186 } 187 188 return ENXIO; 189 } 190 191 static int 192 amdpm_attach(device_t dev) 193 { 194 struct amdpm_softc *amdpm_sc = device_get_softc(dev); 195 u_char val_b; 196 197 /* Enable I/O block access */ 198 val_b = pci_read_config(dev, AMDPCI_GEN_CONFIG_PM, 1); 199 pci_write_config(dev, AMDPCI_GEN_CONFIG_PM, val_b | AMDPCI_PMIOEN, 1); 200 201 /* Allocate I/O space */ 202 if (pci_get_vendor(dev) == AMDPM_VENDORID_AMD) 203 amdpm_sc->rid = AMDPCI_PMBASE; 204 else 205 amdpm_sc->rid = NFPCI_PMBASE; 206 amdpm_sc->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, 207 &amdpm_sc->rid, RF_ACTIVE); 208 209 if (amdpm_sc->res == NULL) { 210 device_printf(dev, "could not map i/o space\n"); 211 return (ENXIO); 212 } 213 214 mtx_init(&amdpm_sc->lock, device_get_nameunit(dev), "amdpm", MTX_DEF); 215 216 /* Allocate a new smbus device */ 217 amdpm_sc->smbus = device_add_child(dev, "smbus", -1); 218 if (!amdpm_sc->smbus) { 219 amdpm_detach(dev); 220 return (EINVAL); 221 } 222 223 bus_generic_attach(dev); 224 225 return (0); 226 } 227 228 static int 229 amdpm_detach(device_t dev) 230 { 231 struct amdpm_softc *amdpm_sc = device_get_softc(dev); 232 233 if (amdpm_sc->smbus) { 234 device_delete_child(dev, amdpm_sc->smbus); 235 amdpm_sc->smbus = NULL; 236 } 237 238 mtx_destroy(&amdpm_sc->lock); 239 if (amdpm_sc->res) 240 bus_release_resource(dev, SYS_RES_IOPORT, amdpm_sc->rid, 241 amdpm_sc->res); 242 243 return (0); 244 } 245 246 static int 247 amdpm_callback(device_t dev, int index, void *data) 248 { 249 int error = 0; 250 251 switch (index) { 252 case SMB_REQUEST_BUS: 253 case SMB_RELEASE_BUS: 254 break; 255 default: 256 error = EINVAL; 257 } 258 259 return (error); 260 } 261 262 static int 263 amdpm_clear(struct amdpm_softc *sc) 264 { 265 266 AMDPM_LOCK_ASSERT(sc); 267 AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_STATUS, AMDSMB_GS_CLEAR_STS); 268 DELAY(10); 269 270 return (0); 271 } 272 273 #if 0 274 static int 275 amdpm_abort(struct amdpm_softc *sc) 276 { 277 u_short l; 278 279 l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); 280 AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, l | AMDSMB_GE_ABORT); 281 282 return (0); 283 } 284 #endif 285 286 static int 287 amdpm_idle(struct amdpm_softc *sc) 288 { 289 u_short sts; 290 291 AMDPM_LOCK_ASSERT(sc); 292 sts = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_STATUS); 293 294 AMDPM_DEBUG(printf("amdpm: busy? STS=0x%x\n", sts)); 295 296 return (~(sts & AMDSMB_GS_HST_STS)); 297 } 298 299 /* 300 * Poll the SMBus controller 301 */ 302 static int 303 amdpm_wait(struct amdpm_softc *sc) 304 { 305 int count = 10000; 306 u_short sts = 0; 307 int error; 308 309 AMDPM_LOCK_ASSERT(sc); 310 /* Wait for command to complete (SMBus controller is idle) */ 311 while(count--) { 312 DELAY(10); 313 sts = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_STATUS); 314 if (!(sts & AMDSMB_GS_HST_STS)) 315 break; 316 } 317 318 AMDPM_DEBUG(printf("amdpm: STS=0x%x (count=%d)\n", sts, count)); 319 320 error = SMB_ENOERR; 321 322 if (!count) 323 error |= SMB_ETIMEOUT; 324 325 if (sts & AMDSMB_GS_ABRT_STS) 326 error |= SMB_EABORT; 327 328 if (sts & AMDSMB_GS_COL_STS) 329 error |= SMB_ENOACK; 330 331 if (sts & AMDSMB_GS_PRERR_STS) 332 error |= SMB_EBUSERR; 333 334 if (error != SMB_ENOERR) 335 amdpm_clear(sc); 336 337 return (error); 338 } 339 340 static int 341 amdpm_quick(device_t dev, u_char slave, int how) 342 { 343 struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev); 344 int error; 345 u_short l; 346 347 AMDPM_LOCK(sc); 348 amdpm_clear(sc); 349 if (!amdpm_idle(sc)) { 350 AMDPM_UNLOCK(sc); 351 return (EBUSY); 352 } 353 354 switch (how) { 355 case SMB_QWRITE: 356 AMDPM_DEBUG(printf("amdpm: QWRITE to 0x%x", slave)); 357 AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB); 358 break; 359 case SMB_QREAD: 360 AMDPM_DEBUG(printf("amdpm: QREAD to 0x%x", slave)); 361 AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB); 362 break; 363 default: 364 panic("%s: unknown QUICK command (%x)!", __func__, how); 365 } 366 l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); 367 AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_QUICK | AMDSMB_GE_HOST_STC); 368 369 error = amdpm_wait(sc); 370 371 AMDPM_DEBUG(printf(", error=0x%x\n", error)); 372 AMDPM_UNLOCK(sc); 373 374 return (error); 375 } 376 377 static int 378 amdpm_sendb(device_t dev, u_char slave, char byte) 379 { 380 struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev); 381 int error; 382 u_short l; 383 384 AMDPM_LOCK(sc); 385 amdpm_clear(sc); 386 if (!amdpm_idle(sc)) { 387 AMDPM_UNLOCK(sc); 388 return (SMB_EBUSY); 389 } 390 391 AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB); 392 AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, byte); 393 l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); 394 AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BYTE | AMDSMB_GE_HOST_STC); 395 396 error = amdpm_wait(sc); 397 398 AMDPM_DEBUG(printf("amdpm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error)); 399 AMDPM_UNLOCK(sc); 400 401 return (error); 402 } 403 404 static int 405 amdpm_recvb(device_t dev, u_char slave, char *byte) 406 { 407 struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev); 408 int error; 409 u_short l; 410 411 AMDPM_LOCK(sc); 412 amdpm_clear(sc); 413 if (!amdpm_idle(sc)) { 414 AMDPM_UNLOCK(sc); 415 return (SMB_EBUSY); 416 } 417 418 AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB); 419 l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); 420 AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BYTE | AMDSMB_GE_HOST_STC); 421 422 if ((error = amdpm_wait(sc)) == SMB_ENOERR) 423 *byte = AMDPM_SMBINW(sc, AMDSMB_HSTDATA); 424 425 AMDPM_DEBUG(printf("amdpm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error)); 426 AMDPM_UNLOCK(sc); 427 428 return (error); 429 } 430 431 static int 432 amdpm_writeb(device_t dev, u_char slave, char cmd, char byte) 433 { 434 struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev); 435 int error; 436 u_short l; 437 438 AMDPM_LOCK(sc); 439 amdpm_clear(sc); 440 if (!amdpm_idle(sc)) { 441 AMDPM_UNLOCK(sc); 442 return (SMB_EBUSY); 443 } 444 445 AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB); 446 AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, byte); 447 AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd); 448 l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); 449 AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BDATA | AMDSMB_GE_HOST_STC); 450 451 error = amdpm_wait(sc); 452 453 AMDPM_DEBUG(printf("amdpm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error)); 454 AMDPM_UNLOCK(sc); 455 456 return (error); 457 } 458 459 static int 460 amdpm_readb(device_t dev, u_char slave, char cmd, char *byte) 461 { 462 struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev); 463 int error; 464 u_short l; 465 466 AMDPM_LOCK(sc); 467 amdpm_clear(sc); 468 if (!amdpm_idle(sc)) { 469 AMDPM_UNLOCK(sc); 470 return (SMB_EBUSY); 471 } 472 473 AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB); 474 AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd); 475 l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); 476 AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BDATA | AMDSMB_GE_HOST_STC); 477 478 if ((error = amdpm_wait(sc)) == SMB_ENOERR) 479 *byte = AMDPM_SMBINW(sc, AMDSMB_HSTDATA); 480 481 AMDPM_DEBUG(printf("amdpm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error)); 482 AMDPM_UNLOCK(sc); 483 484 return (error); 485 } 486 487 static int 488 amdpm_writew(device_t dev, u_char slave, char cmd, short word) 489 { 490 struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev); 491 int error; 492 u_short l; 493 494 AMDPM_LOCK(sc); 495 amdpm_clear(sc); 496 if (!amdpm_idle(sc)) { 497 AMDPM_UNLOCK(sc); 498 return (SMB_EBUSY); 499 } 500 501 AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB); 502 AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, word); 503 AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd); 504 l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); 505 AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_WDATA | AMDSMB_GE_HOST_STC); 506 507 error = amdpm_wait(sc); 508 509 AMDPM_DEBUG(printf("amdpm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error)); 510 AMDPM_UNLOCK(sc); 511 512 return (error); 513 } 514 515 static int 516 amdpm_readw(device_t dev, u_char slave, char cmd, short *word) 517 { 518 struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev); 519 int error; 520 u_short l; 521 522 AMDPM_LOCK(sc); 523 amdpm_clear(sc); 524 if (!amdpm_idle(sc)) { 525 AMDPM_UNLOCK(sc); 526 return (SMB_EBUSY); 527 } 528 529 AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB); 530 AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd); 531 l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); 532 AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_WDATA | AMDSMB_GE_HOST_STC); 533 534 if ((error = amdpm_wait(sc)) == SMB_ENOERR) 535 *word = AMDPM_SMBINW(sc, AMDSMB_HSTDATA); 536 537 AMDPM_DEBUG(printf("amdpm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error)); 538 AMDPM_UNLOCK(sc); 539 540 return (error); 541 } 542 543 static int 544 amdpm_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) 545 { 546 struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev); 547 u_char i; 548 int error; 549 u_short l; 550 551 if (count < 1 || count > 32) 552 return (SMB_EINVAL); 553 554 AMDPM_LOCK(sc); 555 amdpm_clear(sc); 556 if (!amdpm_idle(sc)) { 557 AMDPM_UNLOCK(sc); 558 return (SMB_EBUSY); 559 } 560 561 AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB); 562 563 /* 564 * Do we have to reset the internal 32-byte buffer? 565 * Can't see how to do this from the data sheet. 566 */ 567 AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, count); 568 569 /* Fill the 32-byte internal buffer */ 570 for (i = 0; i < count; i++) { 571 AMDPM_SMBOUTB(sc, AMDSMB_HSTDFIFO, buf[i]); 572 DELAY(2); 573 } 574 AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd); 575 l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); 576 AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, 577 (l & 0xfff8) | AMDSMB_GE_CYC_BLOCK | AMDSMB_GE_HOST_STC); 578 579 error = amdpm_wait(sc); 580 581 AMDPM_DEBUG(printf("amdpm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error)); 582 AMDPM_UNLOCK(sc); 583 584 return (error); 585 } 586 587 static int 588 amdpm_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf) 589 { 590 struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev); 591 u_char data, len, i; 592 int error; 593 u_short l; 594 595 if (*count < 1 || *count > 32) 596 return (SMB_EINVAL); 597 598 AMDPM_LOCK(sc); 599 amdpm_clear(sc); 600 if (!amdpm_idle(sc)) { 601 AMDPM_UNLOCK(sc); 602 return (SMB_EBUSY); 603 } 604 605 AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB); 606 607 AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd); 608 609 l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); 610 AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, 611 (l & 0xfff8) | AMDSMB_GE_CYC_BLOCK | AMDSMB_GE_HOST_STC); 612 613 if ((error = amdpm_wait(sc)) != SMB_ENOERR) 614 goto error; 615 616 len = AMDPM_SMBINW(sc, AMDSMB_HSTDATA); 617 618 /* Read the 32-byte internal buffer */ 619 for (i = 0; i < len; i++) { 620 data = AMDPM_SMBINB(sc, AMDSMB_HSTDFIFO); 621 if (i < *count) 622 buf[i] = data; 623 DELAY(2); 624 } 625 *count = len; 626 627 error: 628 AMDPM_DEBUG(printf("amdpm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, *count, cmd, error)); 629 AMDPM_UNLOCK(sc); 630 631 return (error); 632 } 633 634 static devclass_t amdpm_devclass; 635 636 static device_method_t amdpm_methods[] = { 637 /* Device interface */ 638 DEVMETHOD(device_probe, amdpm_probe), 639 DEVMETHOD(device_attach, amdpm_attach), 640 DEVMETHOD(device_detach, amdpm_detach), 641 642 /* SMBus interface */ 643 DEVMETHOD(smbus_callback, amdpm_callback), 644 DEVMETHOD(smbus_quick, amdpm_quick), 645 DEVMETHOD(smbus_sendb, amdpm_sendb), 646 DEVMETHOD(smbus_recvb, amdpm_recvb), 647 DEVMETHOD(smbus_writeb, amdpm_writeb), 648 DEVMETHOD(smbus_readb, amdpm_readb), 649 DEVMETHOD(smbus_writew, amdpm_writew), 650 DEVMETHOD(smbus_readw, amdpm_readw), 651 DEVMETHOD(smbus_bwrite, amdpm_bwrite), 652 DEVMETHOD(smbus_bread, amdpm_bread), 653 654 { 0, 0 } 655 }; 656 657 static driver_t amdpm_driver = { 658 "amdpm", 659 amdpm_methods, 660 sizeof(struct amdpm_softc), 661 }; 662 663 DRIVER_MODULE(amdpm, pci, amdpm_driver, amdpm_devclass, 0, 0); 664 DRIVER_MODULE(smbus, amdpm, smbus_driver, smbus_devclass, 0, 0); 665 666 MODULE_DEPEND(amdpm, pci, 1, 1, 1); 667 MODULE_DEPEND(amdpm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 668 MODULE_VERSION(amdpm, 1); 669