alpm.c revision 298955
12061Sjkh/*- 210760Sache * Copyright (c) 1998, 1999, 2001 Nicolas Souchu 32061Sjkh * All rights reserved. 42061Sjkh * 58854Srgrimes * Redistribution and use in source and binary forms, with or without 62061Sjkh * modification, are permitted provided that the following conditions 72061Sjkh * are met: 83197Scsgr * 1. Redistributions of source code must retain the above copyright 93197Scsgr * notice, this list of conditions and the following disclaimer. 102061Sjkh * 2. Redistributions in binary form must reproduce the above copyright 112160Scsgr * notice, this list of conditions and the following disclaimer in the 122834Swollman * documentation and/or other materials provided with the distribution. 132061Sjkh * 142061Sjkh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 152160Scsgr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 161594Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 172061Sjkh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 182061Sjkh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 191594Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 207407Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 217407Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 227108Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 237108Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 247108Sphk * SUCH DAMAGE. 257407Srgrimes */ 267407Srgrimes 277407Srgrimes/* 287108Sphk * Power Management support for the Acer M15x3 chipsets 292061Sjkh */ 302061Sjkh 312061Sjkh#include <sys/cdefs.h> 322061Sjkh__FBSDID("$FreeBSD: head/sys/dev/alpm/alpm.c 298955 2016-05-03 03:41:25Z pfg $"); 332061Sjkh 342061Sjkh#include <sys/param.h> 352061Sjkh#include <sys/bus.h> 362061Sjkh#include <sys/kernel.h> 372061Sjkh#include <sys/lock.h> 382061Sjkh#include <sys/module.h> 392061Sjkh#include <sys/mutex.h> 402061Sjkh#include <sys/systm.h> 413197Scsgr 422626Scsgr#include <machine/bus.h> 432626Scsgr#include <machine/resource.h> 442061Sjkh#include <sys/rman.h> 452061Sjkh 462061Sjkh#include <dev/pci/pcivar.h> 472061Sjkh#include <dev/pci/pcireg.h> 482061Sjkh 492061Sjkh#include <dev/smbus/smbconf.h> 502061Sjkh#include "smbus_if.h" 512061Sjkh 522061Sjkh#define ALPM_DEBUG(x) if (alpm_debug) (x) 532061Sjkh 542061Sjkh#ifdef DEBUG 552061Sjkhstatic int alpm_debug = 1; 562061Sjkh#else 572061Sjkhstatic int alpm_debug = 0; 582061Sjkh#endif 592061Sjkh 602061Sjkh#define ACER_M1543_PMU_ID 0x710110b9 612061Sjkh 622834Swollman/* 632834Swollman * I/O registers offsets - the base address is programmed via the 642834Swollman * SMBBA PCI configuration register 652834Swollman */ 662834Swollman#define SMBSTS 0x0 /* SMBus host/slave status register */ 672834Swollman#define SMBCMD 0x1 /* SMBus host/slave command register */ 681594Srgrimes#define SMBSTART 0x2 /* start to generate programmed cycle */ 694486Sphk#define SMBHADDR 0x3 /* host address register */ 704486Sphk#define SMBHDATA 0x4 /* data A register for host controller */ 714486Sphk#define SMBHDATB 0x5 /* data B register for host controller */ 724486Sphk#define SMBHBLOCK 0x6 /* block register for host controller */ 734486Sphk#define SMBHCMD 0x7 /* command register for host controller */ 742061Sjkh 752061Sjkh/* SMBHADDR mask. */ 762061Sjkh#define LSB 0x1 /* XXX: Better name: Read/Write? */ 772061Sjkh 782061Sjkh/* SMBSTS masks */ 792061Sjkh#define TERMINATE 0x80 802061Sjkh#define BUS_COLLI 0x40 812061Sjkh#define DEVICE_ERR 0x20 822061Sjkh#define SMI_I_STS 0x10 832061Sjkh#define HST_BSY 0x08 842061Sjkh#define IDL_STS 0x04 852061Sjkh#define HSTSLV_STS 0x02 862061Sjkh#define HSTSLV_BSY 0x01 872061Sjkh 882061Sjkh/* SMBCMD masks */ 892061Sjkh#define SMB_BLK_CLR 0x80 902061Sjkh#define T_OUT_CMD 0x08 918854Srgrimes#define ABORT_HOST 0x04 922061Sjkh 932061Sjkh/* SMBus commands */ 942061Sjkh#define SMBQUICK 0x00 955511Sjkh#define SMBSRBYTE 0x10 /* send/receive byte */ 962061Sjkh#define SMBWRBYTE 0x20 /* write/read byte */ 972061Sjkh#define SMBWRWORD 0x30 /* write/read word */ 982061Sjkh#define SMBWRBLOCK 0x40 /* write/read block */ 992061Sjkh 1002061Sjkh/* PCI configuration registers and masks 1012061Sjkh */ 1022061Sjkh#define COM 0x4 1033030Srgrimes#define COM_ENABLE_IO 0x1 1042061Sjkh 1053030Srgrimes#define SMBBA PCIR_BAR(1) 1062061Sjkh 1076722Sphk#define ATPC 0x5b 1082061Sjkh#define ATPC_SMBCTRL 0x04 /* XX linux has this as 0x6 */ 1092302Spaul 1102302Spaul#define SMBHSI 0xe0 1112302Spaul#define SMBHSI_SLAVE 0x2 1122302Spaul#define SMBHSI_HOST 0x1 1132302Spaul 1142302Spaul#define SMBHCBC 0xe2 11510760Sache#define SMBHCBC_CLOCK 0x70 11610760Sache 1172302Spaul#define SMBCLOCK_149K 0x0 11810760Sache#define SMBCLOCK_74K 0x20 11910760Sache#define SMBCLOCK_37K 0x40 12010760Sache#define SMBCLOCK_223K 0x80 12110760Sache#define SMBCLOCK_111K 0xa0 1222302Spaul#define SMBCLOCK_55K 0xc0 1232302Spaul 1242302Spaulstruct alpm_softc { 1252302Spaul int base; 1262302Spaul struct resource *res; 1272302Spaul bus_space_tag_t smbst; 1282302Spaul bus_space_handle_t smbsh; 1292061Sjkh device_t smbus; 1302061Sjkh struct mtx lock; 1312061Sjkh}; 1322061Sjkh 1332061Sjkh#define ALPM_LOCK(alpm) mtx_lock(&(alpm)->lock) 1342061Sjkh#define ALPM_UNLOCK(alpm) mtx_unlock(&(alpm)->lock) 1352061Sjkh#define ALPM_LOCK_ASSERT(alpm) mtx_assert(&(alpm)->lock, MA_OWNED) 1362061Sjkh 1372061Sjkh#define ALPM_SMBINB(alpm,register) \ 1382061Sjkh (bus_space_read_1(alpm->smbst, alpm->smbsh, register)) 1392061Sjkh#define ALPM_SMBOUTB(alpm,register,value) \ 1402061Sjkh (bus_space_write_1(alpm->smbst, alpm->smbsh, register, value)) 1412061Sjkh 1422061Sjkhstatic int alpm_detach(device_t dev); 1432061Sjkh 1442061Sjkhstatic int 1452061Sjkhalpm_probe(device_t dev) 1462061Sjkh{ 1472061Sjkh 1482061Sjkh if (pci_get_devid(dev) == ACER_M1543_PMU_ID) { 1492061Sjkh device_set_desc(dev, "AcerLabs M15x3 Power Management Unit"); 1502061Sjkh 1512061Sjkh return (BUS_PROBE_DEFAULT); 1522061Sjkh } 1532061Sjkh 1542061Sjkh return (ENXIO); 1553626Swollman} 1563626Swollman 1573626Swollmanstatic int 1583626Swollmanalpm_attach(device_t dev) 1593626Swollman{ 1603626Swollman int rid; 1613626Swollman u_int32_t l; 1623626Swollman struct alpm_softc *alpm; 1633626Swollman 1643626Swollman alpm = device_get_softc(dev); 1653626Swollman 1667059Sroberto /* Unlock SMBIO base register access */ 1673626Swollman l = pci_read_config(dev, ATPC, 1); 1683626Swollman pci_write_config(dev, ATPC, l & ~ATPC_SMBCTRL, 1); 1693626Swollman 1703626Swollman /* 1713626Swollman * XX linux sets clock to 74k, should we? 1723626Swollman l = pci_read_config(dev, SMBHCBC, 1); 1733626Swollman l &= 0x1f; 1743626Swollman l |= SMBCLOCK_74K; 1753626Swollman pci_write_config(dev, SMBHCBC, l, 1); 1763626Swollman */ 1773626Swollman 1783626Swollman if (bootverbose || alpm_debug) { 1793626Swollman l = pci_read_config(dev, SMBHSI, 1); 1803626Swollman device_printf(dev, "%s/%s", 1813626Swollman (l & SMBHSI_HOST) ? "host":"nohost", 1823626Swollman (l & SMBHSI_SLAVE) ? "slave":"noslave"); 1833626Swollman 1843626Swollman l = pci_read_config(dev, SMBHCBC, 1); 1857446Ssos switch (l & SMBHCBC_CLOCK) { 1863626Swollman case SMBCLOCK_149K: 1873626Swollman printf(" 149K"); 1883626Swollman break; 1893626Swollman case SMBCLOCK_74K: 1903626Swollman printf(" 74K"); 1913626Swollman break; 1923626Swollman case SMBCLOCK_37K: 1932061Sjkh printf(" 37K"); 1942061Sjkh break; 1952061Sjkh case SMBCLOCK_223K: 1962061Sjkh printf(" 223K"); 1972061Sjkh break; 1982061Sjkh case SMBCLOCK_111K: 1992061Sjkh printf(" 111K"); 2002061Sjkh break; 2012061Sjkh case SMBCLOCK_55K: 2022061Sjkh printf(" 55K"); 2032061Sjkh break; 2042061Sjkh default: 2057130Srgrimes printf("unknown"); 2067130Srgrimes break; 2077130Srgrimes } 2082061Sjkh printf("\n"); 2092061Sjkh } 2104249Sache 2112685Srgrimes rid = SMBBA; 2126927Snate alpm->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 2132685Srgrimes RF_ACTIVE); 2143518Sache 2153197Scsgr if (alpm->res == NULL) { 2163197Scsgr device_printf(dev,"Could not allocate Bus space\n"); 2173197Scsgr return (ENXIO); 2182061Sjkh } 2192061Sjkh alpm->smbst = rman_get_bustag(alpm->res); 2202061Sjkh alpm->smbsh = rman_get_bushandle(alpm->res); 2212883Sphk mtx_init(&alpm->lock, device_get_nameunit(dev), "alpm", MTX_DEF); 2223429Sache 2233429Sache /* attach the smbus */ 2247281Srgrimes alpm->smbus = device_add_child(dev, "smbus", -1); 2253242Spaul if (alpm->smbus == NULL) { 2263242Spaul alpm_detach(dev); 2277171Sats return (EINVAL); 2282061Sjkh } 2293213Spst bus_generic_attach(dev); 2304942Sache 2315749Swollman return (0); 2325772Swollman} 2335865Sache 2345866Sachestatic int 2352061Sjkhalpm_detach(device_t dev) 2365366Snate{ 2375366Snate struct alpm_softc *alpm = device_get_softc(dev); 2386934Sse 2395366Snate if (alpm->smbus) { 2405366Snate device_delete_child(dev, alpm->smbus); 2417292Srgrimes alpm->smbus = NULL; 2427292Srgrimes } 2435366Snate mtx_destroy(&alpm->lock); 2445366Snate 2455366Snate if (alpm->res) 2465366Snate bus_release_resource(dev, SYS_RES_IOPORT, SMBBA, alpm->res); 2475366Snate 2485366Snate return (0); 2495772Swollman} 2505772Swollman 2515728Swollmanstatic int 2525728Swollmanalpm_callback(device_t dev, int index, void *data) 2535728Swollman{ 2545728Swollman int error = 0; 2555728Swollman 2565366Snate switch (index) { 2572061Sjkh case SMB_REQUEST_BUS: 2582061Sjkh case SMB_RELEASE_BUS: 2592061Sjkh /* ok, bus allocation accepted */ 2602061Sjkh break; 2612061Sjkh default: 2622061Sjkh error = EINVAL; 2632061Sjkh } 2642061Sjkh 2652061Sjkh return (error); 2668295Srgrimes} 2678295Srgrimes 2688295Srgrimesstatic int 2698295Srgrimesalpm_clear(struct alpm_softc *sc) 2708489Srgrimes{ 2718489Srgrimes ALPM_SMBOUTB(sc, SMBSTS, 0xff); 2728489Srgrimes DELAY(10); 2738489Srgrimes 2748489Srgrimes return (0); 2758489Srgrimes} 2768489Srgrimes 2778489Srgrimes#if 0 2788295Srgrimesstatic int 2792468Spaulalpm_abort(struct alpm_softc *sc) 2802061Sjkh{ 2812273Spaul ALPM_SMBOUTB(sc, SMBCMD, T_OUT_CMD | ABORT_HOST); 2822061Sjkh 2838295Srgrimes return (0); 2842160Scsgr} 2852160Scsgr#endif 2862160Scsgr 2872160Scsgrstatic int 2882279Spaulalpm_idle(struct alpm_softc *sc) 2894054Spst{ 2904054Spst u_char sts; 2912061Sjkh 2922061Sjkh sts = ALPM_SMBINB(sc, SMBSTS); 2932279Spaul 2942468Spaul ALPM_DEBUG(printf("alpm: idle? STS=0x%x\n", sts)); 2952468Spaul 2963197Scsgr return (sts & IDL_STS); 2972626Scsgr} 2982626Scsgr 2992626Scsgr/* 3002626Scsgr * Poll the SMBus controller 3012626Scsgr */ 3022626Scsgrstatic int 3032626Scsgralpm_wait(struct alpm_softc *sc) 3042626Scsgr{ 3052626Scsgr int count = 10000; 3068304Srgrimes u_char sts = 0; 3078304Srgrimes int error; 3088304Srgrimes 3098304Srgrimes /* wait for command to complete and SMBus controller is idle */ 3102061Sjkh while (count--) { 3112061Sjkh DELAY(10); 3122061Sjkh sts = ALPM_SMBINB(sc, SMBSTS); 31310479Sdg if (sts & SMI_I_STS) 3142061Sjkh break; 3152061Sjkh } 3162273Spaul 3172061Sjkh ALPM_DEBUG(printf("alpm: STS=0x%x\n", sts)); 3182061Sjkh 3192061Sjkh error = SMB_ENOERR; 32010479Sdg 32110479Sdg if (!count) 3222061Sjkh error |= SMB_ETIMEOUT; 3231594Srgrimes 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 339static int 340alpm_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 376static int 377alpm_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 402static int 403alpm_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 428static int 429alpm_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 455static int 456alpm_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 482static int 483alpm_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 510static int 511alpm_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 542static int 543alpm_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 583static int 584alpm_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 623error: 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 630static devclass_t alpm_devclass; 631 632static 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 653static driver_t alpm_driver = { 654 "alpm", 655 alpm_methods, 656 sizeof(struct alpm_softc) 657}; 658 659DRIVER_MODULE(alpm, pci, alpm_driver, alpm_devclass, 0, 0); 660DRIVER_MODULE(smbus, alpm, smbus_driver, smbus_devclass, 0, 0); 661MODULE_DEPEND(alpm, pci, 1, 1, 1); 662MODULE_DEPEND(alpm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 663MODULE_VERSION(alpm, 1); 664