amdsmb.c revision 175533
1/*- 2 * Copyright (c) 2005 Ruslan Ermilov 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#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sys/pci/amdsmb.c 175533 2008-01-21 13:26:33Z ru $"); 29 30#include <sys/param.h> 31#include <sys/bus.h> 32#include <sys/kernel.h> 33#include <sys/lock.h> 34#include <sys/module.h> 35#include <sys/mutex.h> 36#include <sys/systm.h> 37 38#include <machine/bus.h> 39#include <machine/resource.h> 40#include <sys/rman.h> 41 42#include <dev/pci/pcivar.h> 43#include <dev/pci/pcireg.h> 44 45#include <dev/smbus/smbconf.h> 46#include "smbus_if.h" 47 48#define AMDSMB_DEBUG(x) if (amdsmb_debug) (x) 49 50#ifdef DEBUG 51static int amdsmb_debug = 1; 52#else 53static int amdsmb_debug = 0; 54#endif 55 56#define AMDSMB_VENDORID_AMD 0x1022 57#define AMDSMB_DEVICEID_AMD8111_SMB2 0x746a 58 59/* 60 * ACPI 3.0, Chapter 12, Embedded Controller Interface. 61 */ 62#define EC_DATA 0x00 /* data register */ 63#define EC_SC 0x04 /* status of controller */ 64#define EC_CMD 0x04 /* command register */ 65 66#define EC_SC_IBF 0x02 /* data ready for embedded controller */ 67#define EC_SC_OBF 0x01 /* data ready for host */ 68#define EC_CMD_WR 0x81 /* write EC */ 69#define EC_CMD_RD 0x80 /* read EC */ 70 71/* 72 * ACPI 3.0, Chapter 12, SMBus Host Controller Interface. 73 */ 74#define SMB_PRTCL 0x00 /* protocol */ 75#define SMB_STS 0x01 /* status */ 76#define SMB_ADDR 0x02 /* address */ 77#define SMB_CMD 0x03 /* command */ 78#define SMB_DATA 0x04 /* 32 data registers */ 79#define SMB_BCNT 0x24 /* number of data bytes */ 80#define SMB_ALRM_A 0x25 /* alarm address */ 81#define SMB_ALRM_D 0x26 /* 2 bytes alarm data */ 82 83#define SMB_STS_DONE 0x80 84#define SMB_STS_ALRM 0x40 85#define SMB_STS_RES 0x20 86#define SMB_STS_STATUS 0x1f 87#define SMB_STS_OK 0x00 /* OK */ 88#define SMB_STS_UF 0x07 /* Unknown Failure */ 89#define SMB_STS_DANA 0x10 /* Device Address Not Acknowledged */ 90#define SMB_STS_DED 0x11 /* Device Error Detected */ 91#define SMB_STS_DCAD 0x12 /* Device Command Access Denied */ 92#define SMB_STS_UE 0x13 /* Unknown Error */ 93#define SMB_STS_DAD 0x17 /* Device Access Denied */ 94#define SMB_STS_T 0x18 /* Timeout */ 95#define SMB_STS_HUP 0x19 /* Host Unsupported Protocol */ 96#define SMB_STS_B 0x1a /* Busy */ 97#define SMB_STS_PEC 0x1f /* PEC (CRC-8) Error */ 98 99#define SMB_PRTCL_WRITE 0x00 100#define SMB_PRTCL_READ 0x01 101#define SMB_PRTCL_QUICK 0x02 102#define SMB_PRTCL_BYTE 0x04 103#define SMB_PRTCL_BYTE_DATA 0x06 104#define SMB_PRTCL_WORD_DATA 0x08 105#define SMB_PRTCL_BLOCK_DATA 0x0a 106#define SMB_PRTCL_PROC_CALL 0x0c 107#define SMB_PRTCL_BLOCK_PROC_CALL 0x0d 108#define SMB_PRTCL_PEC 0x80 109 110struct amdsmb_softc { 111 int rid; 112 struct resource *res; 113 bus_space_tag_t smbst; 114 bus_space_handle_t smbsh; 115 device_t smbus; 116 struct mtx lock; 117}; 118 119#define AMDSMB_LOCK(amdsmb) mtx_lock(&(amdsmb)->lock) 120#define AMDSMB_UNLOCK(amdsmb) mtx_unlock(&(amdsmb)->lock) 121#define AMDSMB_LOCK_ASSERT(amdsmb) mtx_assert(&(amdsmb)->lock, MA_OWNED) 122 123#define AMDSMB_ECINB(amdsmb, register) \ 124 (bus_space_read_1(amdsmb->smbst, amdsmb->smbsh, register)) 125#define AMDSMB_ECOUTB(amdsmb, register, value) \ 126 (bus_space_write_1(amdsmb->smbst, amdsmb->smbsh, register, value)) 127 128static int amdsmb_detach(device_t dev); 129 130static int 131amdsmb_probe(device_t dev) 132{ 133 u_int16_t vid; 134 u_int16_t did; 135 136 vid = pci_get_vendor(dev); 137 did = pci_get_device(dev); 138 139 if (vid == AMDSMB_VENDORID_AMD) { 140 switch(did) { 141 case AMDSMB_DEVICEID_AMD8111_SMB2: 142 device_set_desc(dev, "AMD-8111 SMBus 2.0 Controller"); 143 return (BUS_PROBE_DEFAULT); 144 } 145 } 146 147 return (ENXIO); 148} 149 150static int 151amdsmb_attach(device_t dev) 152{ 153 struct amdsmb_softc *amdsmb_sc = device_get_softc(dev); 154 155 /* Allocate I/O space */ 156 amdsmb_sc->rid = PCIR_BAR(0); 157 158 amdsmb_sc->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, 159 &amdsmb_sc->rid, RF_ACTIVE); 160 161 if (amdsmb_sc->res == NULL) { 162 device_printf(dev, "could not map i/o space\n"); 163 return (ENXIO); 164 } 165 166 amdsmb_sc->smbst = rman_get_bustag(amdsmb_sc->res); 167 amdsmb_sc->smbsh = rman_get_bushandle(amdsmb_sc->res); 168 mtx_init(&amdsmb_sc->lock, device_get_nameunit(dev), "amdsmb", MTX_DEF); 169 170 /* Allocate a new smbus device */ 171 amdsmb_sc->smbus = device_add_child(dev, "smbus", -1); 172 if (!amdsmb_sc->smbus) { 173 amdsmb_detach(dev); 174 return (EINVAL); 175 } 176 177 bus_generic_attach(dev); 178 179 return (0); 180} 181 182static int 183amdsmb_detach(device_t dev) 184{ 185 struct amdsmb_softc *amdsmb_sc = device_get_softc(dev); 186 187 if (amdsmb_sc->smbus) { 188 device_delete_child(dev, amdsmb_sc->smbus); 189 amdsmb_sc->smbus = NULL; 190 } 191 192 mtx_destroy(&amdsmb_sc->lock); 193 if (amdsmb_sc->res) 194 bus_release_resource(dev, SYS_RES_IOPORT, amdsmb_sc->rid, 195 amdsmb_sc->res); 196 197 return (0); 198} 199 200static int 201amdsmb_callback(device_t dev, int index, void *data) 202{ 203 int error = 0; 204 205 switch (index) { 206 case SMB_REQUEST_BUS: 207 case SMB_RELEASE_BUS: 208 break; 209 default: 210 error = EINVAL; 211 } 212 213 return (error); 214} 215 216static int 217amdsmb_ec_wait_write(struct amdsmb_softc *sc) 218{ 219 int timeout = 500; 220 221 while (timeout-- && AMDSMB_ECINB(sc, EC_SC) & EC_SC_IBF) 222 DELAY(1); 223 if (timeout == 0) { 224 device_printf(sc->smbus, "timeout waiting for IBF to clear\n"); 225 return (1); 226 } 227 return (0); 228} 229 230static int 231amdsmb_ec_wait_read(struct amdsmb_softc *sc) 232{ 233 int timeout = 500; 234 235 while (timeout-- && ~AMDSMB_ECINB(sc, EC_SC) & EC_SC_OBF) 236 DELAY(1); 237 if (timeout == 0) { 238 device_printf(sc->smbus, "timeout waiting for OBF to set\n"); 239 return (1); 240 } 241 return (0); 242} 243 244static int 245amdsmb_ec_read(struct amdsmb_softc *sc, u_char addr, u_char *data) 246{ 247 248 AMDSMB_LOCK_ASSERT(sc); 249 if (amdsmb_ec_wait_write(sc)) 250 return (1); 251 AMDSMB_ECOUTB(sc, EC_CMD, EC_CMD_RD); 252 253 if (amdsmb_ec_wait_write(sc)) 254 return (1); 255 AMDSMB_ECOUTB(sc, EC_DATA, addr); 256 257 if (amdsmb_ec_wait_read(sc)) 258 return (1); 259 *data = AMDSMB_ECINB(sc, EC_DATA); 260 261 return (0); 262} 263 264static int 265amdsmb_ec_write(struct amdsmb_softc *sc, u_char addr, u_char data) 266{ 267 268 AMDSMB_LOCK_ASSERT(sc); 269 if (amdsmb_ec_wait_write(sc)) 270 return (1); 271 AMDSMB_ECOUTB(sc, EC_CMD, EC_CMD_WR); 272 273 if (amdsmb_ec_wait_write(sc)) 274 return (1); 275 AMDSMB_ECOUTB(sc, EC_DATA, addr); 276 277 if (amdsmb_ec_wait_write(sc)) 278 return (1); 279 AMDSMB_ECOUTB(sc, EC_DATA, data); 280 281 return (0); 282} 283 284static int 285amdsmb_wait(struct amdsmb_softc *sc) 286{ 287 u_char sts, temp; 288 int error, count; 289 290 AMDSMB_LOCK_ASSERT(sc); 291 amdsmb_ec_read(sc, SMB_PRTCL, &temp); 292 if (temp != 0) 293 { 294 count = 10000; 295 do { 296 DELAY(500); 297 amdsmb_ec_read(sc, SMB_PRTCL, &temp); 298 } while (temp != 0 && count--); 299 if (count == 0) 300 return (SMB_ETIMEOUT); 301 } 302 303 amdsmb_ec_read(sc, SMB_STS, &sts); 304 sts &= SMB_STS_STATUS; 305 AMDSMB_DEBUG(printf("amdsmb: STS=0x%x\n", sts)); 306 307 switch (sts) { 308 case SMB_STS_OK: 309 error = SMB_ENOERR; 310 break; 311 case SMB_STS_DANA: 312 error = SMB_ENOACK; 313 break; 314 case SMB_STS_B: 315 error = SMB_EBUSY; 316 break; 317 case SMB_STS_T: 318 error = SMB_ETIMEOUT; 319 break; 320 case SMB_STS_DCAD: 321 case SMB_STS_DAD: 322 case SMB_STS_HUP: 323 error = SMB_ENOTSUPP; 324 break; 325 default: 326 error = SMB_EBUSERR; 327 break; 328 } 329 330 return (error); 331} 332 333static int 334amdsmb_quick(device_t dev, u_char slave, int how) 335{ 336 struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev); 337 u_char protocol; 338 int error; 339 340 protocol = SMB_PRTCL_QUICK; 341 342 switch (how) { 343 case SMB_QWRITE: 344 protocol |= SMB_PRTCL_WRITE; 345 AMDSMB_DEBUG(printf("amdsmb: QWRITE to 0x%x", slave)); 346 break; 347 case SMB_QREAD: 348 protocol |= SMB_PRTCL_READ; 349 AMDSMB_DEBUG(printf("amdsmb: QREAD to 0x%x", slave)); 350 break; 351 default: 352 panic("%s: unknown QUICK command (%x)!", __func__, how); 353 } 354 355 AMDSMB_LOCK(sc); 356 amdsmb_ec_write(sc, SMB_ADDR, slave); 357 amdsmb_ec_write(sc, SMB_PRTCL, protocol); 358 359 error = amdsmb_wait(sc); 360 361 AMDSMB_DEBUG(printf(", error=0x%x\n", error)); 362 AMDSMB_UNLOCK(sc); 363 364 return (error); 365} 366 367static int 368amdsmb_sendb(device_t dev, u_char slave, char byte) 369{ 370 struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev); 371 int error; 372 373 AMDSMB_LOCK(sc); 374 amdsmb_ec_write(sc, SMB_CMD, byte); 375 amdsmb_ec_write(sc, SMB_ADDR, slave); 376 amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BYTE); 377 378 error = amdsmb_wait(sc); 379 380 AMDSMB_DEBUG(printf("amdsmb: SENDB to 0x%x, byte=0x%x, error=0x%x\n", 381 slave, byte, error)); 382 AMDSMB_UNLOCK(sc); 383 384 return (error); 385} 386 387static int 388amdsmb_recvb(device_t dev, u_char slave, char *byte) 389{ 390 struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev); 391 int error; 392 393 AMDSMB_LOCK(sc); 394 amdsmb_ec_write(sc, SMB_ADDR, slave); 395 amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BYTE); 396 397 if ((error = amdsmb_wait(sc)) == SMB_ENOERR) 398 amdsmb_ec_read(sc, SMB_DATA, byte); 399 400 AMDSMB_DEBUG(printf("amdsmb: RECVB from 0x%x, byte=0x%x, error=0x%x\n", 401 slave, *byte, error)); 402 AMDSMB_UNLOCK(sc); 403 404 return (error); 405} 406 407static int 408amdsmb_writeb(device_t dev, u_char slave, char cmd, char byte) 409{ 410 struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev); 411 int error; 412 413 AMDSMB_LOCK(sc); 414 amdsmb_ec_write(sc, SMB_CMD, cmd); 415 amdsmb_ec_write(sc, SMB_DATA, byte); 416 amdsmb_ec_write(sc, SMB_ADDR, slave); 417 amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BYTE_DATA); 418 419 error = amdsmb_wait(sc); 420 421 AMDSMB_DEBUG(printf("amdsmb: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, " 422 "error=0x%x\n", slave, cmd, byte, error)); 423 AMDSMB_UNLOCK(sc); 424 425 return (error); 426} 427 428static int 429amdsmb_readb(device_t dev, u_char slave, char cmd, char *byte) 430{ 431 struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev); 432 int error; 433 434 AMDSMB_LOCK(sc); 435 amdsmb_ec_write(sc, SMB_CMD, cmd); 436 amdsmb_ec_write(sc, SMB_ADDR, slave); 437 amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BYTE_DATA); 438 439 if ((error = amdsmb_wait(sc)) == SMB_ENOERR) 440 amdsmb_ec_read(sc, SMB_DATA, byte); 441 442 AMDSMB_DEBUG(printf("amdsmb: READB from 0x%x, cmd=0x%x, byte=0x%x, " 443 "error=0x%x\n", slave, cmd, (unsigned char)*byte, error)); 444 AMDSMB_UNLOCK(sc); 445 446 return (error); 447} 448 449static int 450amdsmb_writew(device_t dev, u_char slave, char cmd, short word) 451{ 452 struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev); 453 int error; 454 455 AMDSMB_LOCK(sc); 456 amdsmb_ec_write(sc, SMB_CMD, cmd); 457 amdsmb_ec_write(sc, SMB_DATA, word); 458 amdsmb_ec_write(sc, SMB_DATA + 1, word >> 8); 459 amdsmb_ec_write(sc, SMB_ADDR, slave); 460 amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_WORD_DATA); 461 462 error = amdsmb_wait(sc); 463 464 AMDSMB_DEBUG(printf("amdsmb: WRITEW to 0x%x, cmd=0x%x, word=0x%x, " 465 "error=0x%x\n", slave, cmd, word, error)); 466 AMDSMB_UNLOCK(sc); 467 468 return (error); 469} 470 471static int 472amdsmb_readw(device_t dev, u_char slave, char cmd, short *word) 473{ 474 struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev); 475 u_char temp[2]; 476 int error; 477 478 AMDSMB_LOCK(sc); 479 amdsmb_ec_write(sc, SMB_CMD, cmd); 480 amdsmb_ec_write(sc, SMB_ADDR, slave); 481 amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_WORD_DATA); 482 483 if ((error = amdsmb_wait(sc)) == SMB_ENOERR) { 484 amdsmb_ec_read(sc, SMB_DATA + 0, &temp[0]); 485 amdsmb_ec_read(sc, SMB_DATA + 1, &temp[1]); 486 *word = temp[0] | (temp[1] << 8); 487 } 488 489 AMDSMB_DEBUG(printf("amdsmb: READW from 0x%x, cmd=0x%x, word=0x%x, " 490 "error=0x%x\n", slave, cmd, (unsigned short)*word, error)); 491 AMDSMB_UNLOCK(sc); 492 493 return (error); 494} 495 496static int 497amdsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) 498{ 499 struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev); 500 u_char i; 501 int error; 502 503 if (count < 1 || count > 32) 504 return (SMB_EINVAL); 505 506 AMDSMB_LOCK(sc); 507 amdsmb_ec_write(sc, SMB_CMD, cmd); 508 amdsmb_ec_write(sc, SMB_BCNT, count); 509 for (i = 0; i < count; i++) 510 amdsmb_ec_write(sc, SMB_DATA + i, buf[i]); 511 amdsmb_ec_write(sc, SMB_ADDR, slave); 512 amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BLOCK_DATA); 513 514 error = amdsmb_wait(sc); 515 516 AMDSMB_DEBUG(printf("amdsmb: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, " 517 "error=0x%x", slave, count, cmd, error)); 518 AMDSMB_UNLOCK(sc); 519 520 return (error); 521} 522 523static int 524amdsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf) 525{ 526 struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev); 527 u_char data, len, i; 528 int error; 529 530 if (*count < 1 || *count > 32) 531 return (SMB_EINVAL); 532 533 AMDSMB_LOCK(sc); 534 amdsmb_ec_write(sc, SMB_CMD, cmd); 535 amdsmb_ec_write(sc, SMB_ADDR, slave); 536 amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BLOCK_DATA); 537 538 if ((error = amdsmb_wait(sc)) == SMB_ENOERR) { 539 amdsmb_ec_read(sc, SMB_BCNT, &len); 540 for (i = 0; i < len; i++) { 541 amdsmb_ec_read(sc, SMB_DATA + i, &data); 542 if (i < *count) 543 buf[i] = data; 544 } 545 *count = len; 546 } 547 548 AMDSMB_DEBUG(printf("amdsmb: READBLK to 0x%x, count=0x%x, cmd=0x%x, " 549 "error=0x%x", slave, *count, cmd, error)); 550 AMDSMB_UNLOCK(sc); 551 552 return (error); 553} 554 555static device_method_t amdsmb_methods[] = { 556 /* Device interface */ 557 DEVMETHOD(device_probe, amdsmb_probe), 558 DEVMETHOD(device_attach, amdsmb_attach), 559 DEVMETHOD(device_detach, amdsmb_detach), 560 561 /* SMBus interface */ 562 DEVMETHOD(smbus_callback, amdsmb_callback), 563 DEVMETHOD(smbus_quick, amdsmb_quick), 564 DEVMETHOD(smbus_sendb, amdsmb_sendb), 565 DEVMETHOD(smbus_recvb, amdsmb_recvb), 566 DEVMETHOD(smbus_writeb, amdsmb_writeb), 567 DEVMETHOD(smbus_readb, amdsmb_readb), 568 DEVMETHOD(smbus_writew, amdsmb_writew), 569 DEVMETHOD(smbus_readw, amdsmb_readw), 570 DEVMETHOD(smbus_bwrite, amdsmb_bwrite), 571 DEVMETHOD(smbus_bread, amdsmb_bread), 572 573 { 0, 0 } 574}; 575 576static devclass_t amdsmb_devclass; 577 578static driver_t amdsmb_driver = { 579 "amdsmb", 580 amdsmb_methods, 581 sizeof(struct amdsmb_softc), 582}; 583 584DRIVER_MODULE(amdsmb, pci, amdsmb_driver, amdsmb_devclass, 0, 0); 585DRIVER_MODULE(smbus, amdsmb, smbus_driver, smbus_devclass, 0, 0); 586 587MODULE_DEPEND(amdsmb, pci, 1, 1, 1); 588MODULE_DEPEND(amdsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 589MODULE_VERSION(amdsmb, 1); 590