Annotation of sys/arch/macppc/dev/smu.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: smu.c,v 1.19 2007/05/20 23:38:52 thib Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2005 Mark Kettenis
! 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: #include <sys/param.h>
! 20: #include <sys/systm.h>
! 21: #include <sys/device.h>
! 22: #include <sys/kernel.h>
! 23: #include <sys/rwlock.h>
! 24: #include <sys/proc.h>
! 25: #include <sys/sensors.h>
! 26:
! 27: #include <machine/autoconf.h>
! 28: #include <machine/cpu.h>
! 29:
! 30: #include <dev/clock_subr.h>
! 31: #include <dev/i2c/i2cvar.h>
! 32: #include <dev/ofw/openfirm.h>
! 33:
! 34: #include <arch/macppc/dev/maci2cvar.h>
! 35:
! 36: int smu_match(struct device *, void *, void *);
! 37: void smu_attach(struct device *, struct device *, void *);
! 38:
! 39: #define SMU_MAXFANS 3
! 40:
! 41: struct smu_fan {
! 42: u_int8_t reg;
! 43: u_int16_t min_rpm;
! 44: u_int16_t max_rpm;
! 45: u_int16_t unmanaged_rpm;
! 46: struct ksensor sensor;
! 47: };
! 48:
! 49: #define SMU_MAXSENSORS 3
! 50:
! 51: struct smu_sensor {
! 52: u_int8_t reg;
! 53: struct ksensor sensor;
! 54: };
! 55:
! 56: struct smu_softc {
! 57: struct device sc_dev;
! 58:
! 59: /* SMU command buffer. */
! 60: bus_dma_tag_t sc_dmat;
! 61: bus_dmamap_t sc_cmdmap;
! 62: bus_dma_segment_t sc_cmdseg[1];
! 63: caddr_t sc_cmd;
! 64: struct rwlock sc_lock;
! 65:
! 66: /* Doorbell and mailbox. */
! 67: struct ppc_bus_space sc_mem_bus_space;
! 68: bus_space_tag_t sc_memt;
! 69: bus_space_handle_t sc_gpioh;
! 70: bus_space_handle_t sc_buffh;
! 71:
! 72: struct smu_fan sc_fans[SMU_MAXFANS];
! 73: int sc_num_fans;
! 74:
! 75: struct smu_sensor sc_sensors[SMU_MAXSENSORS];
! 76: int sc_num_sensors;
! 77:
! 78: struct ksensordev sc_sensordev;
! 79:
! 80: u_int16_t sc_cpu_diode_scale;
! 81: int16_t sc_cpu_diode_offset;
! 82: u_int16_t sc_cpu_volt_scale;
! 83: int16_t sc_cpu_volt_offset;
! 84: u_int16_t sc_cpu_curr_scale;
! 85: int16_t sc_cpu_curr_offset;
! 86:
! 87: struct i2c_controller sc_i2c_tag;
! 88: };
! 89:
! 90: struct cfattach smu_ca = {
! 91: sizeof(struct smu_softc), smu_match, smu_attach
! 92: };
! 93:
! 94: struct cfdriver smu_cd = {
! 95: NULL, "smu", DV_DULL,
! 96: };
! 97:
! 98: /* SMU command */
! 99: struct smu_cmd {
! 100: u_int8_t cmd;
! 101: u_int8_t len;
! 102: u_int8_t data[254];
! 103: };
! 104: #define SMU_CMDSZ sizeof(struct smu_cmd)
! 105:
! 106: /* RTC */
! 107: #define SMU_RTC 0x8e
! 108: #define SMU_RTC_SET_DATETIME 0x80
! 109: #define SMU_RTC_GET_DATETIME 0x81
! 110:
! 111: /* ADC */
! 112: #define SMU_ADC 0xd8
! 113:
! 114: /* Fan control */
! 115: #define SMU_FAN 0x4a
! 116:
! 117: /* Data partitions */
! 118: #define SMU_PARTITION 0x3e
! 119: #define SMU_PARTITION_LATEST 0x01
! 120: #define SMU_PARTITION_BASE 0x02
! 121: #define SMU_PARTITION_UPDATE 0x03
! 122:
! 123: /* I2C */
! 124: #define SMU_I2C 0x9a
! 125: #define SMU_I2C_SIMPLE 0x00
! 126: #define SMU_I2C_NORMAL 0x01
! 127: #define SMU_I2C_COMBINED 0x02
! 128:
! 129: /* Power Management */
! 130: #define SMU_POWER 0xaa
! 131:
! 132: /* Miscellaneous */
! 133: #define SMU_MISC 0xee
! 134: #define SMU_MISC_GET_DATA 0x02
! 135:
! 136: int smu_intr(void *);
! 137:
! 138: int smu_do_cmd(struct smu_softc *, int);
! 139: int smu_time_read(time_t *);
! 140: int smu_time_write(time_t);
! 141: int smu_get_datablock(struct smu_softc *sc, u_int8_t, u_int8_t *, size_t);
! 142: int smu_fan_set_rpm(struct smu_softc *, struct smu_fan *, u_int16_t);
! 143: int smu_fan_refresh(struct smu_softc *, struct smu_fan *);
! 144: int smu_sensor_refresh(struct smu_softc *, struct smu_sensor *);
! 145: void smu_refresh_sensors(void *);
! 146:
! 147: int smu_i2c_acquire_bus(void *, int);
! 148: void smu_i2c_release_bus(void *, int);
! 149: int smu_i2c_exec(void *, i2c_op_t, i2c_addr_t,
! 150: const void *, size_t, void *buf, size_t, int);
! 151:
! 152: void smu_slew_voltage(u_int);
! 153:
! 154: #define GPIO_DDR 0x04 /* Data direction */
! 155: #define GPIO_DDR_OUTPUT 0x04 /* Output */
! 156: #define GPIO_DDR_INPUT 0x00 /* Input */
! 157:
! 158: #define GPIO_LEVEL 0x02 /* Pin level (RO) */
! 159:
! 160: #define GPIO_DATA 0x01 /* Data */
! 161:
! 162: int
! 163: smu_match(struct device *parent, void *cf, void *aux)
! 164: {
! 165: struct confargs *ca = aux;
! 166:
! 167: if (strcmp(ca->ca_name, "smu") == 0)
! 168: return (1);
! 169: return (0);
! 170: }
! 171:
! 172: /* XXX */
! 173: extern struct powerpc_bus_dma_tag pci_bus_dma_tag;
! 174:
! 175: void
! 176: smu_attach(struct device *parent, struct device *self, void *aux)
! 177: {
! 178: struct smu_softc *sc = (struct smu_softc *)self;
! 179: struct confargs *ca = aux;
! 180: struct i2cbus_attach_args iba;
! 181: struct smu_fan *fan;
! 182: struct smu_sensor *sensor;
! 183: int nseg, node;
! 184: char type[32], loc[32];
! 185: u_int32_t reg, intr, gpio, val;
! 186: u_int8_t data[12];
! 187:
! 188: /* XXX */
! 189: sc->sc_mem_bus_space.bus_base = 0x80000000;
! 190: sc->sc_mem_bus_space.bus_size = 0;
! 191: sc->sc_mem_bus_space.bus_io = 0;
! 192: sc->sc_memt = &sc->sc_mem_bus_space;
! 193:
! 194: /* Map smu-doorbell gpio. */
! 195: if (OF_getprop(ca->ca_node, "platform-doorbell-ack",
! 196: &node, sizeof node) <= 0 ||
! 197: OF_getprop(node, "reg", ®, sizeof reg) <= 0 ||
! 198: OF_getprop(node, "interrupts", &intr, sizeof intr) <= 0 ||
! 199: OF_getprop(OF_parent(node), "reg", &gpio, sizeof gpio) <= 0) {
! 200: printf(": cannot find smu-doorbell gpio\n");
! 201: return;
! 202: }
! 203: if (bus_space_map(sc->sc_memt, gpio + reg, 1, 0, &sc->sc_gpioh)) {
! 204: printf(": cannot map smu-doorbell gpio\n");
! 205: return;
! 206: }
! 207:
! 208: /* XXX Should get this from OF. */
! 209: if (bus_space_map(sc->sc_memt, 0x860c, 4, 0, &sc->sc_buffh)) {
! 210: printf(": cannot map smu-doorbell buffer\n");
! 211: return;
! 212: }
! 213:
! 214: /* XXX */
! 215: sc->sc_dmat = &pci_bus_dma_tag;
! 216:
! 217: /* Allocate and map SMU command buffer. */
! 218: if (bus_dmamem_alloc(sc->sc_dmat, SMU_CMDSZ, 0, 0,
! 219: sc->sc_cmdseg, 1, &nseg, BUS_DMA_NOWAIT)) {
! 220: printf(": cannot allocate cmd buffer\n");
! 221: return;
! 222: }
! 223: if (bus_dmamem_map(sc->sc_dmat, sc->sc_cmdseg, nseg,
! 224: SMU_CMDSZ, &sc->sc_cmd, BUS_DMA_NOWAIT)) {
! 225: printf(": cannot map cmd buffer\n");
! 226: bus_dmamem_free(sc->sc_dmat, sc->sc_cmdseg, 1);
! 227: return;
! 228: }
! 229: if (bus_dmamap_create(sc->sc_dmat, SMU_CMDSZ, 1, SMU_CMDSZ, 0,
! 230: BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &sc->sc_cmdmap)) {
! 231: printf(": cannot create cmd dmamap\n");
! 232: bus_dmamem_unmap(sc->sc_dmat, sc->sc_cmd, SMU_CMDSZ);
! 233: bus_dmamem_free(sc->sc_dmat, sc->sc_cmdseg, 1);
! 234: return;
! 235: }
! 236: if (bus_dmamap_load(sc->sc_dmat, sc->sc_cmdmap, sc->sc_cmd,
! 237: SMU_CMDSZ, NULL, BUS_DMA_NOWAIT)) {
! 238: printf(": cannot load cmd dmamap\n");
! 239: bus_dmamap_destroy(sc->sc_dmat, sc->sc_cmdmap);
! 240: bus_dmamem_unmap(sc->sc_dmat, sc->sc_cmd, SMU_CMDSZ);
! 241: bus_dmamem_free(sc->sc_dmat, sc->sc_cmdseg, nseg);
! 242: return;
! 243: }
! 244:
! 245: rw_init(&sc->sc_lock, sc->sc_dev.dv_xname);
! 246:
! 247: /* Establish smu-doorbell interrupt. */
! 248: mac_intr_establish(parent, intr, IST_EDGE, IPL_BIO,
! 249: smu_intr, sc, sc->sc_dev.dv_xname);
! 250:
! 251: /* Initialize global variables that control RTC functionality. */
! 252: time_read = smu_time_read;
! 253: time_write = smu_time_write;
! 254:
! 255: /* Fans */
! 256: node = OF_getnodebyname(ca->ca_node, "rpm-fans");
! 257: if (node == 0)
! 258: node = OF_getnodebyname(ca->ca_node, "fans");
! 259: for (node = OF_child(node); node; node = OF_peer(node)) {
! 260: if (OF_getprop(node, "reg", ®, sizeof reg) <= 0 ||
! 261: OF_getprop(node, "device_type", type, sizeof type) <= 0)
! 262: continue;
! 263:
! 264: if (strcmp(type, "fan-rpm-control") != 0) {
! 265: printf(": unsupported fan type: %s\n", type);
! 266: return;
! 267: }
! 268:
! 269: if (sc->sc_num_fans >= SMU_MAXFANS) {
! 270: printf(": too many fans\n");
! 271: return;
! 272: }
! 273:
! 274: fan = &sc->sc_fans[sc->sc_num_fans++];
! 275: fan->sensor.type = SENSOR_FANRPM;
! 276: fan->sensor.flags = SENSOR_FINVALID;
! 277: fan->reg = reg;
! 278:
! 279: if (OF_getprop(node, "min-value", &val, sizeof val) <= 0)
! 280: val = 0;
! 281: fan->min_rpm = val;
! 282: if (OF_getprop(node, "max-value", &val, sizeof val) <= 0)
! 283: val = 0xffff;
! 284: fan->max_rpm = val;
! 285: if (OF_getprop(node, "unmanage-value", &val, sizeof val) <= 0)
! 286: val = fan->max_rpm;
! 287: fan->unmanaged_rpm = val;
! 288:
! 289: if (OF_getprop(node, "location", loc, sizeof loc) <= 0)
! 290: strlcpy(loc, "Unknown", sizeof loc);
! 291: strlcpy(fan->sensor.desc, loc, sizeof sensor->sensor.desc);
! 292:
! 293: /* Start running fans at their "unmanaged" speed. */
! 294: smu_fan_set_rpm(sc, fan, fan->unmanaged_rpm);
! 295:
! 296: sensor_attach(&sc->sc_sensordev, &fan->sensor);
! 297: }
! 298:
! 299: /*
! 300: * Bail out if we didn't find any fans. If we don't set the
! 301: * fans to a safe speed, but tickle the SMU periodically by
! 302: * reading sensors, the fans will never spin up and the
! 303: * machine might overheat.
! 304: */
! 305: if (sc->sc_num_fans == 0) {
! 306: printf(": no fans\n");
! 307: return;
! 308: }
! 309:
! 310: /* Sensors */
! 311: node = OF_getnodebyname(ca->ca_node, "sensors");
! 312: for (node = OF_child(node); node; node = OF_peer(node)) {
! 313: if (OF_getprop(node, "reg", &val, sizeof val) <= 0 ||
! 314: OF_getprop(node, "device_type", type, sizeof type) <= 0)
! 315: continue;
! 316:
! 317: sensor = &sc->sc_sensors[sc->sc_num_sensors++];
! 318: sensor->sensor.flags = SENSOR_FINVALID;
! 319: sensor->reg = val;
! 320:
! 321: if (strcmp(type, "current-sensor") == 0) {
! 322: sensor->sensor.type = SENSOR_AMPS;
! 323: } else if (strcmp(type, "temp-sensor") == 0) {
! 324: sensor->sensor.type = SENSOR_TEMP;
! 325: } else if (strcmp(type, "voltage-sensor") == 0) {
! 326: sensor->sensor.type = SENSOR_VOLTS_DC;
! 327: } else {
! 328: sensor->sensor.type = SENSOR_INTEGER;
! 329: }
! 330:
! 331: if (OF_getprop(node, "location", loc, sizeof loc) <= 0)
! 332: strlcpy(loc, "Unknown", sizeof loc);
! 333: strlcpy(sensor->sensor.desc, loc, sizeof sensor->sensor.desc);
! 334:
! 335: sensor_attach(&sc->sc_sensordev, &sensor->sensor);
! 336: }
! 337:
! 338: /* Register sensor device with sysctl */
! 339: strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
! 340: sizeof(sc->sc_sensordev.xname));
! 341: sensordev_install(&sc->sc_sensordev);
! 342:
! 343: /* CPU temperature diode calibration */
! 344: smu_get_datablock(sc, 0x18, data, sizeof data);
! 345: sc->sc_cpu_diode_scale = (data[4] << 8) + data[5];
! 346: sc->sc_cpu_diode_offset = (data[6] << 8) + data[7];
! 347:
! 348: /* CPU power (voltage and current) calibration */
! 349: smu_get_datablock(sc, 0x21, data, sizeof data);
! 350: sc->sc_cpu_volt_scale = (data[4] << 8) + data[5];
! 351: sc->sc_cpu_volt_offset = (data[6] << 8) + data[7];
! 352: sc->sc_cpu_curr_scale = (data[8] << 8) + data[9];
! 353: sc->sc_cpu_curr_offset = (data[10] << 8) + data[11];
! 354:
! 355: sensor_task_register(sc, smu_refresh_sensors, 5);
! 356: printf("\n");
! 357:
! 358: ppc64_slew_voltage = smu_slew_voltage;
! 359:
! 360: sc->sc_i2c_tag.ic_cookie = sc;
! 361: sc->sc_i2c_tag.ic_acquire_bus = smu_i2c_acquire_bus;
! 362: sc->sc_i2c_tag.ic_release_bus = smu_i2c_release_bus;
! 363: sc->sc_i2c_tag.ic_exec = smu_i2c_exec;
! 364:
! 365: node = OF_getnodebyname(ca->ca_node, "smu-i2c-control");
! 366: node = OF_child(node);
! 367:
! 368: bzero(&iba, sizeof iba);
! 369: iba.iba_name = "iic";
! 370: iba.iba_tag = &sc->sc_i2c_tag;
! 371: iba.iba_bus_scan = maciic_scan;
! 372: iba.iba_bus_scan_arg = &node;
! 373: config_found(&sc->sc_dev, &iba, NULL);
! 374: }
! 375:
! 376: int
! 377: smu_intr(void *arg)
! 378: {
! 379: wakeup(arg);
! 380: return 1;
! 381: }
! 382:
! 383: int
! 384: smu_do_cmd(struct smu_softc *sc, int timo)
! 385: {
! 386: struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
! 387: u_int8_t gpio, ack = ~cmd->cmd;
! 388: int error;
! 389:
! 390: /* Write to mailbox. */
! 391: bus_space_write_4(sc->sc_memt, sc->sc_buffh, 0,
! 392: sc->sc_cmdmap->dm_segs->ds_addr);
! 393:
! 394: /* Flush to RAM. */
! 395: asm __volatile__ ("dcbst 0,%0; sync" :: "r"(sc->sc_cmd): "memory");
! 396:
! 397: /* Ring doorbell. */
! 398: bus_space_write_1(sc->sc_memt, sc->sc_gpioh, 0, GPIO_DDR_OUTPUT);
! 399:
! 400: do {
! 401: error = tsleep(sc, PWAIT, "smu", (timo * hz) / 1000);
! 402: if (error)
! 403: return (error);
! 404: gpio = bus_space_read_1(sc->sc_memt, sc->sc_gpioh, 0);
! 405: } while (!(gpio & (GPIO_DATA)));
! 406:
! 407: /* CPU might have brought back the cache line. */
! 408: asm __volatile__ ("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory");
! 409:
! 410: if (cmd->cmd != ack)
! 411: return (EIO);
! 412: return (0);
! 413: }
! 414:
! 415: int
! 416: smu_time_read(time_t *secs)
! 417: {
! 418: struct smu_softc *sc = smu_cd.cd_devs[0];
! 419: struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
! 420: struct clock_ymdhms dt;
! 421: int error;
! 422:
! 423: rw_enter_write(&sc->sc_lock);
! 424:
! 425: cmd->cmd = SMU_RTC;
! 426: cmd->len = 1;
! 427: cmd->data[0] = SMU_RTC_GET_DATETIME;
! 428: error = smu_do_cmd(sc, 800);
! 429: if (error) {
! 430: rw_exit_write(&sc->sc_lock);
! 431:
! 432: *secs = 0;
! 433: return (error);
! 434: }
! 435:
! 436: dt.dt_year = 2000 + FROMBCD(cmd->data[6]);
! 437: dt.dt_mon = FROMBCD(cmd->data[5]);
! 438: dt.dt_day = FROMBCD(cmd->data[4]);
! 439: dt.dt_hour = FROMBCD(cmd->data[2]);
! 440: dt.dt_min = FROMBCD(cmd->data[1]);
! 441: dt.dt_sec = FROMBCD(cmd->data[0]);
! 442:
! 443: rw_exit_write(&sc->sc_lock);
! 444:
! 445: *secs = clock_ymdhms_to_secs(&dt);
! 446: return (0);
! 447: }
! 448:
! 449: int
! 450: smu_time_write(time_t secs)
! 451: {
! 452: struct smu_softc *sc = smu_cd.cd_devs[0];
! 453: struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
! 454: struct clock_ymdhms dt;
! 455: int error;
! 456:
! 457: clock_secs_to_ymdhms(secs, &dt);
! 458:
! 459: rw_enter_write(&sc->sc_lock);
! 460:
! 461: cmd->cmd = SMU_RTC;
! 462: cmd->len = 8;
! 463: cmd->data[0] = SMU_RTC_SET_DATETIME;
! 464: cmd->data[1] = TOBCD(dt.dt_sec);
! 465: cmd->data[2] = TOBCD(dt.dt_min);
! 466: cmd->data[3] = TOBCD(dt.dt_hour);
! 467: cmd->data[4] = TOBCD(dt.dt_wday);
! 468: cmd->data[5] = TOBCD(dt.dt_day);
! 469: cmd->data[6] = TOBCD(dt.dt_mon);
! 470: cmd->data[7] = TOBCD(dt.dt_year - 2000);
! 471: error = smu_do_cmd(sc, 800);
! 472:
! 473: rw_exit_write(&sc->sc_lock);
! 474:
! 475: return (error);
! 476: }
! 477:
! 478:
! 479: int
! 480: smu_get_datablock(struct smu_softc *sc, u_int8_t id, u_int8_t *buf, size_t len)
! 481: {
! 482: struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
! 483: u_int8_t addr[4];
! 484: int error;
! 485:
! 486: cmd->cmd = SMU_PARTITION;
! 487: cmd->len = 2;
! 488: cmd->data[0] = SMU_PARTITION_LATEST;
! 489: cmd->data[1] = id;
! 490: error = smu_do_cmd(sc, 800);
! 491: if (error)
! 492: return (error);
! 493:
! 494: addr[0] = 0x00;
! 495: addr[1] = 0x00;
! 496: addr[2] = cmd->data[0];
! 497: addr[3] = cmd->data[1];
! 498:
! 499: cmd->cmd = SMU_MISC;
! 500: cmd->len = 7;
! 501: cmd->data[0] = SMU_MISC_GET_DATA;
! 502: cmd->data[1] = sizeof(u_int32_t);
! 503: cmd->data[2] = addr[0];
! 504: cmd->data[3] = addr[1];
! 505: cmd->data[4] = addr[2];
! 506: cmd->data[5] = addr[3];
! 507: cmd->data[6] = len;
! 508: error = smu_do_cmd(sc, 800);
! 509: if (error)
! 510: return (error);
! 511:
! 512: memcpy(buf, cmd->data, len);
! 513: return (0);
! 514: }
! 515:
! 516: int
! 517: smu_fan_set_rpm(struct smu_softc *sc, struct smu_fan *fan, u_int16_t rpm)
! 518: {
! 519: struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
! 520:
! 521: /*
! 522: * On the PowerMac8,2 this command expects the requested fan
! 523: * speed at a different location in the command block than on
! 524: * the PowerMac8,1. We simply store the value at both
! 525: * locations.
! 526: */
! 527: cmd->cmd = SMU_FAN;
! 528: cmd->len = 14;
! 529: cmd->data[0] = 0x00; /* fan-rpm-control */
! 530: cmd->data[1] = 0x01 << fan->reg;
! 531: cmd->data[2] = cmd->data[2 + fan->reg * 2] = (rpm >> 8) & 0xff;
! 532: cmd->data[3] = cmd->data[3 + fan->reg * 2] = (rpm & 0xff);
! 533: return smu_do_cmd(sc, 800);
! 534: }
! 535:
! 536: int
! 537: smu_fan_refresh(struct smu_softc *sc, struct smu_fan *fan)
! 538: {
! 539: struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
! 540: int error;
! 541:
! 542: cmd->cmd = SMU_FAN;
! 543: cmd->len = 2;
! 544: cmd->data[0] = 0x01; /* fan-rpm-control */
! 545: cmd->data[1] = 0x01 << fan->reg;
! 546: error = smu_do_cmd(sc, 800);
! 547: if (error) {
! 548: fan->sensor.flags = SENSOR_FINVALID;
! 549: return (error);
! 550: }
! 551: fan->sensor.value = (cmd->data[1] << 8) + cmd->data[2];
! 552: fan->sensor.flags = 0;
! 553: return (0);
! 554: }
! 555:
! 556: int
! 557: smu_sensor_refresh(struct smu_softc *sc, struct smu_sensor *sensor)
! 558: {
! 559: struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
! 560: int64_t value;
! 561: int error;
! 562:
! 563: cmd->cmd = SMU_ADC;
! 564: cmd->len = 1;
! 565: cmd->data[0] = sensor->reg;
! 566: error = smu_do_cmd(sc, 800);
! 567: if (error) {
! 568: sensor->sensor.flags = SENSOR_FINVALID;
! 569: return (error);
! 570: }
! 571: value = (cmd->data[0] << 8) + cmd->data[1];
! 572: switch (sensor->sensor.type) {
! 573: case SENSOR_TEMP:
! 574: value *= sc->sc_cpu_diode_scale;
! 575: value >>= 3;
! 576: value += ((int64_t)sc->sc_cpu_diode_offset) << 9;
! 577: value <<= 1;
! 578:
! 579: /* Convert from 16.16 fixed point degC into muK. */
! 580: value *= 15625;
! 581: value /= 1024;
! 582: value += 273150000;
! 583: break;
! 584:
! 585: case SENSOR_VOLTS_DC:
! 586: value *= sc->sc_cpu_volt_scale;
! 587: value += sc->sc_cpu_volt_offset;
! 588: value <<= 4;
! 589:
! 590: /* Convert from 16.16 fixed point V into muV. */
! 591: value *= 15625;
! 592: value /= 1024;
! 593: break;
! 594:
! 595: case SENSOR_AMPS:
! 596: value *= sc->sc_cpu_curr_scale;
! 597: value += sc->sc_cpu_curr_offset;
! 598: value <<= 4;
! 599:
! 600: /* Convert from 16.16 fixed point A into muA. */
! 601: value *= 15625;
! 602: value /= 1024;
! 603: break;
! 604:
! 605: default:
! 606: break;
! 607: }
! 608: sensor->sensor.value = value;
! 609: sensor->sensor.flags = 0;
! 610: return (0);
! 611: }
! 612:
! 613: void
! 614: smu_refresh_sensors(void *arg)
! 615: {
! 616: struct smu_softc *sc = arg;
! 617: int i;
! 618:
! 619: rw_enter_write(&sc->sc_lock);
! 620: for (i = 0; i < sc->sc_num_sensors; i++)
! 621: smu_sensor_refresh(sc, &sc->sc_sensors[i]);
! 622: for (i = 0; i < sc->sc_num_fans; i++)
! 623: smu_fan_refresh(sc, &sc->sc_fans[i]);
! 624: rw_exit_write(&sc->sc_lock);
! 625: }
! 626:
! 627: int
! 628: smu_i2c_acquire_bus(void *cookie, int flags)
! 629: {
! 630: struct smu_softc *sc = cookie;
! 631:
! 632: if (flags & I2C_F_POLL)
! 633: return (0);
! 634:
! 635: return (rw_enter(&sc->sc_lock, RW_WRITE));
! 636: }
! 637:
! 638: void
! 639: smu_i2c_release_bus(void *cookie, int flags)
! 640: {
! 641: struct smu_softc *sc = cookie;
! 642:
! 643: if (flags & I2C_F_POLL)
! 644: return;
! 645:
! 646: rw_exit(&sc->sc_lock);
! 647: }
! 648:
! 649: int
! 650: smu_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
! 651: const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
! 652: {
! 653: struct smu_softc *sc = cookie;
! 654: struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
! 655: u_int8_t smu_op = SMU_I2C_NORMAL;
! 656: int error, retries = 10;
! 657:
! 658: if (!I2C_OP_STOP_P(op) || cmdlen > 3 || len > 5)
! 659: return (EINVAL);
! 660:
! 661: if(cmdlen == 0)
! 662: smu_op = SMU_I2C_SIMPLE;
! 663: else if (I2C_OP_READ_P(op))
! 664: smu_op = SMU_I2C_COMBINED;
! 665:
! 666: cmd->cmd = SMU_I2C;
! 667: cmd->len = 9 + len;
! 668: cmd->data[0] = 0xb;
! 669: cmd->data[1] = smu_op;
! 670: cmd->data[2] = addr << 1;
! 671: cmd->data[3] = cmdlen;
! 672: memcpy (&cmd->data[4], cmdbuf, cmdlen);
! 673: cmd->data[7] = addr << 1 | I2C_OP_READ_P(op);
! 674: cmd->data[8] = len;
! 675: memcpy(&cmd->data[9], buf, len);
! 676:
! 677: error = smu_do_cmd(sc, 250);
! 678: if (error)
! 679: return error;
! 680:
! 681: while (retries--) {
! 682: cmd->cmd = SMU_I2C;
! 683: cmd->len = 1;
! 684: cmd->data[0] = 0;
! 685: memset(&cmd->data[1], 0xff, len);
! 686:
! 687: error = smu_do_cmd(sc, 250);
! 688: if (error)
! 689: return error;
! 690:
! 691: if ((cmd->data[0] & 0x80) == 0)
! 692: break;
! 693: if (cmd->data[0] == 0xfd)
! 694: break;
! 695:
! 696: DELAY(15 * 1000);
! 697: }
! 698:
! 699: if (cmd->data[0] & 0x80)
! 700: return (EIO);
! 701:
! 702: if (I2C_OP_READ_P(op))
! 703: memcpy(buf, &cmd->data[1], len);
! 704: return (0);
! 705: }
! 706:
! 707: void
! 708: smu_slew_voltage(u_int freq_scale)
! 709: {
! 710: struct smu_softc *sc = smu_cd.cd_devs[0];
! 711: struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
! 712:
! 713: cmd->cmd = SMU_POWER;
! 714: cmd->len = 8;
! 715: memcpy(cmd->data, "VSLEW", 5);
! 716: cmd->data[5] = 0xff;
! 717: cmd->data[6] = 1;
! 718: cmd->data[7] = freq_scale;
! 719:
! 720: smu_do_cmd(sc, 250);
! 721: }
CVSweb