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