1 /*- 2 * Copyright (c) 1998, 1999, 2001 Nicolas Souchu 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: src/sys/pci/alpm.c,v 1.26 2007/01/11 19:56:24 jhb Exp $ 27 * 28 */ 29 30 /* 31 * Power Management support for the Acer M15x3 chipsets 32 */ 33 #include <sys/param.h> 34 #include <sys/bus.h> 35 #include <sys/kernel.h> 36 #include <sys/lock.h> 37 #include <sys/module.h> 38 #include <sys/rman.h> 39 #include <sys/systm.h> 40 41 #include <bus/pci/pcivar.h> 42 #include <bus/pci/pcireg.h> 43 44 #include <bus/smbus/smbconf.h> 45 #include "smbus_if.h" 46 47 #define ALPM_DEBUG(x) if (alpm_debug) (x) 48 49 #ifdef DEBUG 50 static int alpm_debug = 1; 51 #else 52 static int alpm_debug = 0; 53 #endif 54 55 #define ACER_M1543_PMU_ID 0x710110b9 56 57 /* 58 * I/O registers offsets - the base address is programmed via the 59 * SMBBA PCI configuration register 60 */ 61 #define SMBSTS 0x0 /* SMBus host/slave status register */ 62 #define SMBCMD 0x1 /* SMBus host/slave command register */ 63 #define SMBSTART 0x2 /* start to generate programmed cycle */ 64 #define SMBHADDR 0x3 /* host address register */ 65 #define SMBHDATA 0x4 /* data A register for host controller */ 66 #define SMBHDATB 0x5 /* data B register for host controller */ 67 #define SMBHBLOCK 0x6 /* block register for host controller */ 68 #define SMBHCMD 0x7 /* command register for host controller */ 69 70 /* SMBHADDR mask. */ 71 #define LSB 0x1 /* XXX: Better name: Read/Write? */ 72 73 /* SMBSTS masks */ 74 #define TERMINATE 0x80 75 #define BUS_COLLI 0x40 76 #define DEVICE_ERR 0x20 77 #define SMI_I_STS 0x10 78 #define HST_BSY 0x08 79 #define IDL_STS 0x04 80 #define HSTSLV_STS 0x02 81 #define HSTSLV_BSY 0x01 82 83 /* SMBCMD masks */ 84 #define SMB_BLK_CLR 0x80 85 #define T_OUT_CMD 0x08 86 #define ABORT_HOST 0x04 87 88 /* SMBus commands */ 89 #define SMBQUICK 0x00 90 #define SMBSRBYTE 0x10 /* send/receive byte */ 91 #define SMBWRBYTE 0x20 /* write/read byte */ 92 #define SMBWRWORD 0x30 /* write/read word */ 93 #define SMBWRBLOCK 0x40 /* write/read block */ 94 95 /* PCI configuration registers and masks 96 */ 97 #define COM 0x4 98 #define COM_ENABLE_IO 0x1 99 100 #define SMBBA PCIR_BAR(1) 101 102 #define ATPC 0x5b 103 #define ATPC_SMBCTRL 0x04 /* XX linux has this as 0x6 */ 104 105 #define SMBHSI 0xe0 106 #define SMBHSI_SLAVE 0x2 107 #define SMBHSI_HOST 0x1 108 109 #define SMBHCBC 0xe2 110 #define SMBHCBC_CLOCK 0x70 111 112 #define SMBCLOCK_149K 0x0 113 #define SMBCLOCK_74K 0x20 114 #define SMBCLOCK_37K 0x40 115 #define SMBCLOCK_223K 0x80 116 #define SMBCLOCK_111K 0xa0 117 #define SMBCLOCK_55K 0xc0 118 119 struct alpm_softc { 120 int base; 121 struct resource *res; 122 bus_space_tag_t smbst; 123 bus_space_handle_t smbsh; 124 device_t smbus; 125 struct lock lock; 126 }; 127 128 #define ALPM_LOCK(alpm) lockmgr(&(alpm)->lock, LK_EXCLUSIVE) 129 #define ALPM_UNLOCK(alpm) lockmgr(&(alpm)->lock, LK_RELEASE) 130 #define ALPM_LOCK_ASSERT(alpm) KKASSERT(lockstatus(&(alpm)->lock, curthread) != 0) 131 132 #define ALPM_SMBINB(alpm,register) \ 133 (bus_space_read_1(alpm->smbst, alpm->smbsh, register)) 134 #define ALPM_SMBOUTB(alpm,register,value) \ 135 (bus_space_write_1(alpm->smbst, alpm->smbsh, register, value)) 136 137 static int alpm_detach(device_t dev); 138 139 static int 140 alpm_probe(device_t dev) 141 { 142 143 if (pci_get_devid(dev) == ACER_M1543_PMU_ID) { 144 device_set_desc(dev, "AcerLabs M15x3 Power Management Unit"); 145 146 return (BUS_PROBE_DEFAULT); 147 } 148 149 return (ENXIO); 150 } 151 152 static int 153 alpm_attach(device_t dev) 154 { 155 int rid; 156 u_int32_t l; 157 struct alpm_softc *alpm; 158 159 alpm = device_get_softc(dev); 160 161 /* Unlock SMBIO base register access */ 162 l = pci_read_config(dev, ATPC, 1); 163 pci_write_config(dev, ATPC, l & ~ATPC_SMBCTRL, 1); 164 165 /* 166 * XX linux sets clock to 74k, should we? 167 l = pci_read_config(dev, SMBHCBC, 1); 168 l &= 0x1f; 169 l |= SMBCLOCK_74K; 170 pci_write_config(dev, SMBHCBC, l, 1); 171 */ 172 173 if (bootverbose || alpm_debug) { 174 l = pci_read_config(dev, SMBHSI, 1); 175 device_printf(dev, "%s/%s", 176 (l & SMBHSI_HOST) ? "host":"nohost", 177 (l & SMBHSI_SLAVE) ? "slave":"noslave"); 178 179 l = pci_read_config(dev, SMBHCBC, 1); 180 switch (l & SMBHCBC_CLOCK) { 181 case SMBCLOCK_149K: 182 kprintf(" 149K"); 183 break; 184 case SMBCLOCK_74K: 185 kprintf(" 74K"); 186 break; 187 case SMBCLOCK_37K: 188 kprintf(" 37K"); 189 break; 190 case SMBCLOCK_223K: 191 kprintf(" 223K"); 192 break; 193 case SMBCLOCK_111K: 194 kprintf(" 111K"); 195 break; 196 case SMBCLOCK_55K: 197 kprintf(" 55K"); 198 break; 199 default: 200 kprintf("unknown"); 201 break; 202 } 203 kprintf("\n"); 204 } 205 206 rid = SMBBA; 207 alpm->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 208 RF_ACTIVE); 209 210 if (alpm->res == NULL) { 211 device_printf(dev, "Could not allocate Bus space\n"); 212 return (ENXIO); 213 } 214 alpm->smbst = rman_get_bustag(alpm->res); 215 alpm->smbsh = rman_get_bushandle(alpm->res); 216 lockinit(&alpm->lock, "alpm", 0, LK_CANRECURSE); 217 218 /* attach the smbus */ 219 alpm->smbus = device_add_child(dev, "smbus", -1); 220 if (alpm->smbus == NULL) { 221 alpm_detach(dev); 222 return (EINVAL); 223 } 224 bus_generic_attach(dev); 225 226 return (0); 227 } 228 229 static int 230 alpm_detach(device_t dev) 231 { 232 struct alpm_softc *alpm = device_get_softc(dev); 233 234 if (alpm->smbus) { 235 device_delete_child(dev, alpm->smbus); 236 alpm->smbus = NULL; 237 } 238 lockuninit(&alpm->lock); 239 240 if (alpm->res) 241 bus_release_resource(dev, SYS_RES_IOPORT, SMBBA, alpm->res); 242 243 return (0); 244 } 245 246 static int 247 alpm_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 /* ok, bus allocation accepted */ 255 break; 256 default: 257 error = EINVAL; 258 } 259 260 return (error); 261 } 262 263 static int 264 alpm_clear(struct alpm_softc *sc) 265 { 266 ALPM_SMBOUTB(sc, SMBSTS, 0xff); 267 DELAY(10); 268 269 return (0); 270 } 271 272 #if 0 273 static int 274 alpm_abort(struct alpm_softc *sc) 275 { 276 ALPM_SMBOUTB(sc, SMBCMD, T_OUT_CMD | ABORT_HOST); 277 278 return (0); 279 } 280 #endif 281 282 static int 283 alpm_idle(struct alpm_softc *sc) 284 { 285 u_char sts; 286 287 sts = ALPM_SMBINB(sc, SMBSTS); 288 289 ALPM_DEBUG(kprintf("alpm: idle? STS=0x%x\n", sts)); 290 291 return (sts & IDL_STS); 292 } 293 294 /* 295 * Poll the SMBus controller 296 */ 297 static int 298 alpm_wait(struct alpm_softc *sc) 299 { 300 int count = 10000; 301 u_char sts = 0; 302 int error; 303 304 /* wait for command to complete and SMBus controller is idle */ 305 while (count--) { 306 DELAY(10); 307 sts = ALPM_SMBINB(sc, SMBSTS); 308 if (sts & SMI_I_STS) 309 break; 310 } 311 312 ALPM_DEBUG(kprintf("alpm: STS=0x%x\n", sts)); 313 314 error = SMB_ENOERR; 315 316 if (!count) 317 error |= SMB_ETIMEOUT; 318 319 if (sts & TERMINATE) 320 error |= SMB_EABORT; 321 322 if (sts & BUS_COLLI) 323 error |= SMB_ENOACK; 324 325 if (sts & DEVICE_ERR) 326 error |= SMB_EBUSERR; 327 328 if (error != SMB_ENOERR) 329 alpm_clear(sc); 330 331 return (error); 332 } 333 334 static int 335 alpm_quick(device_t dev, u_char slave, int how) 336 { 337 struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 338 int error; 339 340 ALPM_LOCK(sc); 341 alpm_clear(sc); 342 if (!alpm_idle(sc)) { 343 ALPM_UNLOCK(sc); 344 return (EBUSY); 345 } 346 347 switch (how) { 348 case SMB_QWRITE: 349 ALPM_DEBUG(kprintf("alpm: QWRITE to 0x%x", slave)); 350 ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 351 break; 352 case SMB_QREAD: 353 ALPM_DEBUG(kprintf("alpm: QREAD to 0x%x", slave)); 354 ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 355 break; 356 default: 357 panic("%s: unknown QUICK command (%x)!", __func__, how); 358 } 359 ALPM_SMBOUTB(sc, SMBCMD, SMBQUICK); 360 ALPM_SMBOUTB(sc, SMBSTART, 0xff); 361 362 error = alpm_wait(sc); 363 364 ALPM_DEBUG(kprintf(", error=0x%x\n", error)); 365 ALPM_UNLOCK(sc); 366 367 return (error); 368 } 369 370 static int 371 alpm_sendb(device_t dev, u_char slave, char byte) 372 { 373 struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 374 int error; 375 376 ALPM_LOCK(sc); 377 alpm_clear(sc); 378 if (!alpm_idle(sc)) { 379 ALPM_UNLOCK(sc); 380 return (SMB_EBUSY); 381 } 382 383 ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 384 ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE); 385 ALPM_SMBOUTB(sc, SMBHDATA, byte); 386 ALPM_SMBOUTB(sc, SMBSTART, 0xff); 387 388 error = alpm_wait(sc); 389 390 ALPM_DEBUG(kprintf("alpm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error)); 391 ALPM_UNLOCK(sc); 392 393 return (error); 394 } 395 396 static int 397 alpm_recvb(device_t dev, u_char slave, char *byte) 398 { 399 struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 400 int error; 401 402 ALPM_LOCK(sc); 403 alpm_clear(sc); 404 if (!alpm_idle(sc)) { 405 ALPM_UNLOCK(sc); 406 return (SMB_EBUSY); 407 } 408 409 ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 410 ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE); 411 ALPM_SMBOUTB(sc, SMBSTART, 0xff); 412 413 if ((error = alpm_wait(sc)) == SMB_ENOERR) 414 *byte = ALPM_SMBINB(sc, SMBHDATA); 415 416 ALPM_DEBUG(kprintf("alpm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error)); 417 ALPM_UNLOCK(sc); 418 419 return (error); 420 } 421 422 static int 423 alpm_writeb(device_t dev, u_char slave, char cmd, char byte) 424 { 425 struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 426 int error; 427 428 ALPM_LOCK(sc); 429 alpm_clear(sc); 430 if (!alpm_idle(sc)) { 431 ALPM_UNLOCK(sc); 432 return (SMB_EBUSY); 433 } 434 435 ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 436 ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE); 437 ALPM_SMBOUTB(sc, SMBHDATA, byte); 438 ALPM_SMBOUTB(sc, SMBHCMD, cmd); 439 ALPM_SMBOUTB(sc, SMBSTART, 0xff); 440 441 error = alpm_wait(sc); 442 443 ALPM_DEBUG(kprintf("alpm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error)); 444 ALPM_UNLOCK(sc); 445 446 return (error); 447 } 448 449 static int 450 alpm_readb(device_t dev, u_char slave, char cmd, char *byte) 451 { 452 struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 453 int error; 454 455 ALPM_LOCK(sc); 456 alpm_clear(sc); 457 if (!alpm_idle(sc)) { 458 ALPM_UNLOCK(sc); 459 return (SMB_EBUSY); 460 } 461 462 ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 463 ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE); 464 ALPM_SMBOUTB(sc, SMBHCMD, cmd); 465 ALPM_SMBOUTB(sc, SMBSTART, 0xff); 466 467 if ((error = alpm_wait(sc)) == SMB_ENOERR) 468 *byte = ALPM_SMBINB(sc, SMBHDATA); 469 470 ALPM_DEBUG(kprintf("alpm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error)); 471 ALPM_UNLOCK(sc); 472 473 return (error); 474 } 475 476 static int 477 alpm_writew(device_t dev, u_char slave, char cmd, short word) 478 { 479 struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 480 int error; 481 482 ALPM_LOCK(sc); 483 alpm_clear(sc); 484 if (!alpm_idle(sc)) { 485 ALPM_UNLOCK(sc); 486 return (SMB_EBUSY); 487 } 488 489 ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 490 ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD); 491 ALPM_SMBOUTB(sc, SMBHDATA, word & 0x00ff); 492 ALPM_SMBOUTB(sc, SMBHDATB, (word & 0xff00) >> 8); 493 ALPM_SMBOUTB(sc, SMBHCMD, cmd); 494 ALPM_SMBOUTB(sc, SMBSTART, 0xff); 495 496 error = alpm_wait(sc); 497 498 ALPM_DEBUG(kprintf("alpm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error)); 499 ALPM_UNLOCK(sc); 500 501 return (error); 502 } 503 504 static int 505 alpm_readw(device_t dev, u_char slave, char cmd, short *word) 506 { 507 struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 508 int error; 509 u_char high, low; 510 511 ALPM_LOCK(sc); 512 alpm_clear(sc); 513 if (!alpm_idle(sc)) { 514 ALPM_UNLOCK(sc); 515 return (SMB_EBUSY); 516 } 517 518 ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 519 ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD); 520 ALPM_SMBOUTB(sc, SMBHCMD, cmd); 521 ALPM_SMBOUTB(sc, SMBSTART, 0xff); 522 523 if ((error = alpm_wait(sc)) == SMB_ENOERR) { 524 low = ALPM_SMBINB(sc, SMBHDATA); 525 high = ALPM_SMBINB(sc, SMBHDATB); 526 527 *word = ((high & 0xff) << 8) | (low & 0xff); 528 } 529 530 ALPM_DEBUG(kprintf("alpm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error)); 531 ALPM_UNLOCK(sc); 532 533 return (error); 534 } 535 536 static int 537 alpm_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) 538 { 539 struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 540 u_char i; 541 int error; 542 543 if (count < 1 || count > 32) 544 return (SMB_EINVAL); 545 546 ALPM_LOCK(sc); 547 alpm_clear(sc); 548 if(!alpm_idle(sc)) { 549 ALPM_UNLOCK(sc); 550 return (SMB_EBUSY); 551 } 552 553 ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 554 555 /* set the cmd and reset the 556 * 32-byte long internal buffer */ 557 ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR); 558 559 ALPM_SMBOUTB(sc, SMBHDATA, count); 560 561 /* fill the 32-byte internal buffer */ 562 for (i = 0; i < count; i++) { 563 ALPM_SMBOUTB(sc, SMBHBLOCK, buf[i]); 564 DELAY(2); 565 } 566 ALPM_SMBOUTB(sc, SMBHCMD, cmd); 567 ALPM_SMBOUTB(sc, SMBSTART, 0xff); 568 569 error = alpm_wait(sc); 570 571 ALPM_DEBUG(kprintf("alpm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error)); 572 ALPM_UNLOCK(sc); 573 574 return (error); 575 } 576 577 static int 578 alpm_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf) 579 { 580 struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 581 u_char data, len, i; 582 int error; 583 584 if (*count < 1 || *count > 32) 585 return (SMB_EINVAL); 586 587 ALPM_LOCK(sc); 588 alpm_clear(sc); 589 if (!alpm_idle(sc)) { 590 ALPM_UNLOCK(sc); 591 return (SMB_EBUSY); 592 } 593 594 ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 595 596 /* set the cmd and reset the 597 * 32-byte long internal buffer */ 598 ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR); 599 600 ALPM_SMBOUTB(sc, SMBHCMD, cmd); 601 ALPM_SMBOUTB(sc, SMBSTART, 0xff); 602 603 if ((error = alpm_wait(sc)) != SMB_ENOERR) 604 goto error; 605 606 len = ALPM_SMBINB(sc, SMBHDATA); 607 608 /* read the 32-byte internal buffer */ 609 for (i = 0; i < len; i++) { 610 data = ALPM_SMBINB(sc, SMBHBLOCK); 611 if (i < *count) 612 buf[i] = data; 613 DELAY(2); 614 } 615 *count = len; 616 617 error: 618 ALPM_DEBUG(kprintf("alpm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, *count, cmd, error)); 619 ALPM_UNLOCK(sc); 620 621 return (error); 622 } 623 624 static devclass_t alpm_devclass; 625 626 static device_method_t alpm_methods[] = { 627 /* device interface */ 628 DEVMETHOD(device_probe, alpm_probe), 629 DEVMETHOD(device_attach, alpm_attach), 630 DEVMETHOD(device_detach, alpm_detach), 631 632 /* smbus interface */ 633 DEVMETHOD(smbus_callback, alpm_callback), 634 DEVMETHOD(smbus_quick, alpm_quick), 635 DEVMETHOD(smbus_sendb, alpm_sendb), 636 DEVMETHOD(smbus_recvb, alpm_recvb), 637 DEVMETHOD(smbus_writeb, alpm_writeb), 638 DEVMETHOD(smbus_readb, alpm_readb), 639 DEVMETHOD(smbus_writew, alpm_writew), 640 DEVMETHOD(smbus_readw, alpm_readw), 641 DEVMETHOD(smbus_bwrite, alpm_bwrite), 642 DEVMETHOD(smbus_bread, alpm_bread), 643 644 DEVMETHOD_END 645 }; 646 647 static driver_t alpm_driver = { 648 "alpm", 649 alpm_methods, 650 sizeof(struct alpm_softc) 651 }; 652 653 DRIVER_MODULE(alpm, pci, alpm_driver, alpm_devclass, NULL, NULL); 654 DRIVER_MODULE(smbus, alpm, smbus_driver, smbus_devclass, NULL, NULL); 655 MODULE_DEPEND(alpm, pci, 1, 1, 1); 656 MODULE_DEPEND(alpm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 657 MODULE_VERSION(alpm, 1); 658