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