asmc.c revision 193530
138465Smsmith/*- 238465Smsmith * Copyright (c) 2007, 2008 Rui Paulo <rpaulo@FreeBSD.org> 338465Smsmith * All rights reserved. 438465Smsmith * 538465Smsmith * Redistribution and use in source and binary forms, with or without 638465Smsmith * modification, are permitted provided that the following conditions 738465Smsmith * are met: 838465Smsmith * 1. Redistributions of source code must retain the above copyright 938465Smsmith * notice, this list of conditions and the following disclaimer. 1038465Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1138465Smsmith * notice, this list of conditions and the following disclaimer in the 1238465Smsmith * documentation and/or other materials provided with the distribution. 1338465Smsmith * 1438465Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1538465Smsmith * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 1638465Smsmith * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 1738465Smsmith * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 1838465Smsmith * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 1938465Smsmith * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 2038465Smsmith * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2138465Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 2238465Smsmith * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 2338465Smsmith * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2438465Smsmith * POSSIBILITY OF SUCH DAMAGE. 2538465Smsmith * 2638465Smsmith */ 27119482Sobrien 28119482Sobrien/* 29119482Sobrien * Driver for Apple's System Management Console (SMC). 3038465Smsmith * SMC can be found on the MacBook, MacBook Pro and Mac Mini. 3138465Smsmith * 3238465Smsmith * Inspired by the Linux applesmc driver. 3338465Smsmith */ 3438465Smsmith 3538465Smsmith#include <sys/cdefs.h> 36235158Savg__FBSDID("$FreeBSD: head/sys/dev/asmc/asmc.c 193530 2009-06-05 18:44:36Z jkim $"); 3738465Smsmith 3839664Smsmith#include <sys/param.h> 39240637Savg#include <sys/bus.h> 40181436Sjhb#include <sys/conf.h> 4139664Smsmith#include <sys/kernel.h> 4238465Smsmith#include <sys/lock.h> 4338465Smsmith#include <sys/malloc.h> 44235154Savg#include <sys/module.h> 4538465Smsmith#include <sys/mutex.h> 46281138Srpaulo#include <sys/sysctl.h> 4739441Smsmith#include <sys/systm.h> 4838465Smsmith#include <sys/taskqueue.h> 49235329Savg#include <sys/rman.h> 50235329Savg 51235329Savg#include <machine/resource.h> 52235329Savg 53235154Savg#include <contrib/dev/acpica/include/acpi.h> 54235154Savg 55235154Savg#include <dev/acpica/acpivar.h> 56235154Savg#include <dev/asmc/asmcvar.h> 5756693Sjhb 5839664Smsmith#include "opt_intr_filter.h" 59235154Savg 6039664Smsmith/* 6139944Smsmith * Device interface. 6239944Smsmith */ 6339944Smsmithstatic int asmc_probe(device_t dev); 6439664Smsmithstatic int asmc_attach(device_t dev); 6538465Smsmithstatic int asmc_detach(device_t dev); 6638465Smsmith 6739897Smsmith/* 6840555Smsmith * SMC functions. 6940555Smsmith */ 7064187Sjhbstatic int asmc_init(device_t dev); 71296963Sallanjudestatic int asmc_wait(device_t dev, uint8_t val); 72296963Sallanjudestatic int asmc_key_write(device_t dev, const char *key, uint8_t *buf, 73296963Sallanjude uint8_t len); 74235329Savgstatic int asmc_key_read(device_t dev, const char *key, uint8_t *buf, 75296963Sallanjude uint8_t); 76235329Savgstatic int asmc_fan_count(device_t dev); 77235329Savgstatic int asmc_fan_getvalue(device_t dev, const char *key, int fan); 7839897Smsmithstatic int asmc_temp_getvalue(device_t dev, const char *key); 7938465Smsmithstatic int asmc_sms_read(device_t, const char *key, int16_t *val); 8038465Smsmithstatic void asmc_sms_calibrate(device_t dev); 8138465Smsmithstatic int asmc_sms_intrfast(void *arg); 8238465Smsmith#ifdef INTR_FILTER 8338465Smsmithstatic void asmc_sms_handler(void *arg); 8438465Smsmith#endif 85153536Ssobomaxstatic void asmc_sms_printintr(device_t dev, uint8_t); 86153536Ssobomaxstatic void asmc_sms_task(void *arg, int pending); 87153536Ssobomax 8864187Sjhb/* 8938465Smsmith * Model functions. 9038465Smsmith */ 9138465Smsmithstatic int asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS); 9239178Smsmithstatic int asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS); 9339664Smsmithstatic int asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS); 9439664Smsmithstatic int asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS); 9539944Smsmithstatic int asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS); 9639944Smsmithstatic int asmc_temp_sysctl(SYSCTL_HANDLER_ARGS); 9758713Sjhbstatic int asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS); 9839664Smsmithstatic int asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS); 99181436Sjhbstatic int asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS); 100181436Sjhbstatic int asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS); 101181436Sjhbstatic int asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS); 102181436Sjhb 10338465Smsmithstruct asmc_model { 10439441Smsmith const char *smc_model; /* smbios.system.product env var. */ 10538465Smsmith const char *smc_desc; /* driver description */ 10655211Smsmith 10759087Sps /* Helper functions */ 108200216Sjhb int (*smc_sms_x)(SYSCTL_HANDLER_ARGS); 109200216Sjhb int (*smc_sms_y)(SYSCTL_HANDLER_ARGS); 110200219Sjhb int (*smc_sms_z)(SYSCTL_HANDLER_ARGS); 111200219Sjhb int (*smc_fan_speed)(SYSCTL_HANDLER_ARGS); 112200219Sjhb int (*smc_fan_safespeed)(SYSCTL_HANDLER_ARGS); 113200219Sjhb int (*smc_fan_minspeed)(SYSCTL_HANDLER_ARGS); 114200219Sjhb int (*smc_fan_maxspeed)(SYSCTL_HANDLER_ARGS); 115200219Sjhb int (*smc_fan_targetspeed)(SYSCTL_HANDLER_ARGS); 116153536Ssobomax int (*smc_light_left)(SYSCTL_HANDLER_ARGS); 117200219Sjhb int (*smc_light_right)(SYSCTL_HANDLER_ARGS); 118200219Sjhb 119200219Sjhb const char *smc_temps[ASMC_TEMP_MAX]; 120200219Sjhb const char *smc_tempnames[ASMC_TEMP_MAX]; 121153536Ssobomax const char *smc_tempdescs[ASMC_TEMP_MAX]; 12258713Sjhb}; 123281138Srpaulo 12438465Smsmithstatic struct asmc_model *asmc_match(device_t dev); 12538465Smsmith 12638465Smsmith#define ASMC_SMS_FUNCS asmc_mb_sysctl_sms_x, asmc_mb_sysctl_sms_y, \ 12738465Smsmith asmc_mb_sysctl_sms_z 12838465Smsmith 12939664Smsmith#define ASMC_FAN_FUNCS asmc_mb_sysctl_fanspeed, asmc_mb_sysctl_fansafespeed, \ 13038465Smsmith asmc_mb_sysctl_fanminspeed, \ 131150470Sru asmc_mb_sysctl_fanmaxspeed, \ 132146698Sjhb asmc_mb_sysctl_fantargetspeed 133146698Sjhb#define ASMC_LIGHT_FUNCS asmc_mbp_sysctl_light_left, \ 134146698Sjhb asmc_mbp_sysctl_light_right 135146698Sjhb 136146698Sjhbstruct asmc_model asmc_models[] = { 137146698Sjhb { 13839725Speter "MacBook1,1", "Apple SMC MacBook Core Duo", 139146698Sjhb ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, 14066133Sarchie ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS 14138465Smsmith }, 14238465Smsmith 14338465Smsmith { 144298230Sallanjude "MacBook2,1", "Apple SMC MacBook Core 2 Duo", 14540834Smsmith ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, 146298230Sallanjude ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS 14740834Smsmith }, 14840834Smsmith 14986094Sjhb { 15058713Sjhb "MacBookPro1,1", "Apple SMC MacBook Pro Core Duo (15-inch)", 151126958Sbde ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 15286094Sjhb ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 15386094Sjhb }, 15486094Sjhb 15586094Sjhb { 15686094Sjhb "MacBookPro1,2", "Apple SMC MacBook Pro Core Duo (17-inch)", 15786094Sjhb ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 15886094Sjhb ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 15986094Sjhb }, 16058713Sjhb 16158713Sjhb { 162185029Spjd "MacBookPro2,1", "Apple SMC MacBook Pro Core 2 Duo (17-inch)", 163185029Spjd ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 164185029Spjd ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 165185029Spjd }, 166185029Spjd 167185029Spjd { 168185029Spjd "MacBookPro2,2", "Apple SMC MacBook Pro Core 2 Duo (15-inch)", 169235329Savg ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 170235329Savg ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 171185029Spjd }, 172296963Sallanjude 173296963Sallanjude { 174296963Sallanjude "MacBookPro3,1", "Apple SMC MacBook Pro Core 2 Duo (15-inch LED)", 175296963Sallanjude ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 176296963Sallanjude ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 177296963Sallanjude }, 178296963Sallanjude 179296963Sallanjude { 180296963Sallanjude "MacBookPro3,2", "Apple SMC MacBook Pro Core 2 Duo (17-inch HD)", 181296963Sallanjude ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 182296963Sallanjude ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 183296963Sallanjude }, 184296963Sallanjude 185296963Sallanjude /* The Mac Mini has no SMS */ 186296963Sallanjude { 187296963Sallanjude "Macmini1,1", "Apple SMC Mac Mini", 188296963Sallanjude NULL, NULL, NULL, 189296963Sallanjude ASMC_FAN_FUNCS, 190296963Sallanjude NULL, NULL, 191296963Sallanjude ASMC_MM_TEMPS, ASMC_MM_TEMPNAMES, ASMC_MM_TEMPDESCS 192296963Sallanjude }, 193296963Sallanjude 194296963Sallanjude /* Idem for the MacPro */ 195296963Sallanjude { 196296963Sallanjude "MacPro2", "Apple SMC Mac Pro (8-core)", 19758713Sjhb NULL, NULL, NULL, 19838465Smsmith ASMC_FAN_FUNCS, 19938465Smsmith NULL, NULL, 20038465Smsmith ASMC_MP_TEMPS, ASMC_MP_TEMPNAMES, ASMC_MP_TEMPDESCS 20138465Smsmith }, 20238465Smsmith 20355211Smsmith { 204104315Siwasaki "MacBookAir1,1", "Apple SMC MacBook Air", 205104315Siwasaki ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, 206104315Siwasaki ASMC_MBA_TEMPS, ASMC_MBA_TEMPNAMES, ASMC_MBA_TEMPDESCS 207104315Siwasaki }, 20838465Smsmith 20982531Smsmith 21082531Smsmith { NULL, NULL } 21182531Smsmith}; 212148006Sjkim 213281138Srpaulo#undef ASMC_SMS_FUNCS 214148006Sjkim#undef ASMC_FAN_FUNCS 215271406Simp#undef ASMC_LIGHT_FUNCS 216271406Simp 217271406Simp/* 21838465Smsmith * Driver methods. 21955211Smsmith */ 22038465Smsmithstatic device_method_t asmc_methods[] = { 22139450Smsmith DEVMETHOD(device_probe, asmc_probe), 22239897Smsmith DEVMETHOD(device_attach, asmc_attach), 22339897Smsmith DEVMETHOD(device_detach, asmc_detach), 22438465Smsmith 225114379Speter { 0, 0 } 226114379Speter}; 227269153Smarcel 22864187Sjhbstatic driver_t asmc_driver = { 22964187Sjhb "asmc", 23064187Sjhb asmc_methods, 23138465Smsmith sizeof(struct asmc_softc) 23238465Smsmith}; 23339897Smsmith 23439897Smsmith/* 23539897Smsmith * Debugging 23639897Smsmith */ 23739897Smsmith#define _COMPONENT ACPI_OEM 23839897SmsmithACPI_MODULE_NAME("ASMC") 23939897Smsmith#ifdef DEBUG 24039897Smsmith#define ASMC_DPRINTF(str) device_printf(dev, str) 24139897Smsmith#else 242235329Savg#define ASMC_DPRINTF(str) 243235329Savg#endif 244241293Savg 245235329Savgstatic char *asmc_ids[] = { "APP0001", NULL }; 246235329Savg 24739897Smsmithstatic devclass_t asmc_devclass; 24858713Sjhb 24964187SjhbDRIVER_MODULE(asmc, acpi, asmc_driver, asmc_devclass, NULL, NULL); 25039897SmsmithMODULE_DEPEND(asmc, acpi, 1, 1, 1); 25158713Sjhb 252126958Sbdestatic struct asmc_model * 25358713Sjhbasmc_match(device_t dev) 25486094Sjhb{ 25586094Sjhb int i; 256163897Smarcel char *model; 25758713Sjhb 25858713Sjhb model = getenv("smbios.system.product"); 25964187Sjhb if (model == NULL) 260163897Smarcel return (NULL); 26158713Sjhb 26258713Sjhb for (i = 0; asmc_models[i].smc_model; i++) { 26364187Sjhb if (!strncmp(model, asmc_models[i].smc_model, strlen(model))) { 26464187Sjhb freeenv(model); 26558713Sjhb return (&asmc_models[i]); 26658713Sjhb } 267235329Savg } 268235329Savg freeenv(model); 269235329Savg 270235329Savg return (NULL); 271235329Savg} 272235329Savg 273235329Savgstatic int 274241293Savgasmc_probe(device_t dev) 275241293Savg{ 276235329Savg struct asmc_model *model; 277235329Savg 278235329Savg if (resource_disabled("asmc", 0)) 279241293Savg return (ENXIO); 280241293Savg if (ACPI_ID_PROBE(device_get_parent(dev), dev, asmc_ids) == NULL) 281241293Savg return (ENXIO); 282241293Savg 283241293Savg model = asmc_match(dev); 284241293Savg if (!model) { 285235329Savg device_printf(dev, "model not recognized\n"); 286235329Savg return (ENXIO); 287235329Savg } 288235329Savg device_set_desc(dev, model->smc_desc); 289235329Savg 290235329Savg return (BUS_PROBE_DEFAULT); 291235329Savg} 29256693Sjhb 29339944Smsmithstatic int 29464187Sjhbasmc_attach(device_t dev) 29564187Sjhb{ 29639944Smsmith int i, j; 29739944Smsmith int ret; 298172921Sjhb char name[2]; 29964187Sjhb struct asmc_softc *sc = device_get_softc(dev); 30039944Smsmith struct sysctl_ctx_list *sysctlctx; 30139897Smsmith struct sysctl_oid *sysctlnode; 30239944Smsmith struct asmc_model *model; 30339944Smsmith 304160964Syar sc->sc_ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT, 30539944Smsmith &sc->sc_rid_port, RF_ACTIVE); 30639944Smsmith if (sc->sc_ioport == NULL) { 30739944Smsmith device_printf(dev, "unable to allocate IO port\n"); 30839944Smsmith return (ENOMEM); 30939944Smsmith } 31039944Smsmith 31186094Sjhb sysctlctx = device_get_sysctl_ctx(dev); 312235329Savg sysctlnode = device_get_sysctl_tree(dev); 31358713Sjhb 31458713Sjhb model = asmc_match(dev); 31558713Sjhb 31658713Sjhb mtx_init(&sc->sc_mtx, "asmc", NULL, MTX_SPIN); 31786094Sjhb 318163897Smarcel sc->sc_model = model; 31939944Smsmith asmc_init(dev); 32039944Smsmith 321163897Smarcel /* 32239897Smsmith * dev.asmc.n.fan.* tree. 323235329Savg */ 324293612Sallanjude sc->sc_fan_tree[0] = SYSCTL_ADD_NODE(sysctlctx, 325293612Sallanjude SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "fan", 326293612Sallanjude CTLFLAG_RD, 0, "Fan Root Tree"); 327293612Sallanjude 328293612Sallanjude for (i = 1; i <= sc->sc_nfan; i++) { 32964187Sjhb j = i - 1; 33064187Sjhb name[0] = '0' + j; 33164187Sjhb name[1] = 0; 33264187Sjhb sc->sc_fan_tree[i] = SYSCTL_ADD_NODE(sysctlctx, 33339897Smsmith SYSCTL_CHILDREN(sc->sc_fan_tree[0]), 33439897Smsmith OID_AUTO, name, CTLFLAG_RD, 0, 33538465Smsmith "Fan Subtree"); 33638465Smsmith 33738465Smsmith SYSCTL_ADD_PROC(sysctlctx, 33838465Smsmith SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 33938465Smsmith OID_AUTO, "speed", CTLTYPE_INT | CTLFLAG_RD, 34059408Sps dev, j, model->smc_fan_speed, "I", 34138465Smsmith "Fan speed in RPM"); 34259408Sps 34359408Sps SYSCTL_ADD_PROC(sysctlctx, 34459408Sps SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 34559408Sps OID_AUTO, "safespeed", 34638465Smsmith CTLTYPE_INT | CTLFLAG_RD, 34738465Smsmith dev, j, model->smc_fan_safespeed, "I", 34839441Smsmith "Fan safe speed in RPM"); 34938465Smsmith 35038465Smsmith SYSCTL_ADD_PROC(sysctlctx, 35139450Smsmith SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 35239441Smsmith OID_AUTO, "minspeed", 35339441Smsmith CTLTYPE_INT | CTLFLAG_RD, 35439441Smsmith dev, j, model->smc_fan_minspeed, "I", 35539441Smsmith "Fan minimum speed in RPM"); 35639441Smsmith 35739441Smsmith SYSCTL_ADD_PROC(sysctlctx, 35838465Smsmith SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 35938465Smsmith OID_AUTO, "maxspeed", 36038465Smsmith CTLTYPE_INT | CTLFLAG_RD, 36138465Smsmith dev, j, model->smc_fan_maxspeed, "I", 36238465Smsmith "Fan maximum speed in RPM"); 36339664Smsmith 364153536Ssobomax SYSCTL_ADD_PROC(sysctlctx, 365153536Ssobomax SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 36638465Smsmith OID_AUTO, "targetspeed", 36738465Smsmith CTLTYPE_INT | CTLFLAG_RD, 36840555Smsmith dev, j, model->smc_fan_targetspeed, "I", 369241284Savg "Fan target speed in RPM"); 370241284Savg } 371241284Savg 372241284Savg /* 373241284Savg * dev.asmc.n.temp tree. 374241284Savg */ 375241284Savg sc->sc_temp_tree = SYSCTL_ADD_NODE(sysctlctx, 376241284Savg SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "temp", 377241284Savg CTLFLAG_RD, 0, "Temperature sensors"); 378241284Savg 379241284Savg for (i = 0; model->smc_temps[i]; i++) { 380241284Savg SYSCTL_ADD_PROC(sysctlctx, 381241284Savg SYSCTL_CHILDREN(sc->sc_temp_tree), 382241284Savg OID_AUTO, model->smc_tempnames[i], 383241284Savg CTLTYPE_INT | CTLFLAG_RD, 384241284Savg dev, i, asmc_temp_sysctl, "I", 385241284Savg model->smc_tempdescs[i]); 386241284Savg } 387241284Savg 388293001Sallanjude if (model->smc_sms_x == NULL) 389241284Savg goto nosms; 390241284Savg 391293001Sallanjude /* 392293001Sallanjude * dev.asmc.n.sms tree. 393293001Sallanjude */ 394293001Sallanjude sc->sc_sms_tree = SYSCTL_ADD_NODE(sysctlctx, 395293001Sallanjude SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "sms", 396293001Sallanjude CTLFLAG_RD, 0, "Sudden Motion Sensor"); 397293001Sallanjude 398293001Sallanjude SYSCTL_ADD_PROC(sysctlctx, 399293454Sallanjude SYSCTL_CHILDREN(sc->sc_sms_tree), 400293001Sallanjude OID_AUTO, "x", CTLTYPE_INT | CTLFLAG_RD, 401293001Sallanjude dev, 0, model->smc_sms_x, "I", 402293001Sallanjude "Sudden Motion Sensor X value"); 403293001Sallanjude 404293001Sallanjude SYSCTL_ADD_PROC(sysctlctx, 405293001Sallanjude SYSCTL_CHILDREN(sc->sc_sms_tree), 406293001Sallanjude OID_AUTO, "y", CTLTYPE_INT | CTLFLAG_RD, 407293001Sallanjude dev, 0, model->smc_sms_y, "I", 408293001Sallanjude "Sudden Motion Sensor Y value"); 409293454Sallanjude 410293454Sallanjude SYSCTL_ADD_PROC(sysctlctx, 411293454Sallanjude SYSCTL_CHILDREN(sc->sc_sms_tree), 412293454Sallanjude OID_AUTO, "z", CTLTYPE_INT | CTLFLAG_RD, 413293454Sallanjude dev, 0, model->smc_sms_z, "I", 414293001Sallanjude "Sudden Motion Sensor Z value"); 415293001Sallanjude 416293001Sallanjude /* 417293001Sallanjude * dev.asmc.n.light 418293001Sallanjude */ 419293001Sallanjude if (model->smc_light_left) { 420293001Sallanjude sc->sc_light_tree = SYSCTL_ADD_NODE(sysctlctx, 421293001Sallanjude SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "light", 422293001Sallanjude CTLFLAG_RD, 0, "Keyboard backlight sensors"); 423293001Sallanjude 424241284Savg SYSCTL_ADD_PROC(sysctlctx, 425241284Savg SYSCTL_CHILDREN(sc->sc_light_tree), 426240637Savg OID_AUTO, "left", CTLTYPE_INT | CTLFLAG_RW, 427240341Savg dev, 0, model->smc_light_left, "I", 42840555Smsmith "Keyboard backlight left sensor"); 42940555Smsmith 430240341Savg SYSCTL_ADD_PROC(sysctlctx, 431240637Savg SYSCTL_CHILDREN(sc->sc_light_tree), 43240555Smsmith OID_AUTO, "right", CTLTYPE_INT | CTLFLAG_RW, 43340555Smsmith dev, 0, model->smc_light_right, "I", 43440555Smsmith "Keyboard backlight right sensor"); 43540555Smsmith } 43640555Smsmith 437240341Savg /* 438240637Savg * Need a taskqueue to send devctl_notify() events 43940555Smsmith * when the SMS interrupt us. 44040555Smsmith * 441235329Savg * PI_REALTIME is used due to the sensitivity of the 442235329Savg * interrupt. An interrupt from the SMS means that the 443235329Savg * disk heads should be turned off as quickly as possible. 444235329Savg * 445235329Savg * We only need to do this for the non INTR_FILTER case. 446239068Sae */ 447235329Savg sc->sc_sms_tq = NULL; 448235329Savg#ifndef INTR_FILTER 449235329Savg TASK_INIT(&sc->sc_sms_task, 0, asmc_sms_task, sc); 450239068Sae sc->sc_sms_tq = taskqueue_create_fast("asmc_taskq", M_WAITOK, 451235329Savg taskqueue_thread_enqueue, &sc->sc_sms_tq); 452235329Savg taskqueue_start_threads(&sc->sc_sms_tq, 1, PI_REALTIME, "%s sms taskq", 453239068Sae device_get_nameunit(dev)); 454239068Sae#endif 455235329Savg /* 456239068Sae * Allocate an IRQ for the SMS. 457235329Savg */ 458235329Savg sc->sc_rid_irq = 0; 459235329Savg sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, 460 &sc->sc_rid_irq, RF_ACTIVE); 461 if (sc->sc_irq == NULL) { 462 device_printf(dev, "unable to allocate IRQ resource\n"); 463 ret = ENXIO; 464 goto err2; 465 } 466 467 ret = bus_setup_intr(dev, sc->sc_irq, 468 INTR_TYPE_MISC | INTR_MPSAFE, 469#ifdef INTR_FILTER 470 asmc_sms_intrfast, asmc_sms_handler, 471#else 472 asmc_sms_intrfast, NULL, 473#endif 474 dev, &sc->sc_cookie); 475 476 if (ret) { 477 device_printf(dev, "unable to setup SMS IRQ\n"); 478 goto err1; 479 } 480nosms: 481 return (0); 482err1: 483 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq, sc->sc_irq); 484err2: 485 bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port, 486 sc->sc_ioport); 487 mtx_destroy(&sc->sc_mtx); 488 if (sc->sc_sms_tq) 489 taskqueue_free(sc->sc_sms_tq); 490 491 return (ret); 492} 493 494static int 495asmc_detach(device_t dev) 496{ 497 struct asmc_softc *sc = device_get_softc(dev); 498 499 if (sc->sc_sms_tq) { 500 taskqueue_drain(sc->sc_sms_tq, &sc->sc_sms_task); 501 taskqueue_free(sc->sc_sms_tq); 502 } 503 if (sc->sc_cookie) 504 bus_teardown_intr(dev, sc->sc_irq, sc->sc_cookie); 505 if (sc->sc_irq) 506 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq, 507 sc->sc_irq); 508 if (sc->sc_ioport) 509 bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port, 510 sc->sc_ioport); 511 mtx_destroy(&sc->sc_mtx); 512 513 return (0); 514} 515 516static int 517asmc_init(device_t dev) 518{ 519 struct asmc_softc *sc = device_get_softc(dev); 520 int i, error = 1; 521 uint8_t buf[4]; 522 523 if (sc->sc_model->smc_sms_x == NULL) 524 goto nosms; 525 526 /* 527 * We are ready to recieve interrupts from the SMS. 528 */ 529 buf[0] = 0x01; 530 ASMC_DPRINTF(("intok key\n")); 531 asmc_key_write(dev, ASMC_KEY_INTOK, buf, 1); 532 DELAY(50); 533 534 /* 535 * Initiate the polling intervals. 536 */ 537 buf[0] = 20; /* msecs */ 538 ASMC_DPRINTF(("low int key\n")); 539 asmc_key_write(dev, ASMC_KEY_SMS_LOW_INT, buf, 1); 540 DELAY(200); 541 542 buf[0] = 20; /* msecs */ 543 ASMC_DPRINTF(("high int key\n")); 544 asmc_key_write(dev, ASMC_KEY_SMS_HIGH_INT, buf, 1); 545 DELAY(200); 546 547 buf[0] = 0x00; 548 buf[1] = 0x60; 549 ASMC_DPRINTF(("sms low key\n")); 550 asmc_key_write(dev, ASMC_KEY_SMS_LOW, buf, 2); 551 DELAY(200); 552 553 buf[0] = 0x01; 554 buf[1] = 0xc0; 555 ASMC_DPRINTF(("sms high key\n")); 556 asmc_key_write(dev, ASMC_KEY_SMS_HIGH, buf, 2); 557 DELAY(200); 558 559 /* 560 * I'm not sure what this key does, but it seems to be 561 * required. 562 */ 563 buf[0] = 0x01; 564 ASMC_DPRINTF(("sms flag key\n")); 565 asmc_key_write(dev, ASMC_KEY_SMS_FLAG, buf, 1); 566 DELAY(100); 567 568 /* 569 * Wait up to 5 seconds for SMS initialization. 570 */ 571 for (i = 0; i < 10000; i++) { 572 if (asmc_key_read(dev, ASMC_KEY_SMS, buf, 2) == 0 && 573 (buf[0] != 0x00 || buf[1] != 0x00)) { 574 error = 0; 575 goto out; 576 } 577 buf[0] = ASMC_SMS_INIT1; 578 buf[1] = ASMC_SMS_INIT2; 579 ASMC_DPRINTF(("sms key\n")); 580 asmc_key_write(dev, ASMC_KEY_SMS, buf, 2); 581 DELAY(50); 582 } 583 device_printf(dev, "WARNING: Sudden Motion Sensor not initialized!\n"); 584 585out: 586 asmc_sms_calibrate(dev); 587nosms: 588 sc->sc_nfan = asmc_fan_count(dev); 589 if (sc->sc_nfan > ASMC_MAXFANS) { 590 device_printf(dev, "more than %d fans were detected. Please " 591 "report this.\n", ASMC_MAXFANS); 592 sc->sc_nfan = ASMC_MAXFANS; 593 } 594 595 if (bootverbose) { 596 /* 597 * XXX: The number of keys is a 32 bit buffer, but 598 * right now Apple only uses the last 8 bit. 599 */ 600 asmc_key_read(dev, ASMC_NKEYS, buf, 4); 601 device_printf(dev, "number of keys: %d\n", buf[3]); 602 } 603 604 return (error); 605} 606 607/* 608 * We need to make sure that the SMC acks the byte sent. 609 * Just wait up to 100 ms. 610 */ 611static int 612asmc_wait(device_t dev, uint8_t val) 613{ 614 struct asmc_softc *sc = device_get_softc(dev); 615 u_int i; 616 617 val = val & ASMC_STATUS_MASK; 618 619 for (i = 0; i < 1000; i++) { 620 if ((ASMC_CMDPORT_READ(sc) & ASMC_STATUS_MASK) == val) 621 return (0); 622 DELAY(10); 623 } 624 625 device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, val, 626 ASMC_CMDPORT_READ(sc)); 627 628 return (1); 629} 630 631static int 632asmc_key_read(device_t dev, const char *key, uint8_t *buf, uint8_t len) 633{ 634 int i, error = 1; 635 struct asmc_softc *sc = device_get_softc(dev); 636 637 mtx_lock_spin(&sc->sc_mtx); 638 639 ASMC_CMDPORT_WRITE(sc, ASMC_CMDREAD); 640 if (asmc_wait(dev, 0x0c)) 641 goto out; 642 643 for (i = 0; i < 4; i++) { 644 ASMC_DATAPORT_WRITE(sc, key[i]); 645 if (asmc_wait(dev, 0x04)) 646 goto out; 647 } 648 649 ASMC_DATAPORT_WRITE(sc, len); 650 651 for (i = 0; i < len; i++) { 652 if (asmc_wait(dev, 0x05)) 653 goto out; 654 buf[i] = ASMC_DATAPORT_READ(sc); 655 } 656 657 error = 0; 658out: 659 mtx_unlock_spin(&sc->sc_mtx); 660 661 return (error); 662} 663 664static int 665asmc_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len) 666{ 667 int i, error = -1; 668 struct asmc_softc *sc = device_get_softc(dev); 669 670 mtx_lock_spin(&sc->sc_mtx); 671 672 ASMC_DPRINTF(("cmd port: cmd write\n")); 673 ASMC_CMDPORT_WRITE(sc, ASMC_CMDWRITE); 674 if (asmc_wait(dev, 0x0c)) 675 goto out; 676 677 ASMC_DPRINTF(("data port: key\n")); 678 for (i = 0; i < 4; i++) { 679 ASMC_DATAPORT_WRITE(sc, key[i]); 680 if (asmc_wait(dev, 0x04)) 681 goto out; 682 } 683 ASMC_DPRINTF(("data port: length\n")); 684 ASMC_DATAPORT_WRITE(sc, len); 685 686 ASMC_DPRINTF(("data port: buffer\n")); 687 for (i = 0; i < len; i++) { 688 if (asmc_wait(dev, 0x04)) 689 goto out; 690 ASMC_DATAPORT_WRITE(sc, buf[i]); 691 } 692 693 error = 0; 694out: 695 mtx_unlock_spin(&sc->sc_mtx); 696 697 return (error); 698 699} 700 701/* 702 * Fan control functions. 703 */ 704static int 705asmc_fan_count(device_t dev) 706{ 707 uint8_t buf[1]; 708 709 if (asmc_key_read(dev, ASMC_KEY_FANCOUNT, buf, 1) < 0) 710 return (-1); 711 712 return (buf[0]); 713} 714 715static int 716asmc_fan_getvalue(device_t dev, const char *key, int fan) 717{ 718 int speed; 719 uint8_t buf[2]; 720 char fankey[5]; 721 722 snprintf(fankey, sizeof(fankey), key, fan); 723 if (asmc_key_read(dev, fankey, buf, 2) < 0) 724 return (-1); 725 speed = (buf[0] << 6) | (buf[1] >> 2); 726 727 return (speed); 728} 729 730static int 731asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS) 732{ 733 device_t dev = (device_t) arg1; 734 int fan = arg2; 735 int error; 736 int32_t v; 737 738 v = asmc_fan_getvalue(dev, ASMC_KEY_FANSPEED, fan); 739 error = sysctl_handle_int(oidp, &v, 0, req); 740 741 return (error); 742} 743 744static int 745asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS) 746{ 747 device_t dev = (device_t) arg1; 748 int fan = arg2; 749 int error; 750 int32_t v; 751 752 v = asmc_fan_getvalue(dev, ASMC_KEY_FANSAFESPEED, fan); 753 error = sysctl_handle_int(oidp, &v, 0, req); 754 755 return (error); 756} 757 758 759static int 760asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS) 761{ 762 device_t dev = (device_t) arg1; 763 int fan = arg2; 764 int error; 765 int32_t v; 766 767 v = asmc_fan_getvalue(dev, ASMC_KEY_FANMINSPEED, fan); 768 error = sysctl_handle_int(oidp, &v, 0, req); 769 770 return (error); 771} 772 773static int 774asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS) 775{ 776 device_t dev = (device_t) arg1; 777 int fan = arg2; 778 int error; 779 int32_t v; 780 781 v = asmc_fan_getvalue(dev, ASMC_KEY_FANMAXSPEED, fan); 782 error = sysctl_handle_int(oidp, &v, 0, req); 783 784 return (error); 785} 786 787static int 788asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS) 789{ 790 device_t dev = (device_t) arg1; 791 int fan = arg2; 792 int error; 793 int32_t v; 794 795 v = asmc_fan_getvalue(dev, ASMC_KEY_FANTARGETSPEED, fan); 796 error = sysctl_handle_int(oidp, &v, 0, req); 797 798 return (error); 799} 800 801/* 802 * Temperature functions. 803 */ 804static int 805asmc_temp_getvalue(device_t dev, const char *key) 806{ 807 uint8_t buf[2]; 808 809 /* 810 * Check for invalid temperatures. 811 */ 812 if (asmc_key_read(dev, key, buf, 2) < 0) 813 return (-1); 814 815 return (buf[0]); 816} 817 818static int 819asmc_temp_sysctl(SYSCTL_HANDLER_ARGS) 820{ 821 device_t dev = (device_t) arg1; 822 struct asmc_softc *sc = device_get_softc(dev); 823 int error, val; 824 825 val = asmc_temp_getvalue(dev, sc->sc_model->smc_temps[arg2]); 826 error = sysctl_handle_int(oidp, &val, 0, req); 827 828 return (error); 829} 830 831/* 832 * Sudden Motion Sensor functions. 833 */ 834static int 835asmc_sms_read(device_t dev, const char *key, int16_t *val) 836{ 837 uint8_t buf[2]; 838 int error; 839 840 /* no need to do locking here as asmc_key_read() already does it */ 841 switch (key[3]) { 842 case 'X': 843 case 'Y': 844 case 'Z': 845 error = asmc_key_read(dev, key, buf, 2); 846 break; 847 default: 848 device_printf(dev, "%s called with invalid argument %s\n", 849 __func__, key); 850 error = 1; 851 goto out; 852 } 853 *val = ((int16_t)buf[0] << 8) | buf[1]; 854out: 855 return (error); 856} 857 858static void 859asmc_sms_calibrate(device_t dev) 860{ 861 struct asmc_softc *sc = device_get_softc(dev); 862 863 asmc_sms_read(dev, ASMC_KEY_SMS_X, &sc->sms_rest_x); 864 asmc_sms_read(dev, ASMC_KEY_SMS_Y, &sc->sms_rest_y); 865 asmc_sms_read(dev, ASMC_KEY_SMS_Z, &sc->sms_rest_z); 866} 867 868static int 869asmc_sms_intrfast(void *arg) 870{ 871 uint8_t type; 872 device_t dev = (device_t) arg; 873 struct asmc_softc *sc = device_get_softc(dev); 874 875 mtx_lock_spin(&sc->sc_mtx); 876 type = ASMC_INTPORT_READ(sc); 877 mtx_unlock_spin(&sc->sc_mtx); 878 879 sc->sc_sms_intrtype = type; 880 asmc_sms_printintr(dev, type); 881 882#ifdef INTR_FILTER 883 return (FILTER_SCHEDULE_THREAD | FILTER_HANDLED); 884#else 885 taskqueue_enqueue(sc->sc_sms_tq, &sc->sc_sms_task); 886#endif 887 return (FILTER_HANDLED); 888} 889 890#ifdef INTR_FILTER 891static void 892asmc_sms_handler(void *arg) 893{ 894 struct asmc_softc *sc = device_get_softc(arg); 895 896 asmc_sms_task(sc, 0); 897} 898#endif 899 900 901static void 902asmc_sms_printintr(device_t dev, uint8_t type) 903{ 904 905 switch (type) { 906 case ASMC_SMS_INTFF: 907 device_printf(dev, "WARNING: possible free fall!\n"); 908 break; 909 case ASMC_SMS_INTHA: 910 device_printf(dev, "WARNING: high acceleration detected!\n"); 911 break; 912 case ASMC_SMS_INTSH: 913 device_printf(dev, "WARNING: possible shock!\n"); 914 break; 915 default: 916 device_printf(dev, "%s unknown interrupt\n", __func__); 917 } 918} 919 920static void 921asmc_sms_task(void *arg, int pending) 922{ 923 struct asmc_softc *sc = (struct asmc_softc *)arg; 924 char notify[16]; 925 int type; 926 927 switch (sc->sc_sms_intrtype) { 928 case ASMC_SMS_INTFF: 929 type = 2; 930 break; 931 case ASMC_SMS_INTHA: 932 type = 1; 933 break; 934 case ASMC_SMS_INTSH: 935 type = 0; 936 break; 937 default: 938 type = 255; 939 } 940 941 snprintf(notify, sizeof(notify), " notify=0x%x", type); 942 devctl_notify("ACPI", "asmc", "SMS", notify); 943} 944 945static int 946asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS) 947{ 948 device_t dev = (device_t) arg1; 949 int error; 950 int16_t val; 951 int32_t v; 952 953 asmc_sms_read(dev, ASMC_KEY_SMS_X, &val); 954 v = (int32_t) val; 955 error = sysctl_handle_int(oidp, &v, 0, req); 956 957 return (error); 958} 959 960static int 961asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS) 962{ 963 device_t dev = (device_t) arg1; 964 int error; 965 int16_t val; 966 int32_t v; 967 968 asmc_sms_read(dev, ASMC_KEY_SMS_Y, &val); 969 v = (int32_t) val; 970 error = sysctl_handle_int(oidp, &v, 0, req); 971 972 return (error); 973} 974 975static int 976asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS) 977{ 978 device_t dev = (device_t) arg1; 979 int error; 980 int16_t val; 981 int32_t v; 982 983 asmc_sms_read(dev, ASMC_KEY_SMS_Z, &val); 984 v = (int32_t) val; 985 error = sysctl_handle_int(oidp, &v, sizeof(v), req); 986 987 return (error); 988} 989 990static int 991asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS) 992{ 993 device_t dev = (device_t) arg1; 994 uint8_t buf[6]; 995 int error; 996 unsigned int level; 997 int32_t v; 998 999 asmc_key_read(dev, ASMC_KEY_LIGHTLEFT, buf, 6); 1000 v = buf[2]; 1001 error = sysctl_handle_int(oidp, &v, sizeof(v), req); 1002 if (error == 0 && req->newptr != NULL) { 1003 level = *(unsigned int *)req->newptr; 1004 if (level > 255) 1005 return (EINVAL); 1006 buf[0] = level; 1007 buf[1] = 0x00; 1008 asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, 2); 1009 } 1010 1011 return (error); 1012} 1013 1014static int 1015asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS) 1016{ 1017 device_t dev = (device_t) arg1; 1018 uint8_t buf[6]; 1019 int error; 1020 unsigned int level; 1021 int32_t v; 1022 1023 asmc_key_read(dev, ASMC_KEY_LIGHTRIGHT, buf, 6); 1024 v = buf[2]; 1025 error = sysctl_handle_int(oidp, &v, sizeof(v), req); 1026 if (error == 0 && req->newptr != NULL) { 1027 level = *(unsigned int *)req->newptr; 1028 if (level > 255) 1029 return (EINVAL); 1030 buf[0] = level; 1031 buf[1] = 0x00; 1032 asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, 2); 1033 } 1034 1035 return (error); 1036} 1037