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