1 /* $OpenBSD: amdpm.c,v 1.9 2006/01/05 10:43:15 grange 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 87 u_int amdpm_get_timecount(struct timecounter *tc); 88 89 #ifndef AMDPM_FREQUENCY 90 #define AMDPM_FREQUENCY 3579545 91 #endif 92 93 static 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 103 struct 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 int sc_poll; 112 113 struct timeout sc_rnd_ch; 114 #ifdef AMDPM_RND_COUNTERS 115 struct evcnt sc_rnd_hits; 116 struct evcnt sc_rnd_miss; 117 struct evcnt sc_rnd_data[256]; 118 #endif 119 120 struct i2c_controller sc_i2c_tag; 121 struct lock sc_i2c_lock; 122 struct { 123 i2c_op_t op; 124 void *buf; 125 size_t len; 126 int flags; 127 volatile int error; 128 } sc_i2c_xfer; 129 }; 130 131 int amdpm_match(struct device *, void *, void *); 132 void amdpm_attach(struct device *, struct device *, void *); 133 void amdpm_rnd_callout(void *); 134 135 int amdpm_i2c_acquire_bus(void *, int); 136 void amdpm_i2c_release_bus(void *, int); 137 int amdpm_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t, 138 void *, size_t, int); 139 140 int amdpm_intr(void *); 141 142 struct cfattach amdpm_ca = { 143 sizeof(struct amdpm_softc), amdpm_match, amdpm_attach 144 }; 145 146 struct cfdriver amdpm_cd = { 147 NULL, "amdpm", DV_DULL 148 }; 149 150 #ifdef AMDPM_RND_COUNTERS 151 #define AMDPM_RNDCNT_INCR(ev) (ev)->ev_count++ 152 #else 153 #define AMDPM_RNDCNT_INCR(ev) /* nothing */ 154 #endif 155 156 const struct pci_matchid amdpm_ids[] = { 157 { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_766_PMC }, 158 { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_PBC768_PMC } 159 }; 160 161 int 162 amdpm_match(struct device *parent, void *match, void *aux) 163 { 164 return (pci_matchbyid(aux, amdpm_ids, 165 sizeof(amdpm_ids) / sizeof(amdpm_ids[0]))); 166 } 167 168 void 169 amdpm_attach(struct device *parent, struct device *self, void *aux) 170 { 171 struct amdpm_softc *sc = (struct amdpm_softc *) self; 172 struct pci_attach_args *pa = aux; 173 struct i2cbus_attach_args iba; 174 struct timeval tv1, tv2; 175 pcireg_t cfg_reg, reg; 176 int i; 177 178 sc->sc_pc = pa->pa_pc; 179 sc->sc_tag = pa->pa_tag; 180 sc->sc_iot = pa->pa_iot; 181 sc->sc_poll = 1; /* XXX */ 182 183 cfg_reg = pci_conf_read(pa->pa_pc, pa->pa_tag, AMDPM_CONFREG); 184 if ((cfg_reg & AMDPM_PMIOEN) == 0) { 185 printf(": PMxx space isn't enabled\n"); 186 return; 187 } 188 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, AMDPM_PMPTR); 189 if (bus_space_map(sc->sc_iot, AMDPM_PMBASE(reg), AMDPM_PMSIZE, 190 0, &sc->sc_ioh)) { 191 printf(": failed to map PMxx space\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 210 if (cfg_reg & AMDPM_RNGEN) { 211 /* Check to see if we can read data from the RNG. */ 212 (void) bus_space_read_4(sc->sc_iot, sc->sc_ioh, 213 AMDPM_RNGDATA); 214 /* benchmark the RNG */ 215 microtime(&tv1); 216 for (i = 2 * 1024; i--; ) { 217 while(!(bus_space_read_1(sc->sc_iot, sc->sc_ioh, 218 AMDPM_RNGSTAT) & AMDPM_RNGDONE)) 219 ; 220 (void) bus_space_read_4(sc->sc_iot, sc->sc_ioh, 221 AMDPM_RNGDATA); 222 } 223 microtime(&tv2); 224 225 timersub(&tv2, &tv1, &tv1); 226 if (tv1.tv_sec) 227 tv1.tv_usec += 1000000 * tv1.tv_sec; 228 printf(": rng active, %dKb/sec", 8 * 1000000 / tv1.tv_usec); 229 230 #ifdef AMDPM_RND_COUNTERS 231 evcnt_attach_dynamic(&sc->sc_rnd_hits, EVCNT_TYPE_MISC, 232 NULL, sc->sc_dev.dv_xname, "rnd hits"); 233 evcnt_attach_dynamic(&sc->sc_rnd_miss, EVCNT_TYPE_MISC, 234 NULL, sc->sc_dev.dv_xname, "rnd miss"); 235 for (i = 0; i < 256; i++) { 236 evcnt_attach_dynamic(&sc->sc_rnd_data[i], 237 EVCNT_TYPE_MISC, NULL, sc->sc_dev.dv_xname, 238 "rnd data"); 239 } 240 #endif 241 timeout_set(&sc->sc_rnd_ch, amdpm_rnd_callout, sc); 242 amdpm_rnd_callout(sc); 243 } 244 245 /* Attach I2C bus */ 246 lockinit(&sc->sc_i2c_lock, PRIBIO | PCATCH, "iiclk", 0, 0); 247 sc->sc_i2c_tag.ic_cookie = sc; 248 sc->sc_i2c_tag.ic_acquire_bus = amdpm_i2c_acquire_bus; 249 sc->sc_i2c_tag.ic_release_bus = amdpm_i2c_release_bus; 250 sc->sc_i2c_tag.ic_exec = amdpm_i2c_exec; 251 252 bzero(&iba, sizeof(iba)); 253 iba.iba_name = "iic"; 254 iba.iba_tag = &sc->sc_i2c_tag; 255 config_found(self, &iba, iicbus_print); 256 257 printf("\n"); 258 } 259 260 void 261 amdpm_rnd_callout(void *v) 262 { 263 struct amdpm_softc *sc = v; 264 u_int32_t reg; 265 #ifdef AMDPM_RND_COUNTERS 266 int i; 267 #endif 268 269 if ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_RNGSTAT) & 270 AMDPM_RNGDONE) != 0) { 271 reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_RNGDATA); 272 add_true_randomness(reg); 273 #ifdef AMDPM_RND_COUNTERS 274 AMDPM_RNDCNT_INCR(&sc->sc_rnd_hits); 275 for (i = 0; i < sizeof(reg); i++, reg >>= NBBY) 276 AMDPM_RNDCNT_INCR(&sc->sc_rnd_data[reg & 0xff]); 277 #endif 278 } else 279 AMDPM_RNDCNT_INCR(&sc->sc_rnd_miss); 280 timeout_add(&sc->sc_rnd_ch, 1); 281 } 282 283 #ifdef __HAVE_TIMECOUNTER 284 u_int 285 amdpm_get_timecount(struct timecounter *tc) 286 { 287 struct amdpm_softc *sc = tc->tc_priv; 288 u_int u2; 289 #if 0 290 u_int u1, u3; 291 #endif 292 293 u2 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_TMR); 294 #if 0 295 u3 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_TMR); 296 do { 297 u1 = u2; 298 u2 = u3; 299 u3 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_TMR); 300 } while (u1 > u2 || u2 > u3); 301 #endif 302 return (u2); 303 } 304 #endif 305 306 int 307 amdpm_i2c_acquire_bus(void *cookie, int flags) 308 { 309 struct amdpm_softc *sc = cookie; 310 311 if (cold || sc->sc_poll || (flags & I2C_F_POLL)) 312 return (0); 313 314 return (lockmgr(&sc->sc_i2c_lock, LK_EXCLUSIVE, NULL)); 315 } 316 317 void 318 amdpm_i2c_release_bus(void *cookie, int flags) 319 { 320 struct amdpm_softc *sc = cookie; 321 322 if (cold || sc->sc_poll || (flags & I2C_F_POLL)) 323 return; 324 325 lockmgr(&sc->sc_i2c_lock, LK_RELEASE, NULL); 326 } 327 328 int 329 amdpm_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, 330 const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags) 331 { 332 struct amdpm_softc *sc = cookie; 333 u_int8_t *b; 334 u_int16_t st, ctl, data; 335 int retries; 336 337 DPRINTF(("%s: exec: op %d, addr 0x%x, cmdlen %d, len %d, flags 0x%x\n", 338 sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags)); 339 340 /* Check if there's a transfer already running */ 341 st = bus_space_read_2(sc->sc_iot, sc->sc_ioh, AMDPM_SMBSTAT); 342 DPRINTF(("%s: exec: st 0x%b\n", sc->sc_dev.dv_xname, st, 343 AMDPM_SMBSTAT_BITS)); 344 if (st & AMDPM_SMBSTAT_BSY) 345 return (1); 346 347 if (cold || sc->sc_poll) 348 flags |= I2C_F_POLL; 349 350 if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2) 351 return (1); 352 353 /* Setup transfer */ 354 sc->sc_i2c_xfer.op = op; 355 sc->sc_i2c_xfer.buf = buf; 356 sc->sc_i2c_xfer.len = len; 357 sc->sc_i2c_xfer.flags = flags; 358 sc->sc_i2c_xfer.error = 0; 359 360 /* Set slave address and transfer direction */ 361 bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_SMBADDR, 362 AMDPM_SMBADDR_ADDR(addr) | 363 (I2C_OP_READ_P(op) ? AMDPM_SMBADDR_READ : 0)); 364 365 b = (void *)cmdbuf; 366 if (cmdlen > 0) 367 /* Set command byte */ 368 bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMDPM_SMBCMD, b[0]); 369 370 if (I2C_OP_WRITE_P(op)) { 371 /* Write data */ 372 data = 0; 373 b = buf; 374 if (len > 0) 375 data = b[0]; 376 if (len > 1) 377 data |= ((u_int16_t)b[1] << 8); 378 if (len > 0) 379 bus_space_write_2(sc->sc_iot, sc->sc_ioh, 380 AMDPM_SMBDATA, data); 381 } 382 383 /* Set SMBus command */ 384 if (len == 0) 385 ctl = AMDPM_SMBCTL_CMD_BYTE; 386 else if (len == 1) 387 ctl = AMDPM_SMBCTL_CMD_BDATA; 388 else if (len == 2) 389 ctl = AMDPM_SMBCTL_CMD_WDATA; 390 391 if ((flags & I2C_F_POLL) == 0) 392 ctl |= AMDPM_SMBCTL_CYCEN; 393 394 /* Start transaction */ 395 ctl |= AMDPM_SMBCTL_START; 396 bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_SMBCTL, ctl); 397 398 if (flags & I2C_F_POLL) { 399 /* Poll for completion */ 400 DELAY(AMDPM_SMBUS_DELAY); 401 for (retries = 1000; retries > 0; retries--) { 402 st = bus_space_read_2(sc->sc_iot, sc->sc_ioh, 403 AMDPM_SMBSTAT); 404 if ((st & AMDPM_SMBSTAT_HBSY) == 0) 405 break; 406 DELAY(AMDPM_SMBUS_DELAY); 407 } 408 if (st & AMDPM_SMBSTAT_HBSY) 409 goto timeout; 410 amdpm_intr(sc); 411 } else { 412 /* Wait for interrupt */ 413 if (tsleep(sc, PRIBIO, "iicexec", AMDPM_SMBUS_TIMEOUT * hz)) 414 goto timeout; 415 } 416 417 if (sc->sc_i2c_xfer.error) 418 return (1); 419 420 return (0); 421 422 timeout: 423 /* 424 * Transfer timeout. Kill the transaction and clear status bits. 425 */ 426 printf("%s: timeout, status 0x%b\n", sc->sc_dev.dv_xname, st, 427 AMDPM_SMBSTAT_BITS); 428 bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_SMBCTL, 429 AMDPM_SMBCTL_ABORT); 430 DELAY(AMDPM_SMBUS_DELAY); 431 st = bus_space_read_2(sc->sc_iot, sc->sc_ioh, AMDPM_SMBSTAT); 432 if ((st & AMDPM_SMBSTAT_ABRT) == 0) 433 printf("%s: transaction abort failed, status 0x%b\n", 434 sc->sc_dev.dv_xname, st, AMDPM_SMBSTAT_BITS); 435 bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_SMBSTAT, st); 436 return (1); 437 } 438 439 int 440 amdpm_intr(void *arg) 441 { 442 struct amdpm_softc *sc = arg; 443 u_int16_t st, data; 444 u_int8_t *b; 445 size_t len; 446 447 /* Read status */ 448 st = bus_space_read_2(sc->sc_iot, sc->sc_ioh, AMDPM_SMBSTAT); 449 if ((st & AMDPM_SMBSTAT_HBSY) != 0 || (st & (AMDPM_SMBSTAT_ABRT | 450 AMDPM_SMBSTAT_COL | AMDPM_SMBSTAT_PRERR | AMDPM_SMBSTAT_CYC | 451 AMDPM_SMBSTAT_TO | AMDPM_SMBSTAT_SNP | AMDPM_SMBSTAT_SLV | 452 AMDPM_SMBSTAT_SMBA)) == 0) 453 /* Interrupt was not for us */ 454 return (0); 455 456 DPRINTF(("%s: intr: st 0x%b\n", sc->sc_dev.dv_xname, st, 457 AMDPM_SMBSTAT_BITS)); 458 459 /* Clear status bits */ 460 bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_SMBSTAT, st); 461 462 /* Check for errors */ 463 if (st & (AMDPM_SMBSTAT_COL | AMDPM_SMBSTAT_PRERR | 464 AMDPM_SMBSTAT_TO)) { 465 sc->sc_i2c_xfer.error = 1; 466 goto done; 467 } 468 469 if (st & AMDPM_SMBSTAT_CYC) { 470 if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op)) 471 goto done; 472 473 /* Read data */ 474 b = sc->sc_i2c_xfer.buf; 475 len = sc->sc_i2c_xfer.len; 476 if (len > 0) { 477 data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, 478 AMDPM_SMBDATA); 479 b[0] = data & 0xff; 480 } 481 if (len > 1) 482 b[1] = (data >> 8) & 0xff; 483 } 484 485 done: 486 if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0) 487 wakeup(sc); 488 return (1); 489 } 490