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