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