smu.c revision 204270
1194679Snwhitehorn/*- 2194679Snwhitehorn * Copyright (c) 2009 Nathan Whitehorn 3194679Snwhitehorn * All rights reserved. 4194679Snwhitehorn * 5194679Snwhitehorn * Redistribution and use in source and binary forms, with or without 6194679Snwhitehorn * modification, are permitted provided that the following conditions 7194679Snwhitehorn * are met: 8194679Snwhitehorn * 1. Redistributions of source code must retain the above copyright 9194679Snwhitehorn * notice, this list of conditions and the following disclaimer. 10194679Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright 11194679Snwhitehorn * notice, this list of conditions and the following disclaimer in the 12194679Snwhitehorn * documentation and/or other materials provided with the distribution. 13194679Snwhitehorn * 14194679Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15194679Snwhitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16194679Snwhitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17194679Snwhitehorn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18194679Snwhitehorn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19194679Snwhitehorn * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20194679Snwhitehorn * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 21194679Snwhitehorn * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22194679Snwhitehorn * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23194679Snwhitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24194679Snwhitehorn * SUCH DAMAGE. 25194679Snwhitehorn * 26194679Snwhitehorn */ 27194679Snwhitehorn 28194679Snwhitehorn#include <sys/cdefs.h> 29194679Snwhitehorn__FBSDID("$FreeBSD: head/sys/powerpc/powermac/smu.c 204270 2010-02-24 01:27:36Z nwhitehorn $"); 30194679Snwhitehorn 31194679Snwhitehorn#include <sys/param.h> 32194679Snwhitehorn#include <sys/bus.h> 33194679Snwhitehorn#include <sys/systm.h> 34194679Snwhitehorn#include <sys/module.h> 35204180Snwhitehorn#include <sys/callout.h> 36194679Snwhitehorn#include <sys/conf.h> 37194679Snwhitehorn#include <sys/cpu.h> 38204082Snwhitehorn#include <sys/ctype.h> 39194679Snwhitehorn#include <sys/kernel.h> 40204180Snwhitehorn#include <sys/reboot.h> 41194679Snwhitehorn#include <sys/rman.h> 42194679Snwhitehorn#include <sys/sysctl.h> 43194679Snwhitehorn 44194679Snwhitehorn#include <machine/bus.h> 45194679Snwhitehorn#include <machine/md_var.h> 46194679Snwhitehorn 47204218Snwhitehorn#include <dev/led/led.h> 48194679Snwhitehorn#include <dev/ofw/openfirm.h> 49194679Snwhitehorn#include <dev/ofw/ofw_bus.h> 50194679Snwhitehorn#include <powerpc/powermac/macgpiovar.h> 51194679Snwhitehorn 52194679Snwhitehornstruct smu_cmd { 53204082Snwhitehorn volatile uint8_t cmd; 54194679Snwhitehorn uint8_t len; 55194679Snwhitehorn uint8_t data[254]; 56194679Snwhitehorn}; 57194679Snwhitehorn 58204082Snwhitehornstruct smu_fan { 59204082Snwhitehorn cell_t reg; 60204082Snwhitehorn cell_t min_rpm; 61204082Snwhitehorn cell_t max_rpm; 62204082Snwhitehorn cell_t unmanaged_rpm; 63204082Snwhitehorn char location[32]; 64204180Snwhitehorn 65204179Snwhitehorn int old_style; 66204180Snwhitehorn int setpoint; 67204082Snwhitehorn}; 68204082Snwhitehorn 69204082Snwhitehornstruct smu_sensor { 70204082Snwhitehorn cell_t reg; 71204082Snwhitehorn char location[32]; 72204082Snwhitehorn enum { 73204082Snwhitehorn SMU_CURRENT_SENSOR, 74204082Snwhitehorn SMU_VOLTAGE_SENSOR, 75204082Snwhitehorn SMU_POWER_SENSOR, 76204082Snwhitehorn SMU_TEMP_SENSOR 77204082Snwhitehorn } type; 78204082Snwhitehorn}; 79204082Snwhitehorn 80194679Snwhitehornstruct smu_softc { 81194679Snwhitehorn device_t sc_dev; 82194679Snwhitehorn struct mtx sc_mtx; 83194679Snwhitehorn 84194679Snwhitehorn struct resource *sc_memr; 85194679Snwhitehorn int sc_memrid; 86194679Snwhitehorn 87194679Snwhitehorn bus_dma_tag_t sc_dmatag; 88194679Snwhitehorn bus_space_tag_t sc_bt; 89194679Snwhitehorn bus_space_handle_t sc_mailbox; 90194679Snwhitehorn 91194679Snwhitehorn struct smu_cmd *sc_cmd; 92194679Snwhitehorn bus_addr_t sc_cmd_phys; 93194679Snwhitehorn bus_dmamap_t sc_cmd_dmamap; 94204082Snwhitehorn 95204082Snwhitehorn struct smu_fan *sc_fans; 96204082Snwhitehorn int sc_nfans; 97204082Snwhitehorn struct smu_sensor *sc_sensors; 98204082Snwhitehorn int sc_nsensors; 99204082Snwhitehorn 100204180Snwhitehorn struct callout sc_fanmgt_callout; 101204180Snwhitehorn time_t sc_lastuserchange; 102204180Snwhitehorn 103204082Snwhitehorn /* Calibration data */ 104204082Snwhitehorn uint16_t sc_cpu_diode_scale; 105204082Snwhitehorn int16_t sc_cpu_diode_offset; 106204082Snwhitehorn 107204082Snwhitehorn uint16_t sc_cpu_volt_scale; 108204082Snwhitehorn int16_t sc_cpu_volt_offset; 109204082Snwhitehorn uint16_t sc_cpu_curr_scale; 110204082Snwhitehorn int16_t sc_cpu_curr_offset; 111204082Snwhitehorn 112204082Snwhitehorn uint16_t sc_slots_pow_scale; 113204082Snwhitehorn int16_t sc_slots_pow_offset; 114204180Snwhitehorn 115204180Snwhitehorn /* Thermal management parameters */ 116204180Snwhitehorn int sc_target_temp; /* Default 55 C */ 117204180Snwhitehorn int sc_critical_temp; /* Default 90 C */ 118204218Snwhitehorn 119204218Snwhitehorn struct cdev *sc_leddev; 120194679Snwhitehorn}; 121194679Snwhitehorn 122194679Snwhitehorn/* regular bus attachment functions */ 123194679Snwhitehorn 124194679Snwhitehornstatic int smu_probe(device_t); 125194679Snwhitehornstatic int smu_attach(device_t); 126194679Snwhitehorn 127194679Snwhitehorn/* cpufreq notification hooks */ 128194679Snwhitehorn 129194679Snwhitehornstatic void smu_cpufreq_pre_change(device_t, const struct cf_level *level); 130194679Snwhitehornstatic void smu_cpufreq_post_change(device_t, const struct cf_level *level); 131194679Snwhitehorn 132204082Snwhitehorn/* utility functions */ 133204179Snwhitehornstatic int smu_run_cmd(device_t dev, struct smu_cmd *cmd); 134204082Snwhitehornstatic int smu_get_datablock(device_t dev, int8_t id, uint8_t *buf, 135204082Snwhitehorn size_t len); 136204082Snwhitehornstatic void smu_attach_fans(device_t dev, phandle_t fanroot); 137204082Snwhitehornstatic void smu_attach_sensors(device_t dev, phandle_t sensroot); 138204180Snwhitehornstatic void smu_fanmgt_callout(void *xdev); 139204218Snwhitehornstatic void smu_set_sleepled(void *xdev, int onoff); 140204270Snwhitehornstatic int smu_server_mode(SYSCTL_HANDLER_ARGS); 141204082Snwhitehorn 142194679Snwhitehorn/* where to find the doorbell GPIO */ 143194679Snwhitehorn 144194679Snwhitehornstatic device_t smu_doorbell = NULL; 145194679Snwhitehorn 146194679Snwhitehornstatic device_method_t smu_methods[] = { 147194679Snwhitehorn /* Device interface */ 148194679Snwhitehorn DEVMETHOD(device_probe, smu_probe), 149194679Snwhitehorn DEVMETHOD(device_attach, smu_attach), 150194679Snwhitehorn { 0, 0 }, 151194679Snwhitehorn}; 152194679Snwhitehorn 153194679Snwhitehornstatic driver_t smu_driver = { 154194679Snwhitehorn "smu", 155194679Snwhitehorn smu_methods, 156194679Snwhitehorn sizeof(struct smu_softc) 157194679Snwhitehorn}; 158194679Snwhitehorn 159194679Snwhitehornstatic devclass_t smu_devclass; 160194679Snwhitehorn 161194679SnwhitehornDRIVER_MODULE(smu, nexus, smu_driver, smu_devclass, 0, 0); 162204082SnwhitehornMALLOC_DEFINE(M_SMU, "smu", "SMU Sensor Information"); 163194679Snwhitehorn 164204082Snwhitehorn#define SMU_MAILBOX 0x8000860c 165204180Snwhitehorn#define SMU_FANMGT_INTERVAL 500 /* ms */ 166194679Snwhitehorn 167194679Snwhitehorn/* Command types */ 168204082Snwhitehorn#define SMU_ADC 0xd8 169204082Snwhitehorn#define SMU_FAN 0x4a 170204082Snwhitehorn#define SMU_I2C 0x9a 171204179Snwhitehorn#define SMU_I2C_SIMPLE 0x00 172204179Snwhitehorn#define SMU_I2C_NORMAL 0x01 173204179Snwhitehorn#define SMU_I2C_COMBINED 0x02 174204082Snwhitehorn#define SMU_MISC 0xee 175204179Snwhitehorn#define SMU_MISC_GET_DATA 0x02 176204179Snwhitehorn#define SMU_MISC_LED_CTRL 0x04 177204082Snwhitehorn#define SMU_POWER 0xaa 178204270Snwhitehorn#define SMU_POWER_EVENTS 0x8f 179204270Snwhitehorn#define SMU_PWR_GET_POWERUP 0x00 180204270Snwhitehorn#define SMU_PWR_SET_POWERUP 0x01 181204270Snwhitehorn#define SMU_PWR_CLR_POWERUP 0x02 182194679Snwhitehorn 183204270Snwhitehorn/* Power event types */ 184204270Snwhitehorn#define SMU_WAKEUP_KEYPRESS 0x01 185204270Snwhitehorn#define SMU_WAKEUP_AC_INSERT 0x02 186204270Snwhitehorn#define SMU_WAKEUP_AC_CHANGE 0x04 187204270Snwhitehorn#define SMU_WAKEUP_RING 0x10 188204270Snwhitehorn 189204082Snwhitehorn/* Data blocks */ 190204082Snwhitehorn#define SMU_CPUTEMP_CAL 0x18 191204082Snwhitehorn#define SMU_CPUVOLT_CAL 0x21 192204082Snwhitehorn#define SMU_SLOTPW_CAL 0x78 193204082Snwhitehorn 194204082Snwhitehorn/* Partitions */ 195204082Snwhitehorn#define SMU_PARTITION 0x3e 196204082Snwhitehorn#define SMU_PARTITION_LATEST 0x01 197204082Snwhitehorn#define SMU_PARTITION_BASE 0x02 198204082Snwhitehorn#define SMU_PARTITION_UPDATE 0x03 199204082Snwhitehorn 200194679Snwhitehornstatic int 201194679Snwhitehornsmu_probe(device_t dev) 202194679Snwhitehorn{ 203194679Snwhitehorn const char *name = ofw_bus_get_name(dev); 204194679Snwhitehorn 205194679Snwhitehorn if (strcmp(name, "smu") != 0) 206194679Snwhitehorn return (ENXIO); 207194679Snwhitehorn 208194679Snwhitehorn device_set_desc(dev, "Apple System Management Unit"); 209194679Snwhitehorn return (0); 210194679Snwhitehorn} 211194679Snwhitehorn 212194679Snwhitehornstatic void 213194679Snwhitehornsmu_phys_callback(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) 214194679Snwhitehorn{ 215194679Snwhitehorn struct smu_softc *sc = xsc; 216194679Snwhitehorn 217194679Snwhitehorn sc->sc_cmd_phys = segs[0].ds_addr; 218194679Snwhitehorn} 219194679Snwhitehorn 220194679Snwhitehornstatic int 221194679Snwhitehornsmu_attach(device_t dev) 222194679Snwhitehorn{ 223194679Snwhitehorn struct smu_softc *sc; 224204082Snwhitehorn phandle_t node, child; 225204082Snwhitehorn uint8_t data[12]; 226194679Snwhitehorn 227194679Snwhitehorn sc = device_get_softc(dev); 228194679Snwhitehorn 229194679Snwhitehorn mtx_init(&sc->sc_mtx, "smu", NULL, MTX_DEF); 230194679Snwhitehorn 231194679Snwhitehorn /* 232194679Snwhitehorn * Map the mailbox area. This should be determined from firmware, 233194679Snwhitehorn * but I have not found a simple way to do that. 234194679Snwhitehorn */ 235194679Snwhitehorn bus_dma_tag_create(NULL, 16, 0, BUS_SPACE_MAXADDR_32BIT, 236194679Snwhitehorn BUS_SPACE_MAXADDR, NULL, NULL, PAGE_SIZE, 1, PAGE_SIZE, 0, NULL, 237194679Snwhitehorn NULL, &(sc->sc_dmatag)); 238204082Snwhitehorn sc->sc_bt = &bs_le_tag; 239194679Snwhitehorn bus_space_map(sc->sc_bt, SMU_MAILBOX, 4, 0, &sc->sc_mailbox); 240194679Snwhitehorn 241194679Snwhitehorn /* 242194679Snwhitehorn * Allocate the command buffer. This can be anywhere in the low 4 GB 243194679Snwhitehorn * of memory. 244194679Snwhitehorn */ 245194679Snwhitehorn bus_dmamem_alloc(sc->sc_dmatag, (void **)&sc->sc_cmd, BUS_DMA_WAITOK | 246194679Snwhitehorn BUS_DMA_ZERO, &sc->sc_cmd_dmamap); 247194679Snwhitehorn bus_dmamap_load(sc->sc_dmatag, sc->sc_cmd_dmamap, 248194679Snwhitehorn sc->sc_cmd, PAGE_SIZE, smu_phys_callback, sc, 0); 249194679Snwhitehorn 250194679Snwhitehorn /* 251194679Snwhitehorn * Set up handlers to change CPU voltage when CPU frequency is changed. 252194679Snwhitehorn */ 253194679Snwhitehorn EVENTHANDLER_REGISTER(cpufreq_pre_change, smu_cpufreq_pre_change, dev, 254194679Snwhitehorn EVENTHANDLER_PRI_ANY); 255194679Snwhitehorn EVENTHANDLER_REGISTER(cpufreq_post_change, smu_cpufreq_post_change, dev, 256194679Snwhitehorn EVENTHANDLER_PRI_ANY); 257194679Snwhitehorn 258204082Snwhitehorn /* 259204082Snwhitehorn * Detect and attach child devices. 260204082Snwhitehorn */ 261204082Snwhitehorn node = ofw_bus_get_node(dev); 262204082Snwhitehorn for (child = OF_child(node); child != 0; child = OF_peer(child)) { 263204082Snwhitehorn char name[32]; 264204082Snwhitehorn memset(name, 0, sizeof(name)); 265204082Snwhitehorn OF_getprop(child, "name", name, sizeof(name)); 266204082Snwhitehorn 267204082Snwhitehorn if (strncmp(name, "rpm-fans", 9) == 0 || 268204082Snwhitehorn strncmp(name, "fans", 5) == 0) 269204082Snwhitehorn smu_attach_fans(dev, child); 270204082Snwhitehorn 271204082Snwhitehorn if (strncmp(name, "sensors", 8) == 0) 272204082Snwhitehorn smu_attach_sensors(dev, child); 273204082Snwhitehorn } 274204082Snwhitehorn 275204082Snwhitehorn /* 276204082Snwhitehorn * Collect calibration constants. 277204082Snwhitehorn */ 278204082Snwhitehorn smu_get_datablock(dev, SMU_CPUTEMP_CAL, data, sizeof(data)); 279204082Snwhitehorn sc->sc_cpu_diode_scale = (data[4] << 8) + data[5]; 280204082Snwhitehorn sc->sc_cpu_diode_offset = (data[6] << 8) + data[7]; 281204082Snwhitehorn 282204082Snwhitehorn smu_get_datablock(dev, SMU_CPUVOLT_CAL, data, sizeof(data)); 283204082Snwhitehorn sc->sc_cpu_volt_scale = (data[4] << 8) + data[5]; 284204082Snwhitehorn sc->sc_cpu_volt_offset = (data[6] << 8) + data[7]; 285204082Snwhitehorn sc->sc_cpu_curr_scale = (data[8] << 8) + data[9]; 286204082Snwhitehorn sc->sc_cpu_curr_offset = (data[10] << 8) + data[11]; 287204082Snwhitehorn 288204082Snwhitehorn smu_get_datablock(dev, SMU_SLOTPW_CAL, data, sizeof(data)); 289204082Snwhitehorn sc->sc_slots_pow_scale = (data[4] << 8) + data[5]; 290204082Snwhitehorn sc->sc_slots_pow_offset = (data[6] << 8) + data[7]; 291204082Snwhitehorn 292204180Snwhitehorn /* 293204180Snwhitehorn * Set up simple-minded thermal management. 294204180Snwhitehorn */ 295204180Snwhitehorn sc->sc_target_temp = 55; 296204180Snwhitehorn sc->sc_critical_temp = 90; 297204180Snwhitehorn 298204180Snwhitehorn SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 299204180Snwhitehorn SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 300204180Snwhitehorn "target_temp", CTLTYPE_INT | CTLFLAG_RW, &sc->sc_target_temp, 301204180Snwhitehorn sizeof(int), "Target temperature (C)"); 302204180Snwhitehorn SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 303204180Snwhitehorn SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 304204180Snwhitehorn "critical_temp", CTLTYPE_INT | CTLFLAG_RW, 305204180Snwhitehorn &sc->sc_critical_temp, sizeof(int), "Critical temperature (C)"); 306204180Snwhitehorn 307204180Snwhitehorn callout_init(&sc->sc_fanmgt_callout, 1); 308204180Snwhitehorn smu_fanmgt_callout(dev); 309204180Snwhitehorn 310204218Snwhitehorn /* 311204218Snwhitehorn * Set up LED interface 312204218Snwhitehorn */ 313204218Snwhitehorn sc->sc_leddev = led_create(smu_set_sleepled, dev, "sleepled"); 314204218Snwhitehorn 315204270Snwhitehorn /* 316204270Snwhitehorn * Reset on power loss behavior 317204270Snwhitehorn */ 318204270Snwhitehorn 319204270Snwhitehorn SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 320204270Snwhitehorn SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 321204270Snwhitehorn "server_mode", CTLTYPE_INT | CTLFLAG_RW, dev, 0, 322204270Snwhitehorn smu_server_mode, "I", "Enable reboot after power failure"); 323204270Snwhitehorn 324194679Snwhitehorn return (0); 325194679Snwhitehorn} 326194679Snwhitehorn 327194679Snwhitehornstatic int 328194679Snwhitehornsmu_run_cmd(device_t dev, struct smu_cmd *cmd) 329194679Snwhitehorn{ 330194679Snwhitehorn struct smu_softc *sc; 331204082Snwhitehorn int doorbell_ack, result, oldpow; 332194679Snwhitehorn 333194679Snwhitehorn sc = device_get_softc(dev); 334194679Snwhitehorn 335194679Snwhitehorn mtx_lock(&sc->sc_mtx); 336194679Snwhitehorn 337204082Snwhitehorn oldpow = powerpc_pow_enabled; 338204082Snwhitehorn powerpc_pow_enabled = 0; 339204082Snwhitehorn 340194679Snwhitehorn /* Copy the command to the mailbox */ 341194679Snwhitehorn memcpy(sc->sc_cmd, cmd, sizeof(*cmd)); 342194679Snwhitehorn bus_dmamap_sync(sc->sc_dmatag, sc->sc_cmd_dmamap, BUS_DMASYNC_PREWRITE); 343194679Snwhitehorn bus_space_write_4(sc->sc_bt, sc->sc_mailbox, 0, sc->sc_cmd_phys); 344194679Snwhitehorn 345204082Snwhitehorn /* Flush the cacheline it is in -- SMU bypasses the cache */ 346204082Snwhitehorn __asm __volatile("sync; dcbf 0,%0; sync" :: "r"(sc->sc_cmd): "memory"); 347194679Snwhitehorn 348194679Snwhitehorn /* Ring SMU doorbell */ 349194679Snwhitehorn macgpio_write(smu_doorbell, GPIO_DDR_OUTPUT); 350194679Snwhitehorn 351194679Snwhitehorn /* Wait for the doorbell GPIO to go high, signaling completion */ 352194679Snwhitehorn do { 353194679Snwhitehorn /* XXX: timeout */ 354194679Snwhitehorn DELAY(50); 355194679Snwhitehorn doorbell_ack = macgpio_read(smu_doorbell); 356204082Snwhitehorn } while (doorbell_ack != (GPIO_DDR_OUTPUT | GPIO_LEVEL_RO | GPIO_DATA)); 357194679Snwhitehorn 358194679Snwhitehorn /* Check result. First invalidate the cache again... */ 359194679Snwhitehorn __asm __volatile("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory"); 360194679Snwhitehorn 361194679Snwhitehorn bus_dmamap_sync(sc->sc_dmatag, sc->sc_cmd_dmamap, BUS_DMASYNC_POSTREAD); 362194679Snwhitehorn 363194679Snwhitehorn /* SMU acks the command by inverting the command bits */ 364204082Snwhitehorn if (sc->sc_cmd->cmd == ((~cmd->cmd) & 0xff)) 365194679Snwhitehorn result = 0; 366194679Snwhitehorn else 367194679Snwhitehorn result = EIO; 368194679Snwhitehorn 369204082Snwhitehorn powerpc_pow_enabled = oldpow; 370204082Snwhitehorn 371204082Snwhitehorn memcpy(cmd->data, sc->sc_cmd->data, sizeof(cmd->data)); 372204179Snwhitehorn cmd->len = sc->sc_cmd->len; 373204082Snwhitehorn 374194679Snwhitehorn mtx_unlock(&sc->sc_mtx); 375194679Snwhitehorn 376194679Snwhitehorn return (result); 377194679Snwhitehorn} 378194679Snwhitehorn 379204082Snwhitehornstatic int 380204082Snwhitehornsmu_get_datablock(device_t dev, int8_t id, uint8_t *buf, size_t len) 381204082Snwhitehorn{ 382204082Snwhitehorn struct smu_cmd cmd; 383204082Snwhitehorn uint8_t addr[4]; 384204082Snwhitehorn 385204082Snwhitehorn cmd.cmd = SMU_PARTITION; 386204082Snwhitehorn cmd.len = 2; 387204082Snwhitehorn cmd.data[0] = SMU_PARTITION_LATEST; 388204082Snwhitehorn cmd.data[1] = id; 389204082Snwhitehorn 390204082Snwhitehorn smu_run_cmd(dev, &cmd); 391204082Snwhitehorn 392204082Snwhitehorn addr[0] = addr[1] = 0; 393204082Snwhitehorn addr[2] = cmd.data[0]; 394204082Snwhitehorn addr[3] = cmd.data[1]; 395204082Snwhitehorn 396204082Snwhitehorn cmd.cmd = SMU_MISC; 397204082Snwhitehorn cmd.len = 7; 398204082Snwhitehorn cmd.data[0] = SMU_MISC_GET_DATA; 399204082Snwhitehorn cmd.data[1] = sizeof(addr); 400204082Snwhitehorn memcpy(&cmd.data[2], addr, sizeof(addr)); 401204082Snwhitehorn cmd.data[6] = len; 402204082Snwhitehorn 403204082Snwhitehorn smu_run_cmd(dev, &cmd); 404204082Snwhitehorn memcpy(buf, cmd.data, len); 405204082Snwhitehorn return (0); 406204082Snwhitehorn} 407204082Snwhitehorn 408194679Snwhitehornstatic void 409194679Snwhitehornsmu_slew_cpu_voltage(device_t dev, int to) 410194679Snwhitehorn{ 411194679Snwhitehorn struct smu_cmd cmd; 412194679Snwhitehorn 413194679Snwhitehorn cmd.cmd = SMU_POWER; 414194679Snwhitehorn cmd.len = 8; 415194679Snwhitehorn cmd.data[0] = 'V'; 416194679Snwhitehorn cmd.data[1] = 'S'; 417194679Snwhitehorn cmd.data[2] = 'L'; 418194679Snwhitehorn cmd.data[3] = 'E'; 419194679Snwhitehorn cmd.data[4] = 'W'; 420194679Snwhitehorn cmd.data[5] = 0xff; 421194679Snwhitehorn cmd.data[6] = 1; 422194679Snwhitehorn cmd.data[7] = to; 423194679Snwhitehorn 424194679Snwhitehorn smu_run_cmd(dev, &cmd); 425194679Snwhitehorn} 426194679Snwhitehorn 427194679Snwhitehornstatic void 428194679Snwhitehornsmu_cpufreq_pre_change(device_t dev, const struct cf_level *level) 429194679Snwhitehorn{ 430194679Snwhitehorn /* 431194679Snwhitehorn * Make sure the CPU voltage is raised before we raise 432194679Snwhitehorn * the clock. 433194679Snwhitehorn */ 434194679Snwhitehorn 435194679Snwhitehorn if (level->rel_set[0].freq == 10000 /* max */) 436194679Snwhitehorn smu_slew_cpu_voltage(dev, 0); 437194679Snwhitehorn} 438194679Snwhitehorn 439194679Snwhitehornstatic void 440194679Snwhitehornsmu_cpufreq_post_change(device_t dev, const struct cf_level *level) 441194679Snwhitehorn{ 442194679Snwhitehorn /* We are safe to reduce CPU voltage after a downward transition */ 443194679Snwhitehorn 444194679Snwhitehorn if (level->rel_set[0].freq < 10000 /* max */) 445194679Snwhitehorn smu_slew_cpu_voltage(dev, 1); /* XXX: 1/4 voltage for 970MP? */ 446194679Snwhitehorn} 447194679Snwhitehorn 448194679Snwhitehorn/* Routines for probing the SMU doorbell GPIO */ 449194679Snwhitehornstatic int doorbell_probe(device_t dev); 450194679Snwhitehornstatic int doorbell_attach(device_t dev); 451194679Snwhitehorn 452194679Snwhitehornstatic device_method_t doorbell_methods[] = { 453194679Snwhitehorn /* Device interface */ 454194679Snwhitehorn DEVMETHOD(device_probe, doorbell_probe), 455194679Snwhitehorn DEVMETHOD(device_attach, doorbell_attach), 456194679Snwhitehorn { 0, 0 }, 457194679Snwhitehorn}; 458194679Snwhitehorn 459194679Snwhitehornstatic driver_t doorbell_driver = { 460194679Snwhitehorn "smudoorbell", 461194679Snwhitehorn doorbell_methods, 462194679Snwhitehorn 0 463194679Snwhitehorn}; 464194679Snwhitehorn 465194679Snwhitehornstatic devclass_t doorbell_devclass; 466194679Snwhitehorn 467194679SnwhitehornDRIVER_MODULE(smudoorbell, macgpio, doorbell_driver, doorbell_devclass, 0, 0); 468194679Snwhitehorn 469194679Snwhitehornstatic int 470194679Snwhitehorndoorbell_probe(device_t dev) 471194679Snwhitehorn{ 472194679Snwhitehorn const char *name = ofw_bus_get_name(dev); 473194679Snwhitehorn 474194679Snwhitehorn if (strcmp(name, "smu-doorbell") != 0) 475194679Snwhitehorn return (ENXIO); 476194679Snwhitehorn 477194679Snwhitehorn device_set_desc(dev, "SMU Doorbell GPIO"); 478194679Snwhitehorn device_quiet(dev); 479194679Snwhitehorn return (0); 480194679Snwhitehorn} 481194679Snwhitehorn 482194679Snwhitehornstatic int 483194679Snwhitehorndoorbell_attach(device_t dev) 484194679Snwhitehorn{ 485194679Snwhitehorn smu_doorbell = dev; 486194679Snwhitehorn return (0); 487194679Snwhitehorn} 488204082Snwhitehorn 489204082Snwhitehorn/* 490204082Snwhitehorn * Sensor and fan management 491204082Snwhitehorn */ 492204082Snwhitehorn 493204082Snwhitehornstatic int 494204082Snwhitehornsmu_fan_set_rpm(device_t smu, struct smu_fan *fan, int rpm) 495204082Snwhitehorn{ 496204082Snwhitehorn struct smu_cmd cmd; 497204179Snwhitehorn int error; 498204082Snwhitehorn 499204082Snwhitehorn cmd.cmd = SMU_FAN; 500204179Snwhitehorn error = EIO; 501204082Snwhitehorn 502204179Snwhitehorn /* Clamp to allowed range */ 503204179Snwhitehorn rpm = max(fan->min_rpm, rpm); 504204179Snwhitehorn rpm = min(fan->max_rpm, rpm); 505204179Snwhitehorn 506204082Snwhitehorn /* 507204179Snwhitehorn * Apple has two fan control mechanisms. We can't distinguish 508204179Snwhitehorn * them except by seeing if the new one fails. If the new one 509204179Snwhitehorn * fails, use the old one. 510204082Snwhitehorn */ 511204179Snwhitehorn 512204179Snwhitehorn if (!fan->old_style) { 513204179Snwhitehorn cmd.len = 4; 514204179Snwhitehorn cmd.data[0] = 0x30; 515204179Snwhitehorn cmd.data[1] = fan->reg; 516204179Snwhitehorn cmd.data[2] = (rpm >> 8) & 0xff; 517204179Snwhitehorn cmd.data[3] = rpm & 0xff; 518204179Snwhitehorn 519204179Snwhitehorn error = smu_run_cmd(smu, &cmd); 520204179Snwhitehorn if (error) 521204179Snwhitehorn fan->old_style = 1; 522204179Snwhitehorn } 523204082Snwhitehorn 524204179Snwhitehorn if (fan->old_style) { 525204179Snwhitehorn cmd.len = 14; 526204179Snwhitehorn cmd.data[0] = 0; 527204179Snwhitehorn cmd.data[1] = 1 << fan->reg; 528204179Snwhitehorn cmd.data[2 + 2*fan->reg] = (rpm >> 8) & 0xff; 529204179Snwhitehorn cmd.data[3 + 2*fan->reg] = rpm & 0xff; 530204179Snwhitehorn error = smu_run_cmd(smu, &cmd); 531204179Snwhitehorn } 532204082Snwhitehorn 533204180Snwhitehorn if (error == 0) 534204180Snwhitehorn fan->setpoint = rpm; 535204180Snwhitehorn 536204179Snwhitehorn return (error); 537204082Snwhitehorn} 538204082Snwhitehorn 539204082Snwhitehornstatic int 540204082Snwhitehornsmu_fan_read_rpm(device_t smu, struct smu_fan *fan) 541204082Snwhitehorn{ 542204082Snwhitehorn struct smu_cmd cmd; 543204082Snwhitehorn 544204082Snwhitehorn cmd.cmd = SMU_FAN; 545204179Snwhitehorn cmd.len = 1; 546204082Snwhitehorn cmd.data[0] = 1; 547204082Snwhitehorn 548204082Snwhitehorn smu_run_cmd(smu, &cmd); 549204082Snwhitehorn 550204179Snwhitehorn return ((cmd.data[fan->reg*2+1] << 8) | cmd.data[fan->reg*2+2]); 551204082Snwhitehorn} 552204082Snwhitehorn 553204082Snwhitehornstatic int 554204082Snwhitehornsmu_fanrpm_sysctl(SYSCTL_HANDLER_ARGS) 555204082Snwhitehorn{ 556204082Snwhitehorn device_t smu; 557204082Snwhitehorn struct smu_softc *sc; 558204082Snwhitehorn struct smu_fan *fan; 559204082Snwhitehorn int rpm, error; 560204082Snwhitehorn 561204082Snwhitehorn smu = arg1; 562204082Snwhitehorn sc = device_get_softc(smu); 563204082Snwhitehorn fan = &sc->sc_fans[arg2]; 564204082Snwhitehorn 565204082Snwhitehorn rpm = smu_fan_read_rpm(smu, fan); 566204082Snwhitehorn error = sysctl_handle_int(oidp, &rpm, 0, req); 567204082Snwhitehorn 568204082Snwhitehorn if (error || !req->newptr) 569204082Snwhitehorn return (error); 570204082Snwhitehorn 571204180Snwhitehorn sc->sc_lastuserchange = time_uptime; 572204082Snwhitehorn 573204082Snwhitehorn return (smu_fan_set_rpm(smu, fan, rpm)); 574204082Snwhitehorn} 575204082Snwhitehorn 576204082Snwhitehornstatic void 577204082Snwhitehornsmu_attach_fans(device_t dev, phandle_t fanroot) 578204082Snwhitehorn{ 579204082Snwhitehorn struct smu_fan *fan; 580204082Snwhitehorn struct smu_softc *sc; 581204082Snwhitehorn struct sysctl_oid *oid, *fanroot_oid; 582204082Snwhitehorn struct sysctl_ctx_list *ctx; 583204082Snwhitehorn phandle_t child; 584204082Snwhitehorn char type[32], sysctl_name[32]; 585204082Snwhitehorn int i; 586204082Snwhitehorn 587204082Snwhitehorn sc = device_get_softc(dev); 588204082Snwhitehorn sc->sc_nfans = 0; 589204082Snwhitehorn 590204082Snwhitehorn for (child = OF_child(fanroot); child != 0; child = OF_peer(child)) 591204082Snwhitehorn sc->sc_nfans++; 592204082Snwhitehorn 593204082Snwhitehorn if (sc->sc_nfans == 0) { 594204082Snwhitehorn device_printf(dev, "WARNING: No fans detected!\n"); 595204082Snwhitehorn return; 596204082Snwhitehorn } 597204082Snwhitehorn 598204082Snwhitehorn sc->sc_fans = malloc(sc->sc_nfans * sizeof(struct smu_fan), M_SMU, 599204082Snwhitehorn M_WAITOK | M_ZERO); 600204082Snwhitehorn 601204082Snwhitehorn fan = sc->sc_fans; 602204082Snwhitehorn sc->sc_nfans = 0; 603204082Snwhitehorn 604204082Snwhitehorn ctx = device_get_sysctl_ctx(dev); 605204082Snwhitehorn fanroot_oid = SYSCTL_ADD_NODE(ctx, 606204082Snwhitehorn SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "fans", 607204082Snwhitehorn CTLFLAG_RD, 0, "SMU Fan Information"); 608204082Snwhitehorn 609204082Snwhitehorn for (child = OF_child(fanroot); child != 0; child = OF_peer(child)) { 610204082Snwhitehorn OF_getprop(child, "device_type", type, sizeof(type)); 611204082Snwhitehorn if (strcmp(type, "fan-rpm-control") != 0) 612204082Snwhitehorn continue; 613204082Snwhitehorn 614204179Snwhitehorn fan->old_style = 0; 615204082Snwhitehorn OF_getprop(child, "reg", &fan->reg, sizeof(cell_t)); 616204082Snwhitehorn OF_getprop(child, "min-value", &fan->min_rpm, sizeof(cell_t)); 617204082Snwhitehorn OF_getprop(child, "max-value", &fan->max_rpm, sizeof(cell_t)); 618204179Snwhitehorn 619204179Snwhitehorn if (OF_getprop(child, "unmanaged-value", &fan->unmanaged_rpm, 620204179Snwhitehorn sizeof(cell_t)) != sizeof(cell_t)) 621204179Snwhitehorn fan->unmanaged_rpm = fan->max_rpm; 622204179Snwhitehorn 623204180Snwhitehorn fan->setpoint = smu_fan_read_rpm(dev, fan); 624204180Snwhitehorn 625204082Snwhitehorn OF_getprop(child, "location", fan->location, 626204082Snwhitehorn sizeof(fan->location)); 627204180Snwhitehorn 628204082Snwhitehorn /* Add sysctls */ 629204082Snwhitehorn for (i = 0; i < strlen(fan->location); i++) { 630204082Snwhitehorn sysctl_name[i] = tolower(fan->location[i]); 631204082Snwhitehorn if (isspace(sysctl_name[i])) 632204082Snwhitehorn sysctl_name[i] = '_'; 633204082Snwhitehorn } 634204082Snwhitehorn sysctl_name[i] = 0; 635204082Snwhitehorn 636204082Snwhitehorn oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(fanroot_oid), 637204082Snwhitehorn OID_AUTO, sysctl_name, CTLFLAG_RD, 0, "Fan Information"); 638204082Snwhitehorn SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "minrpm", 639204082Snwhitehorn CTLTYPE_INT | CTLFLAG_RD, &fan->min_rpm, sizeof(cell_t), 640204082Snwhitehorn "Minimum allowed RPM"); 641204082Snwhitehorn SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "maxrpm", 642204082Snwhitehorn CTLTYPE_INT | CTLFLAG_RD, &fan->max_rpm, sizeof(cell_t), 643204082Snwhitehorn "Maximum allowed RPM"); 644204082Snwhitehorn SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "rpm", 645204082Snwhitehorn CTLTYPE_INT | CTLFLAG_RW, dev, sc->sc_nfans, 646204082Snwhitehorn smu_fanrpm_sysctl, "I", "Fan RPM"); 647204082Snwhitehorn 648204082Snwhitehorn fan++; 649204082Snwhitehorn sc->sc_nfans++; 650204082Snwhitehorn } 651204082Snwhitehorn} 652204082Snwhitehorn 653204082Snwhitehornstatic int 654204082Snwhitehornsmu_sensor_read(device_t smu, struct smu_sensor *sens) 655204082Snwhitehorn{ 656204082Snwhitehorn struct smu_cmd cmd; 657204082Snwhitehorn struct smu_softc *sc; 658204082Snwhitehorn int64_t value; 659204082Snwhitehorn 660204082Snwhitehorn cmd.cmd = SMU_ADC; 661204082Snwhitehorn cmd.len = 1; 662204082Snwhitehorn cmd.data[0] = sens->reg; 663204082Snwhitehorn 664204082Snwhitehorn smu_run_cmd(smu, &cmd); 665204082Snwhitehorn 666204082Snwhitehorn sc = device_get_softc(smu); 667204082Snwhitehorn value = (cmd.data[0] << 8) | cmd.data[1]; 668204082Snwhitehorn 669204082Snwhitehorn switch (sens->type) { 670204082Snwhitehorn case SMU_TEMP_SENSOR: 671204082Snwhitehorn value *= sc->sc_cpu_diode_scale; 672204082Snwhitehorn value >>= 3; 673204082Snwhitehorn value += ((int64_t)sc->sc_cpu_diode_offset) << 9; 674204082Snwhitehorn value <<= 1; 675204082Snwhitehorn 676204082Snwhitehorn /* Convert from 16.16 fixed point degC into integer C. */ 677204082Snwhitehorn value *= 15625; 678204082Snwhitehorn value /= 1024; 679204082Snwhitehorn value /= 1000000; 680204082Snwhitehorn break; 681204082Snwhitehorn case SMU_VOLTAGE_SENSOR: 682204082Snwhitehorn value *= sc->sc_cpu_volt_scale; 683204082Snwhitehorn value += sc->sc_cpu_volt_offset; 684204082Snwhitehorn value <<= 4; 685204082Snwhitehorn 686204082Snwhitehorn /* Convert from 16.16 fixed point V into mV. */ 687204082Snwhitehorn value *= 15625; 688204082Snwhitehorn value /= 1024; 689204082Snwhitehorn value /= 1000; 690204082Snwhitehorn break; 691204082Snwhitehorn case SMU_CURRENT_SENSOR: 692204082Snwhitehorn value *= sc->sc_cpu_curr_scale; 693204082Snwhitehorn value += sc->sc_cpu_curr_offset; 694204082Snwhitehorn value <<= 4; 695204082Snwhitehorn 696204082Snwhitehorn /* Convert from 16.16 fixed point A into mA. */ 697204082Snwhitehorn value *= 15625; 698204082Snwhitehorn value /= 1024; 699204082Snwhitehorn value /= 1000; 700204082Snwhitehorn break; 701204082Snwhitehorn case SMU_POWER_SENSOR: 702204082Snwhitehorn value *= sc->sc_slots_pow_scale; 703204082Snwhitehorn value += sc->sc_slots_pow_offset; 704204082Snwhitehorn value <<= 4; 705204082Snwhitehorn 706204082Snwhitehorn /* Convert from 16.16 fixed point W into mW. */ 707204082Snwhitehorn value *= 15625; 708204082Snwhitehorn value /= 1024; 709204082Snwhitehorn value /= 1000; 710204082Snwhitehorn break; 711204082Snwhitehorn } 712204082Snwhitehorn 713204082Snwhitehorn return (value); 714204082Snwhitehorn} 715204082Snwhitehorn 716204082Snwhitehornstatic int 717204082Snwhitehornsmu_sensor_sysctl(SYSCTL_HANDLER_ARGS) 718204082Snwhitehorn{ 719204082Snwhitehorn device_t smu; 720204082Snwhitehorn struct smu_softc *sc; 721204082Snwhitehorn struct smu_sensor *sens; 722204082Snwhitehorn int value, error; 723204082Snwhitehorn 724204082Snwhitehorn smu = arg1; 725204082Snwhitehorn sc = device_get_softc(smu); 726204082Snwhitehorn sens = &sc->sc_sensors[arg2]; 727204082Snwhitehorn 728204082Snwhitehorn value = smu_sensor_read(smu, sens); 729204082Snwhitehorn error = sysctl_handle_int(oidp, &value, 0, req); 730204082Snwhitehorn 731204082Snwhitehorn return (error); 732204082Snwhitehorn} 733204082Snwhitehorn 734204082Snwhitehornstatic void 735204082Snwhitehornsmu_attach_sensors(device_t dev, phandle_t sensroot) 736204082Snwhitehorn{ 737204082Snwhitehorn struct smu_sensor *sens; 738204082Snwhitehorn struct smu_softc *sc; 739204082Snwhitehorn struct sysctl_oid *sensroot_oid; 740204082Snwhitehorn struct sysctl_ctx_list *ctx; 741204082Snwhitehorn phandle_t child; 742204082Snwhitehorn char type[32]; 743204082Snwhitehorn int i; 744204082Snwhitehorn 745204082Snwhitehorn sc = device_get_softc(dev); 746204082Snwhitehorn sc->sc_nsensors = 0; 747204082Snwhitehorn 748204082Snwhitehorn for (child = OF_child(sensroot); child != 0; child = OF_peer(child)) 749204082Snwhitehorn sc->sc_nsensors++; 750204082Snwhitehorn 751204082Snwhitehorn if (sc->sc_nsensors == 0) { 752204082Snwhitehorn device_printf(dev, "WARNING: No sensors detected!\n"); 753204082Snwhitehorn return; 754204082Snwhitehorn } 755204082Snwhitehorn 756204179Snwhitehorn sc->sc_sensors = malloc(sc->sc_nsensors * sizeof(struct smu_sensor), 757204179Snwhitehorn M_SMU, M_WAITOK | M_ZERO); 758204082Snwhitehorn 759204082Snwhitehorn sens = sc->sc_sensors; 760204082Snwhitehorn sc->sc_nsensors = 0; 761204082Snwhitehorn 762204082Snwhitehorn ctx = device_get_sysctl_ctx(dev); 763204082Snwhitehorn sensroot_oid = SYSCTL_ADD_NODE(ctx, 764204082Snwhitehorn SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensors", 765204082Snwhitehorn CTLFLAG_RD, 0, "SMU Sensor Information"); 766204082Snwhitehorn 767204082Snwhitehorn for (child = OF_child(sensroot); child != 0; child = OF_peer(child)) { 768204082Snwhitehorn char sysctl_name[40], sysctl_desc[40]; 769204082Snwhitehorn const char *units; 770204082Snwhitehorn 771204082Snwhitehorn OF_getprop(child, "device_type", type, sizeof(type)); 772204082Snwhitehorn 773204082Snwhitehorn if (strcmp(type, "current-sensor") == 0) { 774204082Snwhitehorn sens->type = SMU_CURRENT_SENSOR; 775204082Snwhitehorn units = "mA"; 776204082Snwhitehorn } else if (strcmp(type, "temp-sensor") == 0) { 777204082Snwhitehorn sens->type = SMU_TEMP_SENSOR; 778204082Snwhitehorn units = "C"; 779204082Snwhitehorn } else if (strcmp(type, "voltage-sensor") == 0) { 780204082Snwhitehorn sens->type = SMU_VOLTAGE_SENSOR; 781204082Snwhitehorn units = "mV"; 782204082Snwhitehorn } else if (strcmp(type, "power-sensor") == 0) { 783204082Snwhitehorn sens->type = SMU_POWER_SENSOR; 784204082Snwhitehorn units = "mW"; 785204082Snwhitehorn } else { 786204082Snwhitehorn continue; 787204082Snwhitehorn } 788204082Snwhitehorn 789204082Snwhitehorn OF_getprop(child, "reg", &sens->reg, sizeof(cell_t)); 790204082Snwhitehorn OF_getprop(child, "location", sens->location, 791204082Snwhitehorn sizeof(sens->location)); 792204082Snwhitehorn 793204082Snwhitehorn for (i = 0; i < strlen(sens->location); i++) { 794204082Snwhitehorn sysctl_name[i] = tolower(sens->location[i]); 795204082Snwhitehorn if (isspace(sysctl_name[i])) 796204082Snwhitehorn sysctl_name[i] = '_'; 797204082Snwhitehorn } 798204082Snwhitehorn sysctl_name[i] = 0; 799204082Snwhitehorn 800204082Snwhitehorn sprintf(sysctl_desc,"%s (%s)", sens->location, units); 801204082Snwhitehorn 802204082Snwhitehorn SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sensroot_oid), OID_AUTO, 803204082Snwhitehorn sysctl_name, CTLTYPE_INT | CTLFLAG_RD, dev, sc->sc_nsensors, 804204082Snwhitehorn smu_sensor_sysctl, "I", sysctl_desc); 805204082Snwhitehorn 806204082Snwhitehorn sens++; 807204082Snwhitehorn sc->sc_nsensors++; 808204082Snwhitehorn } 809204082Snwhitehorn} 810204082Snwhitehorn 811204180Snwhitehornstatic int 812204180Snwhitehornms_to_ticks(int ms) 813204180Snwhitehorn{ 814204180Snwhitehorn if (hz > 1000) 815204180Snwhitehorn return ms*(hz/1000); 816204180Snwhitehorn 817204180Snwhitehorn return ms/(1000/hz); 818204180Snwhitehorn} 819204180Snwhitehorn 820204180Snwhitehornstatic void 821204180Snwhitehornsmu_fanmgt_callout(void *xdev) { 822204180Snwhitehorn device_t smu = xdev; 823204180Snwhitehorn struct smu_softc *sc; 824204180Snwhitehorn int i, maxtemp, temp, factor; 825204180Snwhitehorn 826204180Snwhitehorn sc = device_get_softc(smu); 827204180Snwhitehorn 828204180Snwhitehorn if (time_uptime - sc->sc_lastuserchange < 3) { 829204180Snwhitehorn /* 830204180Snwhitehorn * If we have heard from a user process in the last 3 seconds, 831204180Snwhitehorn * go away. 832204180Snwhitehorn */ 833204180Snwhitehorn 834204180Snwhitehorn callout_reset(&sc->sc_fanmgt_callout, 835204180Snwhitehorn ms_to_ticks(SMU_FANMGT_INTERVAL), smu_fanmgt_callout, smu); 836204180Snwhitehorn return; 837204180Snwhitehorn } 838204180Snwhitehorn 839204180Snwhitehorn maxtemp = 0; 840204180Snwhitehorn for (i = 0; i < sc->sc_nsensors; i++) { 841204180Snwhitehorn if (sc->sc_sensors[i].type != SMU_TEMP_SENSOR) 842204180Snwhitehorn continue; 843204180Snwhitehorn 844204180Snwhitehorn temp = smu_sensor_read(smu, &sc->sc_sensors[i]); 845204180Snwhitehorn if (temp > maxtemp) 846204180Snwhitehorn maxtemp = temp; 847204180Snwhitehorn } 848204180Snwhitehorn 849204180Snwhitehorn if (maxtemp < 10) { /* Bail if no good sensors */ 850204180Snwhitehorn for (i = 0; i < sc->sc_nfans; i++) 851204180Snwhitehorn smu_fan_set_rpm(smu, &sc->sc_fans[i], 852204180Snwhitehorn sc->sc_fans[i].unmanaged_rpm); 853204180Snwhitehorn return; 854204180Snwhitehorn } 855204180Snwhitehorn 856204180Snwhitehorn if (maxtemp > sc->sc_critical_temp) { 857204180Snwhitehorn device_printf(smu, "WARNING: Current system temperature (%d C) " 858204180Snwhitehorn "exceeds critical temperature (%d C)! Shutting down!\n", 859204180Snwhitehorn maxtemp, sc->sc_critical_temp); 860204180Snwhitehorn shutdown_nice(RB_POWEROFF); 861204180Snwhitehorn } 862204180Snwhitehorn 863204180Snwhitehorn if (maxtemp - sc->sc_target_temp > 20) 864204180Snwhitehorn device_printf(smu, "WARNING: Current system temperature (%d C) " 865204180Snwhitehorn "more than 20 degrees over target temperature (%d C)!\n", 866204180Snwhitehorn maxtemp, sc->sc_target_temp); 867204180Snwhitehorn 868204180Snwhitehorn if (maxtemp > sc->sc_target_temp) 869204180Snwhitehorn factor = 110; 870204180Snwhitehorn else if (sc->sc_target_temp - maxtemp > 4) 871204180Snwhitehorn factor = 90; 872204180Snwhitehorn else if (sc->sc_target_temp - maxtemp > 1) 873204180Snwhitehorn factor = 95; 874204180Snwhitehorn else 875204180Snwhitehorn factor = 100; 876204180Snwhitehorn 877204180Snwhitehorn for (i = 0; i < sc->sc_nfans; i++) 878204180Snwhitehorn smu_fan_set_rpm(smu, &sc->sc_fans[i], 879204180Snwhitehorn (sc->sc_fans[i].setpoint * factor) / 100); 880204180Snwhitehorn 881204180Snwhitehorn callout_reset(&sc->sc_fanmgt_callout, 882204180Snwhitehorn ms_to_ticks(SMU_FANMGT_INTERVAL), smu_fanmgt_callout, smu); 883204180Snwhitehorn} 884204180Snwhitehorn 885204218Snwhitehornstatic void 886204218Snwhitehornsmu_set_sleepled(void *xdev, int onoff) 887204218Snwhitehorn{ 888204218Snwhitehorn struct smu_cmd cmd; 889204218Snwhitehorn device_t smu = xdev; 890204218Snwhitehorn 891204218Snwhitehorn cmd.cmd = SMU_MISC; 892204218Snwhitehorn cmd.len = 3; 893204218Snwhitehorn cmd.data[0] = SMU_MISC_LED_CTRL; 894204218Snwhitehorn cmd.data[1] = 0; 895204218Snwhitehorn cmd.data[2] = onoff; 896204218Snwhitehorn 897204218Snwhitehorn smu_run_cmd(smu, &cmd); 898204218Snwhitehorn} 899204218Snwhitehorn 900204270Snwhitehornstatic int 901204270Snwhitehornsmu_server_mode(SYSCTL_HANDLER_ARGS) 902204270Snwhitehorn{ 903204270Snwhitehorn struct smu_cmd cmd; 904204270Snwhitehorn u_int server_mode; 905204270Snwhitehorn device_t smu = arg1; 906204270Snwhitehorn int error; 907204270Snwhitehorn 908204270Snwhitehorn cmd.cmd = SMU_POWER_EVENTS; 909204270Snwhitehorn cmd.len = 1; 910204270Snwhitehorn cmd.data[0] = SMU_PWR_GET_POWERUP; 911204270Snwhitehorn 912204270Snwhitehorn error = smu_run_cmd(smu, &cmd); 913204270Snwhitehorn 914204270Snwhitehorn if (error) 915204270Snwhitehorn return (error); 916204270Snwhitehorn 917204270Snwhitehorn server_mode = (cmd.data[1] & SMU_WAKEUP_AC_INSERT) ? 1 : 0; 918204270Snwhitehorn 919204270Snwhitehorn error = sysctl_handle_int(oidp, &server_mode, 0, req); 920204270Snwhitehorn 921204270Snwhitehorn if (error || !req->newptr) 922204270Snwhitehorn return (error); 923204270Snwhitehorn 924204270Snwhitehorn if (server_mode == 1) 925204270Snwhitehorn cmd.data[0] = SMU_PWR_SET_POWERUP; 926204270Snwhitehorn else if (server_mode == 0) 927204270Snwhitehorn cmd.data[0] = SMU_PWR_CLR_POWERUP; 928204270Snwhitehorn else 929204270Snwhitehorn return (EINVAL); 930204270Snwhitehorn 931204270Snwhitehorn cmd.len = 3; 932204270Snwhitehorn cmd.data[1] = 0; 933204270Snwhitehorn cmd.data[2] = SMU_WAKEUP_AC_INSERT; 934204270Snwhitehorn 935204270Snwhitehorn return (smu_run_cmd(smu, &cmd)); 936204270Snwhitehorn} 937204270Snwhitehorn 938