amdpm.c revision 1.14
1/* $OpenBSD: amdpm.c,v 1.14 2006/03/08 09:21:14 dlg Exp $ */ 2 3/* 4 * Copyright (c) 2006 Alexander Yurchenko <grange@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19/*- 20 * Copyright (c) 2002 The NetBSD Foundation, Inc. 21 * All rights reserved. 22 * 23 * This code is derived from software contributed to The NetBSD Foundation 24 * by Enami Tsugutomo. 25 * 26 * Redistribution and use in source and binary forms, with or without 27 * modification, are permitted provided that the following conditions 28 * are met: 29 * 1. Redistributions of source code must retain the above copyright 30 * notice, this list of conditions and the following disclaimer. 31 * 2. Redistributions in binary form must reproduce the above copyright 32 * notice, this list of conditions and the following disclaimer in the 33 * documentation and/or other materials provided with the distribution. 34 * 3. All advertising materials mentioning features or use of this software 35 * must display the following acknowledgement: 36 * This product includes software developed by the NetBSD 37 * Foundation, Inc. and its contributors. 38 * 4. Neither the name of The NetBSD Foundation nor the names of its 39 * contributors may be used to endorse or promote products derived 40 * from this software without specific prior written permission. 41 * 42 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 43 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 44 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 45 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 46 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 47 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 48 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 49 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 50 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 51 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 52 * POSSIBILITY OF SUCH DAMAGE. 53 */ 54 55#include <sys/param.h> 56#include <sys/systm.h> 57#include <sys/device.h> 58#include <sys/kernel.h> 59#include <sys/lock.h> 60#include <sys/proc.h> 61#include <sys/timeout.h> 62#ifdef __HAVE_TIMECOUNTER 63#include <sys/timetc.h> 64#endif 65 66#include <machine/bus.h> 67 68#include <dev/pci/pcivar.h> 69#include <dev/pci/pcireg.h> 70#include <dev/pci/pcidevs.h> 71 72#include <dev/pci/amdpmreg.h> 73 74#include <dev/rndvar.h> 75#include <dev/i2c/i2cvar.h> 76 77#ifdef AMDPM_DEBUG 78#define DPRINTF(x) printf x 79#else 80#define DPRINTF(x) 81#endif 82 83#define AMDPM_SMBUS_DELAY 100 84#define AMDPM_SMBUS_TIMEOUT 1 85 86#ifdef __HAVE_TIMECOUNTER 87u_int amdpm_get_timecount(struct timecounter *tc); 88 89#ifndef AMDPM_FREQUENCY 90#define AMDPM_FREQUENCY 3579545 91#endif 92 93static struct timecounter amdpm_timecounter = { 94 amdpm_get_timecount, /* get_timecount */ 95 0, /* no poll_pps */ 96 0xffffff, /* counter_mask */ 97 AMDPM_FREQUENCY, /* frequency */ 98 "AMDPM", /* name */ 99 1000 /* quality */ 100}; 101#endif 102 103struct amdpm_softc { 104 struct device sc_dev; 105 106 pci_chipset_tag_t sc_pc; 107 pcitag_t sc_tag; 108 109 bus_space_tag_t sc_iot; 110 bus_space_handle_t sc_ioh; /* PMxx space */ 111 bus_space_handle_t sc_i2c_ioh; /* I2C space */ 112 int sc_poll; 113 114 struct timeout sc_rnd_ch; 115 116 struct i2c_controller sc_i2c_tag; 117 struct lock sc_i2c_lock; 118 struct { 119 i2c_op_t op; 120 void *buf; 121 size_t len; 122 int flags; 123 volatile int error; 124 } sc_i2c_xfer; 125}; 126 127int amdpm_match(struct device *, void *, void *); 128void amdpm_attach(struct device *, struct device *, void *); 129void amdpm_rnd_callout(void *); 130 131int amdpm_i2c_acquire_bus(void *, int); 132void amdpm_i2c_release_bus(void *, int); 133int amdpm_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t, 134 void *, size_t, int); 135 136int amdpm_intr(void *); 137 138struct cfattach amdpm_ca = { 139 sizeof(struct amdpm_softc), amdpm_match, amdpm_attach 140}; 141 142struct cfdriver amdpm_cd = { 143 NULL, "amdpm", DV_DULL 144}; 145 146const struct pci_matchid amdpm_ids[] = { 147 { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_PBC756_PMC }, 148 { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_766_PMC }, 149 { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_PBC768_PMC }, 150 { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_8111_PMC }, 151 { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE_SMB } 152}; 153 154int 155amdpm_match(struct device *parent, void *match, void *aux) 156{ 157 return (pci_matchbyid(aux, amdpm_ids, 158 sizeof(amdpm_ids) / sizeof(amdpm_ids[0]))); 159} 160 161void 162amdpm_attach(struct device *parent, struct device *self, void *aux) 163{ 164 struct amdpm_softc *sc = (struct amdpm_softc *) self; 165 struct pci_attach_args *pa = aux; 166 struct i2cbus_attach_args iba; 167 pcireg_t cfg_reg, reg; 168 int i; 169 170 sc->sc_pc = pa->pa_pc; 171 sc->sc_tag = pa->pa_tag; 172 sc->sc_iot = pa->pa_iot; 173 sc->sc_poll = 1; /* XXX */ 174 175 176 if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_AMD) { 177 cfg_reg = pci_conf_read(pa->pa_pc, pa->pa_tag, AMDPM_CONFREG); 178 if ((cfg_reg & AMDPM_PMIOEN) == 0) { 179 printf(": PMxx space isn't enabled\n"); 180 return; 181 } 182 183 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, AMDPM_PMPTR); 184 if (bus_space_map(sc->sc_iot, AMDPM_PMBASE(reg), AMDPM_PMSIZE, 0, 185 &sc->sc_ioh)) { 186 printf(": failed to map PMxx space\n"); 187 return; 188 } 189 if (bus_space_subregion(sc->sc_iot, sc->sc_ioh, AMDPM_SMB_REGS, 190 AMDPM_SMB_SIZE, &sc->sc_i2c_ioh)) { 191 printf(": failed to map I2C subregion\n"); 192 return; 193 } 194 195#ifdef __HAVE_TIMECOUNTER 196 if ((cfg_reg & AMDPM_TMRRST) == 0 && 197 (cfg_reg & AMDPM_STOPTMR) == 0 && 198 PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_AMD_PBC768_PMC) { 199 printf(": %d-bit timer at %dHz", 200 (cfg_reg & AMDPM_TMR32) ? 32 : 24, 201 amdpm_timecounter.tc_frequency); 202 203 amdpm_timecounter.tc_priv = sc; 204 if (cfg_reg & AMDPM_TMR32) 205 amdpm_timecounter.tc_counter_mask = 0xffffffffu; 206 tc_init(&amdpm_timecounter); 207 } 208#endif 209 if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_AMD_PBC768_PMC || 210 PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_AMD_8111_PMC) { 211 if ((cfg_reg & AMDPM_RNGEN) ==0) { 212 pci_conf_write(pa->pa_pc, pa->pa_tag, 213 AMDPM_CONFREG, cfg_reg | AMDPM_RNGEN); 214 cfg_reg = pci_conf_read(pa->pa_pc, pa->pa_tag, 215 AMDPM_CONFREG); 216 } 217 if (cfg_reg & AMDPM_RNGEN) { 218 /* Check to see if we can read data from the RNG. */ 219 (void) bus_space_read_4(sc->sc_iot, sc->sc_ioh, 220 AMDPM_RNGDATA); 221 for (i = 1000; i--; ) { 222 if (bus_space_read_1(sc->sc_iot, 223 sc->sc_ioh, AMDPM_RNGSTAT) & 224 AMDPM_RNGDONE) 225 break; 226 DELAY(10); 227 } 228 if (bus_space_read_1(sc->sc_iot, sc->sc_ioh, 229 AMDPM_RNGSTAT) & AMDPM_RNGDONE) { 230 printf(": rng active"); 231 timeout_set(&sc->sc_rnd_ch, 232 amdpm_rnd_callout, sc); 233 amdpm_rnd_callout(sc); 234 } 235 } 236 } 237 } else if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_NVIDIA) { 238 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, NFPM_PMPTR); 239 if (bus_space_map(sc->sc_iot, AMDPM_PMBASE(reg), AMDPM_SMB_SIZE, 0, 240 &sc->sc_i2c_ioh)) { 241 printf(": failed to map I2C subregion\n"); 242 return; 243 } 244 } 245 printf("\n"); 246 247 /* Attach I2C bus */ 248 lockinit(&sc->sc_i2c_lock, PRIBIO | PCATCH, "iiclk", 0, 0); 249 sc->sc_i2c_tag.ic_cookie = sc; 250 sc->sc_i2c_tag.ic_acquire_bus = amdpm_i2c_acquire_bus; 251 sc->sc_i2c_tag.ic_release_bus = amdpm_i2c_release_bus; 252 sc->sc_i2c_tag.ic_exec = amdpm_i2c_exec; 253 254 bzero(&iba, sizeof(iba)); 255 iba.iba_name = "iic"; 256 iba.iba_tag = &sc->sc_i2c_tag; 257 config_found(self, &iba, iicbus_print); 258} 259 260void 261amdpm_rnd_callout(void *v) 262{ 263 struct amdpm_softc *sc = v; 264 u_int32_t reg; 265 266 if ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_RNGSTAT) & 267 AMDPM_RNGDONE) != 0) { 268 reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_RNGDATA); 269 add_true_randomness(reg); 270 } 271 timeout_add(&sc->sc_rnd_ch, 1); 272} 273 274#ifdef __HAVE_TIMECOUNTER 275u_int 276amdpm_get_timecount(struct timecounter *tc) 277{ 278 struct amdpm_softc *sc = tc->tc_priv; 279 u_int u2; 280#if 0 281 u_int u1, u3; 282#endif 283 284 u2 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_TMR); 285#if 0 286 u3 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_TMR); 287 do { 288 u1 = u2; 289 u2 = u3; 290 u3 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_TMR); 291 } while (u1 > u2 || u2 > u3); 292#endif 293 return (u2); 294} 295#endif 296 297int 298amdpm_i2c_acquire_bus(void *cookie, int flags) 299{ 300 struct amdpm_softc *sc = cookie; 301 302 if (cold || sc->sc_poll || (flags & I2C_F_POLL)) 303 return (0); 304 305 return (lockmgr(&sc->sc_i2c_lock, LK_EXCLUSIVE, NULL)); 306} 307 308void 309amdpm_i2c_release_bus(void *cookie, int flags) 310{ 311 struct amdpm_softc *sc = cookie; 312 313 if (cold || sc->sc_poll || (flags & I2C_F_POLL)) 314 return; 315 316 lockmgr(&sc->sc_i2c_lock, LK_RELEASE, NULL); 317} 318 319int 320amdpm_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, 321 const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags) 322{ 323 struct amdpm_softc *sc = cookie; 324 u_int8_t *b; 325 u_int16_t st, ctl, data; 326 int retries; 327 328 DPRINTF(("%s: exec: op %d, addr 0x%x, cmdlen %d, len %d, flags 0x%x\n", 329 sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags)); 330 331 /* Wait for bus to be idle */ 332 for (retries = 100; retries > 0; retries--) { 333 st = bus_space_read_2(sc->sc_iot, sc->sc_i2c_ioh, AMDPM_SMBSTAT); 334 if (!(st & AMDPM_SMBSTAT_BSY)) 335 break; 336 DELAY(AMDPM_SMBUS_DELAY); 337 } 338 DPRINTF(("%s: exec: st 0x%b\n", sc->sc_dev.dv_xname, st, 339 AMDPM_SMBSTAT_BITS)); 340 if (st & AMDPM_SMBSTAT_BSY) 341 return (1); 342 343 if (cold || sc->sc_poll) 344 flags |= I2C_F_POLL; 345 346 if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2) 347 return (1); 348 349 /* Setup transfer */ 350 sc->sc_i2c_xfer.op = op; 351 sc->sc_i2c_xfer.buf = buf; 352 sc->sc_i2c_xfer.len = len; 353 sc->sc_i2c_xfer.flags = flags; 354 sc->sc_i2c_xfer.error = 0; 355 356 /* Set slave address and transfer direction */ 357 bus_space_write_2(sc->sc_iot, sc->sc_i2c_ioh, AMDPM_SMBADDR, 358 AMDPM_SMBADDR_ADDR(addr) | 359 (I2C_OP_READ_P(op) ? AMDPM_SMBADDR_READ : 0)); 360 361 b = (void *)cmdbuf; 362 if (cmdlen > 0) 363 /* Set command byte */ 364 bus_space_write_1(sc->sc_iot, sc->sc_i2c_ioh, AMDPM_SMBCMD, b[0]); 365 366 if (I2C_OP_WRITE_P(op)) { 367 /* Write data */ 368 data = 0; 369 b = buf; 370 if (len > 0) 371 data = b[0]; 372 if (len > 1) 373 data |= ((u_int16_t)b[1] << 8); 374 if (len > 0) 375 bus_space_write_2(sc->sc_iot, sc->sc_i2c_ioh, 376 AMDPM_SMBDATA, data); 377 } 378 379 /* Set SMBus command */ 380 if (len == 0) 381 ctl = AMDPM_SMBCTL_CMD_BYTE; 382 else if (len == 1) 383 ctl = AMDPM_SMBCTL_CMD_BDATA; 384 else if (len == 2) 385 ctl = AMDPM_SMBCTL_CMD_WDATA; 386 387 if ((flags & I2C_F_POLL) == 0) 388 ctl |= AMDPM_SMBCTL_CYCEN; 389 390 /* Start transaction */ 391 ctl |= AMDPM_SMBCTL_START; 392 bus_space_write_2(sc->sc_iot, sc->sc_i2c_ioh, AMDPM_SMBCTL, ctl); 393 394 if (flags & I2C_F_POLL) { 395 /* Poll for completion */ 396 DELAY(AMDPM_SMBUS_DELAY); 397 for (retries = 1000; retries > 0; retries--) { 398 st = bus_space_read_2(sc->sc_iot, sc->sc_i2c_ioh, 399 AMDPM_SMBSTAT); 400 if ((st & AMDPM_SMBSTAT_HBSY) == 0) 401 break; 402 DELAY(AMDPM_SMBUS_DELAY); 403 } 404 if (st & AMDPM_SMBSTAT_HBSY) 405 goto timeout; 406 amdpm_intr(sc); 407 } else { 408 /* Wait for interrupt */ 409 if (tsleep(sc, PRIBIO, "iicexec", AMDPM_SMBUS_TIMEOUT * hz)) 410 goto timeout; 411 } 412 413 if (sc->sc_i2c_xfer.error) 414 return (1); 415 416 return (0); 417 418timeout: 419 /* 420 * Transfer timeout. Kill the transaction and clear status bits. 421 */ 422 printf("%s: timeout, status 0x%b\n", sc->sc_dev.dv_xname, st, 423 AMDPM_SMBSTAT_BITS); 424 bus_space_write_2(sc->sc_iot, sc->sc_i2c_ioh, AMDPM_SMBCTL, 425 AMDPM_SMBCTL_ABORT); 426 DELAY(AMDPM_SMBUS_DELAY); 427 st = bus_space_read_2(sc->sc_iot, sc->sc_i2c_ioh, AMDPM_SMBSTAT); 428 if ((st & AMDPM_SMBSTAT_ABRT) == 0) 429 printf("%s: transaction abort failed, status 0x%b\n", 430 sc->sc_dev.dv_xname, st, AMDPM_SMBSTAT_BITS); 431 bus_space_write_2(sc->sc_iot, sc->sc_i2c_ioh, AMDPM_SMBSTAT, st); 432 return (1); 433} 434 435int 436amdpm_intr(void *arg) 437{ 438 struct amdpm_softc *sc = arg; 439 u_int16_t st, data; 440 u_int8_t *b; 441 size_t len; 442 443 /* Read status */ 444 st = bus_space_read_2(sc->sc_iot, sc->sc_i2c_ioh, AMDPM_SMBSTAT); 445 if ((st & AMDPM_SMBSTAT_HBSY) != 0 || (st & (AMDPM_SMBSTAT_ABRT | 446 AMDPM_SMBSTAT_COL | AMDPM_SMBSTAT_PRERR | AMDPM_SMBSTAT_CYC | 447 AMDPM_SMBSTAT_TO | AMDPM_SMBSTAT_SNP | AMDPM_SMBSTAT_SLV | 448 AMDPM_SMBSTAT_SMBA)) == 0) 449 /* Interrupt was not for us */ 450 return (0); 451 452 DPRINTF(("%s: intr: st 0x%b\n", sc->sc_dev.dv_xname, st, 453 AMDPM_SMBSTAT_BITS)); 454 455 /* Clear status bits */ 456 bus_space_write_2(sc->sc_iot, sc->sc_i2c_ioh, AMDPM_SMBSTAT, st); 457 458 /* Check for errors */ 459 if (st & (AMDPM_SMBSTAT_COL | AMDPM_SMBSTAT_PRERR | 460 AMDPM_SMBSTAT_TO)) { 461 sc->sc_i2c_xfer.error = 1; 462 goto done; 463 } 464 465 if (st & AMDPM_SMBSTAT_CYC) { 466 if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op)) 467 goto done; 468 469 /* Read data */ 470 b = sc->sc_i2c_xfer.buf; 471 len = sc->sc_i2c_xfer.len; 472 if (len > 0) { 473 data = bus_space_read_2(sc->sc_iot, sc->sc_i2c_ioh, 474 AMDPM_SMBDATA); 475 b[0] = data & 0xff; 476 } 477 if (len > 1) 478 b[1] = (data >> 8) & 0xff; 479 } 480 481done: 482 if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0) 483 wakeup(sc); 484 return (1); 485} 486