asmc.c revision 241885
1173426Srpaulo/*- 2177972Srpaulo * Copyright (c) 2007, 2008 Rui Paulo <rpaulo@FreeBSD.org> 3173426Srpaulo * All rights reserved. 4173426Srpaulo * 5173426Srpaulo * Redistribution and use in source and binary forms, with or without 6173426Srpaulo * modification, are permitted provided that the following conditions 7173426Srpaulo * are met: 8173426Srpaulo * 1. Redistributions of source code must retain the above copyright 9173426Srpaulo * notice, this list of conditions and the following disclaimer. 10173426Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 11173426Srpaulo * notice, this list of conditions and the following disclaimer in the 12173426Srpaulo * documentation and/or other materials provided with the distribution. 13173426Srpaulo * 14173426Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15173426Srpaulo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16173426Srpaulo * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17173426Srpaulo * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 18173426Srpaulo * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19173426Srpaulo * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20173426Srpaulo * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21173426Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 22173426Srpaulo * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 23173426Srpaulo * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24173426Srpaulo * POSSIBILITY OF SUCH DAMAGE. 25173426Srpaulo * 26173426Srpaulo */ 27173426Srpaulo 28173426Srpaulo/* 29173426Srpaulo * Driver for Apple's System Management Console (SMC). 30173426Srpaulo * SMC can be found on the MacBook, MacBook Pro and Mac Mini. 31173426Srpaulo * 32173426Srpaulo * Inspired by the Linux applesmc driver. 33173426Srpaulo */ 34173426Srpaulo 35173426Srpaulo#include <sys/cdefs.h> 36173426Srpaulo__FBSDID("$FreeBSD: head/sys/dev/asmc/asmc.c 241885 2012-10-22 13:06:09Z eadler $"); 37173426Srpaulo 38173426Srpaulo#include <sys/param.h> 39173426Srpaulo#include <sys/bus.h> 40173426Srpaulo#include <sys/conf.h> 41173426Srpaulo#include <sys/kernel.h> 42173426Srpaulo#include <sys/lock.h> 43173426Srpaulo#include <sys/malloc.h> 44173426Srpaulo#include <sys/module.h> 45173426Srpaulo#include <sys/mutex.h> 46173426Srpaulo#include <sys/sysctl.h> 47173426Srpaulo#include <sys/systm.h> 48173426Srpaulo#include <sys/taskqueue.h> 49173426Srpaulo#include <sys/rman.h> 50178118Srpaulo 51173426Srpaulo#include <machine/resource.h> 52193530Sjkim 53193530Sjkim#include <contrib/dev/acpica/include/acpi.h> 54193530Sjkim 55177972Srpaulo#include <dev/acpica/acpivar.h> 56173426Srpaulo#include <dev/asmc/asmcvar.h> 57173426Srpaulo 58178118Srpaulo#include "opt_intr_filter.h" 59178118Srpaulo 60173426Srpaulo/* 61173426Srpaulo * Device interface. 62173426Srpaulo */ 63173426Srpaulostatic int asmc_probe(device_t dev); 64173426Srpaulostatic int asmc_attach(device_t dev); 65173426Srpaulostatic int asmc_detach(device_t dev); 66173426Srpaulo 67173426Srpaulo/* 68173426Srpaulo * SMC functions. 69173426Srpaulo */ 70173426Srpaulostatic int asmc_init(device_t dev); 71195046Srpaulostatic int asmc_command(device_t dev, uint8_t command); 72173426Srpaulostatic int asmc_wait(device_t dev, uint8_t val); 73195046Srpaulostatic int asmc_wait_ack(device_t dev, uint8_t val, int amount); 74173426Srpaulostatic int asmc_key_write(device_t dev, const char *key, uint8_t *buf, 75173426Srpaulo uint8_t len); 76173426Srpaulostatic int asmc_key_read(device_t dev, const char *key, uint8_t *buf, 77173426Srpaulo uint8_t); 78173426Srpaulostatic int asmc_fan_count(device_t dev); 79173426Srpaulostatic int asmc_fan_getvalue(device_t dev, const char *key, int fan); 80173426Srpaulostatic int asmc_temp_getvalue(device_t dev, const char *key); 81173426Srpaulostatic int asmc_sms_read(device_t, const char *key, int16_t *val); 82173426Srpaulostatic void asmc_sms_calibrate(device_t dev); 83173426Srpaulostatic int asmc_sms_intrfast(void *arg); 84173426Srpaulo#ifdef INTR_FILTER 85173426Srpaulostatic void asmc_sms_handler(void *arg); 86173426Srpaulo#endif 87173426Srpaulostatic void asmc_sms_printintr(device_t dev, uint8_t); 88173426Srpaulostatic void asmc_sms_task(void *arg, int pending); 89197190Srpaulo#ifdef DEBUG 90197190Srpaulovoid asmc_dumpall(device_t); 91197190Srpaulostatic int asmc_key_dump(device_t, int); 92197190Srpaulo#endif 93173426Srpaulo 94173426Srpaulo/* 95173426Srpaulo * Model functions. 96173426Srpaulo */ 97173426Srpaulostatic int asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS); 98173426Srpaulostatic int asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS); 99173426Srpaulostatic int asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS); 100173426Srpaulostatic int asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS); 101173426Srpaulostatic int asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS); 102173426Srpaulostatic int asmc_temp_sysctl(SYSCTL_HANDLER_ARGS); 103173426Srpaulostatic int asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS); 104173426Srpaulostatic int asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS); 105173426Srpaulostatic int asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS); 106173426Srpaulostatic int asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS); 107173426Srpaulostatic int asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS); 108195046Srpaulostatic int asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS); 109173426Srpaulo 110173426Srpaulostruct asmc_model { 111173426Srpaulo const char *smc_model; /* smbios.system.product env var. */ 112173426Srpaulo const char *smc_desc; /* driver description */ 113173426Srpaulo 114173426Srpaulo /* Helper functions */ 115173426Srpaulo int (*smc_sms_x)(SYSCTL_HANDLER_ARGS); 116173426Srpaulo int (*smc_sms_y)(SYSCTL_HANDLER_ARGS); 117173426Srpaulo int (*smc_sms_z)(SYSCTL_HANDLER_ARGS); 118173426Srpaulo int (*smc_fan_speed)(SYSCTL_HANDLER_ARGS); 119173426Srpaulo int (*smc_fan_safespeed)(SYSCTL_HANDLER_ARGS); 120173426Srpaulo int (*smc_fan_minspeed)(SYSCTL_HANDLER_ARGS); 121173426Srpaulo int (*smc_fan_maxspeed)(SYSCTL_HANDLER_ARGS); 122173426Srpaulo int (*smc_fan_targetspeed)(SYSCTL_HANDLER_ARGS); 123173426Srpaulo int (*smc_light_left)(SYSCTL_HANDLER_ARGS); 124173426Srpaulo int (*smc_light_right)(SYSCTL_HANDLER_ARGS); 125195046Srpaulo int (*smc_light_control)(SYSCTL_HANDLER_ARGS); 126173426Srpaulo 127178145Srpaulo const char *smc_temps[ASMC_TEMP_MAX]; 128178145Srpaulo const char *smc_tempnames[ASMC_TEMP_MAX]; 129178145Srpaulo const char *smc_tempdescs[ASMC_TEMP_MAX]; 130173426Srpaulo}; 131173426Srpaulo 132173426Srpaulostatic struct asmc_model *asmc_match(device_t dev); 133173426Srpaulo 134173426Srpaulo#define ASMC_SMS_FUNCS asmc_mb_sysctl_sms_x, asmc_mb_sysctl_sms_y, \ 135173426Srpaulo asmc_mb_sysctl_sms_z 136173426Srpaulo 137173426Srpaulo#define ASMC_FAN_FUNCS asmc_mb_sysctl_fanspeed, asmc_mb_sysctl_fansafespeed, \ 138173426Srpaulo asmc_mb_sysctl_fanminspeed, \ 139173426Srpaulo asmc_mb_sysctl_fanmaxspeed, \ 140173426Srpaulo asmc_mb_sysctl_fantargetspeed 141173426Srpaulo#define ASMC_LIGHT_FUNCS asmc_mbp_sysctl_light_left, \ 142195046Srpaulo asmc_mbp_sysctl_light_right, \ 143195046Srpaulo asmc_mbp_sysctl_light_control 144173426Srpaulo 145173426Srpaulostruct asmc_model asmc_models[] = { 146173426Srpaulo { 147173426Srpaulo "MacBook1,1", "Apple SMC MacBook Core Duo", 148195046Srpaulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL, 149173426Srpaulo ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS 150173426Srpaulo }, 151173426Srpaulo 152173426Srpaulo { 153173426Srpaulo "MacBook2,1", "Apple SMC MacBook Core 2 Duo", 154195046Srpaulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL, 155173426Srpaulo ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS 156173426Srpaulo }, 157173426Srpaulo 158173426Srpaulo { 159173426Srpaulo "MacBookPro1,1", "Apple SMC MacBook Pro Core Duo (15-inch)", 160173426Srpaulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 161173426Srpaulo ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 162173426Srpaulo }, 163173426Srpaulo 164173426Srpaulo { 165173426Srpaulo "MacBookPro1,2", "Apple SMC MacBook Pro Core Duo (17-inch)", 166173426Srpaulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 167173426Srpaulo ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 168173426Srpaulo }, 169173426Srpaulo 170173426Srpaulo { 171173426Srpaulo "MacBookPro2,1", "Apple SMC MacBook Pro Core 2 Duo (17-inch)", 172173426Srpaulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 173173426Srpaulo ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 174173426Srpaulo }, 175173426Srpaulo 176173426Srpaulo { 177173426Srpaulo "MacBookPro2,2", "Apple SMC MacBook Pro Core 2 Duo (15-inch)", 178173426Srpaulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 179173426Srpaulo ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 180173426Srpaulo }, 181173426Srpaulo 182173426Srpaulo { 183173426Srpaulo "MacBookPro3,1", "Apple SMC MacBook Pro Core 2 Duo (15-inch LED)", 184173426Srpaulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 185173426Srpaulo ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 186173426Srpaulo }, 187173426Srpaulo 188173426Srpaulo { 189173426Srpaulo "MacBookPro3,2", "Apple SMC MacBook Pro Core 2 Duo (17-inch HD)", 190173426Srpaulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 191173426Srpaulo ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 192173426Srpaulo }, 193173426Srpaulo 194195046Srpaulo { 195195046Srpaulo "MacBookPro4,1", "Apple SMC MacBook Pro Core 2 Duo (Penryn)", 196195046Srpaulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 197195046Srpaulo ASMC_MBP4_TEMPS, ASMC_MBP4_TEMPNAMES, ASMC_MBP4_TEMPDESCS 198195046Srpaulo }, 199195046Srpaulo 200173426Srpaulo /* The Mac Mini has no SMS */ 201173426Srpaulo { 202173426Srpaulo "Macmini1,1", "Apple SMC Mac Mini", 203173426Srpaulo NULL, NULL, NULL, 204173851Srpaulo ASMC_FAN_FUNCS, 205195046Srpaulo NULL, NULL, NULL, 206173426Srpaulo ASMC_MM_TEMPS, ASMC_MM_TEMPNAMES, ASMC_MM_TEMPDESCS 207173426Srpaulo }, 208173426Srpaulo 209178145Srpaulo /* Idem for the MacPro */ 210178145Srpaulo { 211178145Srpaulo "MacPro2", "Apple SMC Mac Pro (8-core)", 212178145Srpaulo NULL, NULL, NULL, 213178145Srpaulo ASMC_FAN_FUNCS, 214195046Srpaulo NULL, NULL, NULL, 215178145Srpaulo ASMC_MP_TEMPS, ASMC_MP_TEMPNAMES, ASMC_MP_TEMPDESCS 216178145Srpaulo }, 217182850Srpaulo 218182850Srpaulo { 219182850Srpaulo "MacBookAir1,1", "Apple SMC MacBook Air", 220195046Srpaulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL, 221182850Srpaulo ASMC_MBA_TEMPS, ASMC_MBA_TEMPNAMES, ASMC_MBA_TEMPDESCS 222182850Srpaulo }, 223182850Srpaulo 224178145Srpaulo 225173426Srpaulo { NULL, NULL } 226173426Srpaulo}; 227173426Srpaulo 228173426Srpaulo#undef ASMC_SMS_FUNCS 229173426Srpaulo#undef ASMC_FAN_FUNCS 230173426Srpaulo#undef ASMC_LIGHT_FUNCS 231173426Srpaulo 232173426Srpaulo/* 233173426Srpaulo * Driver methods. 234173426Srpaulo */ 235173426Srpaulostatic device_method_t asmc_methods[] = { 236173426Srpaulo DEVMETHOD(device_probe, asmc_probe), 237173426Srpaulo DEVMETHOD(device_attach, asmc_attach), 238173426Srpaulo DEVMETHOD(device_detach, asmc_detach), 239173426Srpaulo 240173426Srpaulo { 0, 0 } 241173426Srpaulo}; 242173426Srpaulo 243173426Srpaulostatic driver_t asmc_driver = { 244173426Srpaulo "asmc", 245173426Srpaulo asmc_methods, 246173426Srpaulo sizeof(struct asmc_softc) 247173426Srpaulo}; 248173426Srpaulo 249177972Srpaulo/* 250177972Srpaulo * Debugging 251177972Srpaulo */ 252177972Srpaulo#define _COMPONENT ACPI_OEM 253177972SrpauloACPI_MODULE_NAME("ASMC") 254177972Srpaulo#ifdef DEBUG 255177972Srpaulo#define ASMC_DPRINTF(str) device_printf(dev, str) 256177977Srpaulo#else 257177977Srpaulo#define ASMC_DPRINTF(str) 258177972Srpaulo#endif 259177972Srpaulo 260195046Srpaulo/* NB: can't be const */ 261177972Srpaulostatic char *asmc_ids[] = { "APP0001", NULL }; 262177972Srpaulo 263173426Srpaulostatic devclass_t asmc_devclass; 264173426Srpaulo 265177972SrpauloDRIVER_MODULE(asmc, acpi, asmc_driver, asmc_devclass, NULL, NULL); 266177972SrpauloMODULE_DEPEND(asmc, acpi, 1, 1, 1); 267173426Srpaulo 268173426Srpaulostatic struct asmc_model * 269173426Srpauloasmc_match(device_t dev) 270173426Srpaulo{ 271173426Srpaulo int i; 272173426Srpaulo char *model; 273173426Srpaulo 274173426Srpaulo model = getenv("smbios.system.product"); 275185433Srpaulo if (model == NULL) 276185433Srpaulo return (NULL); 277185433Srpaulo 278173426Srpaulo for (i = 0; asmc_models[i].smc_model; i++) { 279173426Srpaulo if (!strncmp(model, asmc_models[i].smc_model, strlen(model))) { 280173426Srpaulo freeenv(model); 281173426Srpaulo return (&asmc_models[i]); 282173426Srpaulo } 283173426Srpaulo } 284173426Srpaulo freeenv(model); 285173426Srpaulo 286173426Srpaulo return (NULL); 287173426Srpaulo} 288173426Srpaulo 289173426Srpaulostatic int 290173426Srpauloasmc_probe(device_t dev) 291173426Srpaulo{ 292173426Srpaulo struct asmc_model *model; 293173426Srpaulo 294241885Seadler if (resource_disabled("asmc", 0)) 295241885Seadler return (ENXIO); 296177972Srpaulo if (ACPI_ID_PROBE(device_get_parent(dev), dev, asmc_ids) == NULL) 297177972Srpaulo return (ENXIO); 298177972Srpaulo 299173426Srpaulo model = asmc_match(dev); 300177972Srpaulo if (!model) { 301177972Srpaulo device_printf(dev, "model not recognized\n"); 302173426Srpaulo return (ENXIO); 303177972Srpaulo } 304173426Srpaulo device_set_desc(dev, model->smc_desc); 305173426Srpaulo 306173426Srpaulo return (BUS_PROBE_DEFAULT); 307173426Srpaulo} 308173426Srpaulo 309173426Srpaulostatic int 310173426Srpauloasmc_attach(device_t dev) 311173426Srpaulo{ 312173426Srpaulo int i, j; 313173426Srpaulo int ret; 314173426Srpaulo char name[2]; 315173426Srpaulo struct asmc_softc *sc = device_get_softc(dev); 316173426Srpaulo struct sysctl_ctx_list *sysctlctx; 317173426Srpaulo struct sysctl_oid *sysctlnode; 318173426Srpaulo struct asmc_model *model; 319173426Srpaulo 320177972Srpaulo sc->sc_ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT, 321177972Srpaulo &sc->sc_rid_port, RF_ACTIVE); 322177972Srpaulo if (sc->sc_ioport == NULL) { 323177972Srpaulo device_printf(dev, "unable to allocate IO port\n"); 324177972Srpaulo return (ENOMEM); 325177972Srpaulo } 326177972Srpaulo 327173426Srpaulo sysctlctx = device_get_sysctl_ctx(dev); 328173426Srpaulo sysctlnode = device_get_sysctl_tree(dev); 329173426Srpaulo 330173426Srpaulo model = asmc_match(dev); 331173426Srpaulo 332173426Srpaulo mtx_init(&sc->sc_mtx, "asmc", NULL, MTX_SPIN); 333173426Srpaulo 334173426Srpaulo sc->sc_model = model; 335173426Srpaulo asmc_init(dev); 336173426Srpaulo 337173426Srpaulo /* 338173426Srpaulo * dev.asmc.n.fan.* tree. 339173426Srpaulo */ 340173426Srpaulo sc->sc_fan_tree[0] = SYSCTL_ADD_NODE(sysctlctx, 341173426Srpaulo SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "fan", 342173426Srpaulo CTLFLAG_RD, 0, "Fan Root Tree"); 343173426Srpaulo 344173426Srpaulo for (i = 1; i <= sc->sc_nfan; i++) { 345173426Srpaulo j = i - 1; 346173426Srpaulo name[0] = '0' + j; 347173426Srpaulo name[1] = 0; 348173426Srpaulo sc->sc_fan_tree[i] = SYSCTL_ADD_NODE(sysctlctx, 349173426Srpaulo SYSCTL_CHILDREN(sc->sc_fan_tree[0]), 350173426Srpaulo OID_AUTO, name, CTLFLAG_RD, 0, 351173426Srpaulo "Fan Subtree"); 352173426Srpaulo 353173426Srpaulo SYSCTL_ADD_PROC(sysctlctx, 354173426Srpaulo SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 355173426Srpaulo OID_AUTO, "speed", CTLTYPE_INT | CTLFLAG_RD, 356173426Srpaulo dev, j, model->smc_fan_speed, "I", 357173426Srpaulo "Fan speed in RPM"); 358173426Srpaulo 359173426Srpaulo SYSCTL_ADD_PROC(sysctlctx, 360173426Srpaulo SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 361173426Srpaulo OID_AUTO, "safespeed", 362173426Srpaulo CTLTYPE_INT | CTLFLAG_RD, 363173426Srpaulo dev, j, model->smc_fan_safespeed, "I", 364173426Srpaulo "Fan safe speed in RPM"); 365173426Srpaulo 366173426Srpaulo SYSCTL_ADD_PROC(sysctlctx, 367173426Srpaulo SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 368173426Srpaulo OID_AUTO, "minspeed", 369173426Srpaulo CTLTYPE_INT | CTLFLAG_RD, 370173426Srpaulo dev, j, model->smc_fan_minspeed, "I", 371173426Srpaulo "Fan minimum speed in RPM"); 372173426Srpaulo 373173426Srpaulo SYSCTL_ADD_PROC(sysctlctx, 374173426Srpaulo SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 375173426Srpaulo OID_AUTO, "maxspeed", 376173426Srpaulo CTLTYPE_INT | CTLFLAG_RD, 377173426Srpaulo dev, j, model->smc_fan_maxspeed, "I", 378173426Srpaulo "Fan maximum speed in RPM"); 379173426Srpaulo 380173426Srpaulo SYSCTL_ADD_PROC(sysctlctx, 381173426Srpaulo SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 382173426Srpaulo OID_AUTO, "targetspeed", 383173426Srpaulo CTLTYPE_INT | CTLFLAG_RD, 384173426Srpaulo dev, j, model->smc_fan_targetspeed, "I", 385173426Srpaulo "Fan target speed in RPM"); 386173426Srpaulo } 387173426Srpaulo 388173426Srpaulo /* 389173426Srpaulo * dev.asmc.n.temp tree. 390173426Srpaulo */ 391173426Srpaulo sc->sc_temp_tree = SYSCTL_ADD_NODE(sysctlctx, 392173426Srpaulo SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "temp", 393173426Srpaulo CTLFLAG_RD, 0, "Temperature sensors"); 394173426Srpaulo 395173426Srpaulo for (i = 0; model->smc_temps[i]; i++) { 396173426Srpaulo SYSCTL_ADD_PROC(sysctlctx, 397173426Srpaulo SYSCTL_CHILDREN(sc->sc_temp_tree), 398173426Srpaulo OID_AUTO, model->smc_tempnames[i], 399173426Srpaulo CTLTYPE_INT | CTLFLAG_RD, 400173426Srpaulo dev, i, asmc_temp_sysctl, "I", 401173426Srpaulo model->smc_tempdescs[i]); 402173426Srpaulo } 403173426Srpaulo 404195046Srpaulo /* 405195046Srpaulo * dev.asmc.n.light 406195046Srpaulo */ 407195046Srpaulo if (model->smc_light_left) { 408195046Srpaulo sc->sc_light_tree = SYSCTL_ADD_NODE(sysctlctx, 409195046Srpaulo SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "light", 410195046Srpaulo CTLFLAG_RD, 0, "Keyboard backlight sensors"); 411195046Srpaulo 412195046Srpaulo SYSCTL_ADD_PROC(sysctlctx, 413195046Srpaulo SYSCTL_CHILDREN(sc->sc_light_tree), 414195046Srpaulo OID_AUTO, "left", CTLTYPE_INT | CTLFLAG_RD, 415195046Srpaulo dev, 0, model->smc_light_left, "I", 416195046Srpaulo "Keyboard backlight left sensor"); 417195046Srpaulo 418195046Srpaulo SYSCTL_ADD_PROC(sysctlctx, 419195046Srpaulo SYSCTL_CHILDREN(sc->sc_light_tree), 420195046Srpaulo OID_AUTO, "right", CTLTYPE_INT | CTLFLAG_RD, 421195046Srpaulo dev, 0, model->smc_light_right, "I", 422195046Srpaulo "Keyboard backlight right sensor"); 423195046Srpaulo 424195046Srpaulo SYSCTL_ADD_PROC(sysctlctx, 425195046Srpaulo SYSCTL_CHILDREN(sc->sc_light_tree), 426196455Srpaulo OID_AUTO, "control", 427196455Srpaulo CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, 428195046Srpaulo dev, 0, model->smc_light_control, "I", 429195046Srpaulo "Keyboard backlight brightness control"); 430195046Srpaulo } 431195046Srpaulo 432173426Srpaulo if (model->smc_sms_x == NULL) 433173426Srpaulo goto nosms; 434173426Srpaulo 435173426Srpaulo /* 436173426Srpaulo * dev.asmc.n.sms tree. 437173426Srpaulo */ 438173426Srpaulo sc->sc_sms_tree = SYSCTL_ADD_NODE(sysctlctx, 439173426Srpaulo SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "sms", 440173426Srpaulo CTLFLAG_RD, 0, "Sudden Motion Sensor"); 441173426Srpaulo 442173426Srpaulo SYSCTL_ADD_PROC(sysctlctx, 443173426Srpaulo SYSCTL_CHILDREN(sc->sc_sms_tree), 444173426Srpaulo OID_AUTO, "x", CTLTYPE_INT | CTLFLAG_RD, 445173426Srpaulo dev, 0, model->smc_sms_x, "I", 446173426Srpaulo "Sudden Motion Sensor X value"); 447173426Srpaulo 448173426Srpaulo SYSCTL_ADD_PROC(sysctlctx, 449173426Srpaulo SYSCTL_CHILDREN(sc->sc_sms_tree), 450173426Srpaulo OID_AUTO, "y", CTLTYPE_INT | CTLFLAG_RD, 451173426Srpaulo dev, 0, model->smc_sms_y, "I", 452173426Srpaulo "Sudden Motion Sensor Y value"); 453173426Srpaulo 454173426Srpaulo SYSCTL_ADD_PROC(sysctlctx, 455173426Srpaulo SYSCTL_CHILDREN(sc->sc_sms_tree), 456173426Srpaulo OID_AUTO, "z", CTLTYPE_INT | CTLFLAG_RD, 457173426Srpaulo dev, 0, model->smc_sms_z, "I", 458173426Srpaulo "Sudden Motion Sensor Z value"); 459173426Srpaulo 460173426Srpaulo /* 461173426Srpaulo * Need a taskqueue to send devctl_notify() events 462173426Srpaulo * when the SMS interrupt us. 463173426Srpaulo * 464173426Srpaulo * PI_REALTIME is used due to the sensitivity of the 465173426Srpaulo * interrupt. An interrupt from the SMS means that the 466173426Srpaulo * disk heads should be turned off as quickly as possible. 467173426Srpaulo * 468173426Srpaulo * We only need to do this for the non INTR_FILTER case. 469173426Srpaulo */ 470173426Srpaulo sc->sc_sms_tq = NULL; 471173426Srpaulo#ifndef INTR_FILTER 472173426Srpaulo TASK_INIT(&sc->sc_sms_task, 0, asmc_sms_task, sc); 473173426Srpaulo sc->sc_sms_tq = taskqueue_create_fast("asmc_taskq", M_WAITOK, 474173426Srpaulo taskqueue_thread_enqueue, &sc->sc_sms_tq); 475173426Srpaulo taskqueue_start_threads(&sc->sc_sms_tq, 1, PI_REALTIME, "%s sms taskq", 476173426Srpaulo device_get_nameunit(dev)); 477173426Srpaulo#endif 478173426Srpaulo /* 479173426Srpaulo * Allocate an IRQ for the SMS. 480173426Srpaulo */ 481177972Srpaulo sc->sc_rid_irq = 0; 482177972Srpaulo sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, 483177972Srpaulo &sc->sc_rid_irq, RF_ACTIVE); 484177972Srpaulo if (sc->sc_irq == NULL) { 485173426Srpaulo device_printf(dev, "unable to allocate IRQ resource\n"); 486173426Srpaulo ret = ENXIO; 487173426Srpaulo goto err2; 488173426Srpaulo } 489173426Srpaulo 490177972Srpaulo ret = bus_setup_intr(dev, sc->sc_irq, 491173426Srpaulo INTR_TYPE_MISC | INTR_MPSAFE, 492173426Srpaulo#ifdef INTR_FILTER 493173426Srpaulo asmc_sms_intrfast, asmc_sms_handler, 494173426Srpaulo#else 495173426Srpaulo asmc_sms_intrfast, NULL, 496173426Srpaulo#endif 497173426Srpaulo dev, &sc->sc_cookie); 498173426Srpaulo 499173426Srpaulo if (ret) { 500173426Srpaulo device_printf(dev, "unable to setup SMS IRQ\n"); 501173426Srpaulo goto err1; 502173426Srpaulo } 503173426Srpaulonosms: 504173426Srpaulo return (0); 505173426Srpauloerr1: 506177972Srpaulo bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq, sc->sc_irq); 507173426Srpauloerr2: 508177972Srpaulo bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port, 509177972Srpaulo sc->sc_ioport); 510173426Srpaulo mtx_destroy(&sc->sc_mtx); 511173426Srpaulo if (sc->sc_sms_tq) 512173426Srpaulo taskqueue_free(sc->sc_sms_tq); 513173426Srpaulo 514173426Srpaulo return (ret); 515173426Srpaulo} 516173426Srpaulo 517173426Srpaulostatic int 518173426Srpauloasmc_detach(device_t dev) 519173426Srpaulo{ 520173426Srpaulo struct asmc_softc *sc = device_get_softc(dev); 521173426Srpaulo 522173426Srpaulo if (sc->sc_sms_tq) { 523173426Srpaulo taskqueue_drain(sc->sc_sms_tq, &sc->sc_sms_task); 524173426Srpaulo taskqueue_free(sc->sc_sms_tq); 525173426Srpaulo } 526173426Srpaulo if (sc->sc_cookie) 527177972Srpaulo bus_teardown_intr(dev, sc->sc_irq, sc->sc_cookie); 528177972Srpaulo if (sc->sc_irq) 529177972Srpaulo bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq, 530177972Srpaulo sc->sc_irq); 531177972Srpaulo if (sc->sc_ioport) 532177972Srpaulo bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port, 533177972Srpaulo sc->sc_ioport); 534173426Srpaulo mtx_destroy(&sc->sc_mtx); 535173426Srpaulo 536173426Srpaulo return (0); 537173426Srpaulo} 538173426Srpaulo 539197190Srpaulo#ifdef DEBUG 540197190Srpaulovoid asmc_dumpall(device_t dev) 541197190Srpaulo{ 542197190Srpaulo int i; 543197190Srpaulo 544197190Srpaulo /* XXX magic number */ 545197190Srpaulo for (i=0; i < 0x100; i++) 546197190Srpaulo asmc_key_dump(dev, i); 547197190Srpaulo} 548197190Srpaulo#endif 549197190Srpaulo 550173426Srpaulostatic int 551173426Srpauloasmc_init(device_t dev) 552173426Srpaulo{ 553173426Srpaulo struct asmc_softc *sc = device_get_softc(dev); 554173426Srpaulo int i, error = 1; 555173426Srpaulo uint8_t buf[4]; 556173426Srpaulo 557173426Srpaulo if (sc->sc_model->smc_sms_x == NULL) 558173426Srpaulo goto nosms; 559173426Srpaulo 560173426Srpaulo /* 561173426Srpaulo * We are ready to recieve interrupts from the SMS. 562173426Srpaulo */ 563173426Srpaulo buf[0] = 0x01; 564177972Srpaulo ASMC_DPRINTF(("intok key\n")); 565173426Srpaulo asmc_key_write(dev, ASMC_KEY_INTOK, buf, 1); 566173426Srpaulo DELAY(50); 567173426Srpaulo 568173426Srpaulo /* 569173426Srpaulo * Initiate the polling intervals. 570173426Srpaulo */ 571173426Srpaulo buf[0] = 20; /* msecs */ 572177972Srpaulo ASMC_DPRINTF(("low int key\n")); 573173426Srpaulo asmc_key_write(dev, ASMC_KEY_SMS_LOW_INT, buf, 1); 574173426Srpaulo DELAY(200); 575173426Srpaulo 576173426Srpaulo buf[0] = 20; /* msecs */ 577177972Srpaulo ASMC_DPRINTF(("high int key\n")); 578173426Srpaulo asmc_key_write(dev, ASMC_KEY_SMS_HIGH_INT, buf, 1); 579173426Srpaulo DELAY(200); 580173426Srpaulo 581173426Srpaulo buf[0] = 0x00; 582173426Srpaulo buf[1] = 0x60; 583177972Srpaulo ASMC_DPRINTF(("sms low key\n")); 584173426Srpaulo asmc_key_write(dev, ASMC_KEY_SMS_LOW, buf, 2); 585173426Srpaulo DELAY(200); 586173426Srpaulo 587173426Srpaulo buf[0] = 0x01; 588173426Srpaulo buf[1] = 0xc0; 589177972Srpaulo ASMC_DPRINTF(("sms high key\n")); 590173426Srpaulo asmc_key_write(dev, ASMC_KEY_SMS_HIGH, buf, 2); 591173426Srpaulo DELAY(200); 592173426Srpaulo 593173426Srpaulo /* 594173426Srpaulo * I'm not sure what this key does, but it seems to be 595173426Srpaulo * required. 596173426Srpaulo */ 597173426Srpaulo buf[0] = 0x01; 598177972Srpaulo ASMC_DPRINTF(("sms flag key\n")); 599173426Srpaulo asmc_key_write(dev, ASMC_KEY_SMS_FLAG, buf, 1); 600177979Srpaulo DELAY(100); 601173426Srpaulo 602197190Srpaulo sc->sc_sms_intr_works = 0; 603197190Srpaulo 604173426Srpaulo /* 605197190Srpaulo * Retry SMS initialization 1000 times 606197190Srpaulo * (takes approx. 2 seconds in worst case) 607173426Srpaulo */ 608197190Srpaulo for (i = 0; i < 1000; i++) { 609173426Srpaulo if (asmc_key_read(dev, ASMC_KEY_SMS, buf, 2) == 0 && 610197190Srpaulo (buf[0] == ASMC_SMS_INIT1 && buf[1] == ASMC_SMS_INIT2)) { 611173426Srpaulo error = 0; 612197190Srpaulo sc->sc_sms_intr_works = 1; 613177977Srpaulo goto out; 614173426Srpaulo } 615173426Srpaulo buf[0] = ASMC_SMS_INIT1; 616173426Srpaulo buf[1] = ASMC_SMS_INIT2; 617177972Srpaulo ASMC_DPRINTF(("sms key\n")); 618173426Srpaulo asmc_key_write(dev, ASMC_KEY_SMS, buf, 2); 619173426Srpaulo DELAY(50); 620173426Srpaulo } 621177977Srpaulo device_printf(dev, "WARNING: Sudden Motion Sensor not initialized!\n"); 622173426Srpaulo 623177977Srpauloout: 624173426Srpaulo asmc_sms_calibrate(dev); 625173426Srpaulonosms: 626173426Srpaulo sc->sc_nfan = asmc_fan_count(dev); 627173426Srpaulo if (sc->sc_nfan > ASMC_MAXFANS) { 628173426Srpaulo device_printf(dev, "more than %d fans were detected. Please " 629173426Srpaulo "report this.\n", ASMC_MAXFANS); 630173426Srpaulo sc->sc_nfan = ASMC_MAXFANS; 631173426Srpaulo } 632173426Srpaulo 633173426Srpaulo if (bootverbose) { 634173426Srpaulo /* 635173426Srpaulo * XXX: The number of keys is a 32 bit buffer, but 636173426Srpaulo * right now Apple only uses the last 8 bit. 637173426Srpaulo */ 638173426Srpaulo asmc_key_read(dev, ASMC_NKEYS, buf, 4); 639173426Srpaulo device_printf(dev, "number of keys: %d\n", buf[3]); 640173426Srpaulo } 641173426Srpaulo 642197190Srpaulo#ifdef DEBUG 643197190Srpaulo asmc_dumpall(dev); 644197190Srpaulo#endif 645197190Srpaulo 646173426Srpaulo return (error); 647173426Srpaulo} 648173426Srpaulo 649173426Srpaulo/* 650173426Srpaulo * We need to make sure that the SMC acks the byte sent. 651195046Srpaulo * Just wait up to (amount * 10) ms. 652173426Srpaulo */ 653173426Srpaulostatic int 654195046Srpauloasmc_wait_ack(device_t dev, uint8_t val, int amount) 655173426Srpaulo{ 656177972Srpaulo struct asmc_softc *sc = device_get_softc(dev); 657173426Srpaulo u_int i; 658173426Srpaulo 659173426Srpaulo val = val & ASMC_STATUS_MASK; 660173426Srpaulo 661195046Srpaulo for (i = 0; i < amount; i++) { 662177972Srpaulo if ((ASMC_CMDPORT_READ(sc) & ASMC_STATUS_MASK) == val) 663173426Srpaulo return (0); 664173426Srpaulo DELAY(10); 665173426Srpaulo } 666173426Srpaulo 667195046Srpaulo return (1); 668195046Srpaulo} 669195046Srpaulo 670195046Srpaulo/* 671195046Srpaulo * We need to make sure that the SMC acks the byte sent. 672195046Srpaulo * Just wait up to 100 ms. 673195046Srpaulo */ 674195046Srpaulostatic int 675195046Srpauloasmc_wait(device_t dev, uint8_t val) 676195046Srpaulo{ 677195046Srpaulo struct asmc_softc *sc; 678195046Srpaulo 679195046Srpaulo if (asmc_wait_ack(dev, val, 1000) == 0) 680195046Srpaulo return (0); 681195046Srpaulo 682195046Srpaulo sc = device_get_softc(dev); 683195046Srpaulo val = val & ASMC_STATUS_MASK; 684195046Srpaulo 685195046Srpaulo#ifdef DEBUG 686173426Srpaulo device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, val, 687177972Srpaulo ASMC_CMDPORT_READ(sc)); 688195046Srpaulo#endif 689195046Srpaulo return (1); 690195046Srpaulo} 691173426Srpaulo 692195046Srpaulo/* 693195046Srpaulo * Send the given command, retrying up to 10 times if 694195046Srpaulo * the acknowledgement fails. 695195046Srpaulo */ 696195046Srpaulostatic int 697195046Srpauloasmc_command(device_t dev, uint8_t command) { 698195046Srpaulo 699195046Srpaulo int i; 700195046Srpaulo struct asmc_softc *sc = device_get_softc(dev); 701195046Srpaulo 702195046Srpaulo for (i=0; i < 10; i++) { 703195046Srpaulo ASMC_CMDPORT_WRITE(sc, command); 704195046Srpaulo if (asmc_wait_ack(dev, 0x0c, 100) == 0) { 705195046Srpaulo return (0); 706195046Srpaulo } 707195046Srpaulo } 708195046Srpaulo 709195046Srpaulo#ifdef DEBUG 710195046Srpaulo device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, command, 711195046Srpaulo ASMC_CMDPORT_READ(sc)); 712195046Srpaulo#endif 713173426Srpaulo return (1); 714173426Srpaulo} 715173426Srpaulo 716173426Srpaulostatic int 717173426Srpauloasmc_key_read(device_t dev, const char *key, uint8_t *buf, uint8_t len) 718173426Srpaulo{ 719195046Srpaulo int i, error = 1, try = 0; 720173426Srpaulo struct asmc_softc *sc = device_get_softc(dev); 721173426Srpaulo 722173426Srpaulo mtx_lock_spin(&sc->sc_mtx); 723173426Srpaulo 724195046Srpaulobegin: 725195046Srpaulo if (asmc_command(dev, ASMC_CMDREAD)) 726173426Srpaulo goto out; 727173426Srpaulo 728173426Srpaulo for (i = 0; i < 4; i++) { 729177972Srpaulo ASMC_DATAPORT_WRITE(sc, key[i]); 730173426Srpaulo if (asmc_wait(dev, 0x04)) 731173426Srpaulo goto out; 732173426Srpaulo } 733173426Srpaulo 734177972Srpaulo ASMC_DATAPORT_WRITE(sc, len); 735173426Srpaulo 736173426Srpaulo for (i = 0; i < len; i++) { 737173426Srpaulo if (asmc_wait(dev, 0x05)) 738173426Srpaulo goto out; 739177972Srpaulo buf[i] = ASMC_DATAPORT_READ(sc); 740173426Srpaulo } 741173426Srpaulo 742173426Srpaulo error = 0; 743173426Srpauloout: 744195046Srpaulo if (error) { 745195046Srpaulo if (++try < 10) goto begin; 746195046Srpaulo device_printf(dev,"%s for key %s failed %d times, giving up\n", 747195046Srpaulo __func__, key, try); 748195046Srpaulo } 749195046Srpaulo 750173426Srpaulo mtx_unlock_spin(&sc->sc_mtx); 751173426Srpaulo 752173426Srpaulo return (error); 753173426Srpaulo} 754173426Srpaulo 755197190Srpaulo#ifdef DEBUG 756173426Srpaulostatic int 757197190Srpauloasmc_key_dump(device_t dev, int number) 758197190Srpaulo{ 759197190Srpaulo struct asmc_softc *sc = device_get_softc(dev); 760197190Srpaulo char key[5] = { 0 }; 761197190Srpaulo char type[7] = { 0 }; 762197190Srpaulo uint8_t index[4]; 763197190Srpaulo uint8_t v[32]; 764197190Srpaulo uint8_t maxlen; 765197190Srpaulo int i, error = 1, try = 0; 766197190Srpaulo 767197190Srpaulo mtx_lock_spin(&sc->sc_mtx); 768197190Srpaulo 769197190Srpaulo index[0] = (number >> 24) & 0xff; 770197190Srpaulo index[1] = (number >> 16) & 0xff; 771197190Srpaulo index[2] = (number >> 8) & 0xff; 772197190Srpaulo index[3] = (number) & 0xff; 773197190Srpaulo 774197190Srpaulobegin: 775197190Srpaulo if (asmc_command(dev, 0x12)) 776197190Srpaulo goto out; 777197190Srpaulo 778197190Srpaulo for (i = 0; i < 4; i++) { 779197190Srpaulo ASMC_DATAPORT_WRITE(sc, index[i]); 780197190Srpaulo if (asmc_wait(dev, 0x04)) 781197190Srpaulo goto out; 782197190Srpaulo } 783197190Srpaulo 784197190Srpaulo ASMC_DATAPORT_WRITE(sc, 4); 785197190Srpaulo 786197190Srpaulo for (i = 0; i < 4; i++) { 787197190Srpaulo if (asmc_wait(dev, 0x05)) 788197190Srpaulo goto out; 789197190Srpaulo key[i] = ASMC_DATAPORT_READ(sc); 790197190Srpaulo } 791197190Srpaulo 792197190Srpaulo /* get type */ 793197190Srpaulo if (asmc_command(dev, 0x13)) 794197190Srpaulo goto out; 795197190Srpaulo 796197190Srpaulo for (i = 0; i < 4; i++) { 797197190Srpaulo ASMC_DATAPORT_WRITE(sc, key[i]); 798197190Srpaulo if (asmc_wait(dev, 0x04)) 799197190Srpaulo goto out; 800197190Srpaulo } 801197190Srpaulo 802197190Srpaulo ASMC_DATAPORT_WRITE(sc, 6); 803197190Srpaulo 804197190Srpaulo for (i = 0; i < 6; i++) { 805197190Srpaulo if (asmc_wait(dev, 0x05)) 806197190Srpaulo goto out; 807197190Srpaulo type[i] = ASMC_DATAPORT_READ(sc); 808197190Srpaulo } 809197190Srpaulo 810197190Srpaulo error = 0; 811197190Srpauloout: 812197190Srpaulo if (error) { 813197190Srpaulo if (++try < 10) goto begin; 814197190Srpaulo device_printf(dev,"%s for key %s failed %d times, giving up\n", 815197190Srpaulo __func__, key, try); 816197190Srpaulo mtx_unlock_spin(&sc->sc_mtx); 817197190Srpaulo } 818197190Srpaulo else { 819197190Srpaulo char buf[1024]; 820197190Srpaulo char buf2[8]; 821197190Srpaulo mtx_unlock_spin(&sc->sc_mtx); 822197190Srpaulo maxlen = type[0]; 823197190Srpaulo type[0] = ' '; 824197190Srpaulo type[5] = 0; 825197190Srpaulo if (maxlen > sizeof(v)) { 826197202Srpaulo device_printf(dev, 827197202Srpaulo "WARNING: cropping maxlen from %d to %zu\n", 828197202Srpaulo maxlen, sizeof(v)); 829197190Srpaulo maxlen = sizeof(v); 830197190Srpaulo } 831197190Srpaulo for (i = 0; i < sizeof(v); i++) { 832197190Srpaulo v[i] = 0; 833197190Srpaulo } 834197190Srpaulo asmc_key_read(dev, key, v, maxlen); 835197190Srpaulo snprintf(buf, sizeof(buf), "key %d is: %s, type %s " 836197190Srpaulo "(len %d), data", number, key, type, maxlen); 837197190Srpaulo for (i = 0; i < maxlen; i++) { 838197190Srpaulo snprintf(buf2, sizeof(buf), " %02x", v[i]); 839197190Srpaulo strlcat(buf, buf2, sizeof(buf)); 840197190Srpaulo } 841197190Srpaulo strlcat(buf, " \n", sizeof(buf)); 842197190Srpaulo device_printf(dev, buf); 843197190Srpaulo } 844197190Srpaulo 845197190Srpaulo return (error); 846197190Srpaulo} 847197190Srpaulo#endif 848197190Srpaulo 849197190Srpaulostatic int 850173426Srpauloasmc_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len) 851173426Srpaulo{ 852195046Srpaulo int i, error = -1, try = 0; 853173426Srpaulo struct asmc_softc *sc = device_get_softc(dev); 854173426Srpaulo 855173426Srpaulo mtx_lock_spin(&sc->sc_mtx); 856173426Srpaulo 857195046Srpaulobegin: 858177972Srpaulo ASMC_DPRINTF(("cmd port: cmd write\n")); 859195046Srpaulo if (asmc_command(dev, ASMC_CMDWRITE)) 860173426Srpaulo goto out; 861173426Srpaulo 862177972Srpaulo ASMC_DPRINTF(("data port: key\n")); 863173426Srpaulo for (i = 0; i < 4; i++) { 864177972Srpaulo ASMC_DATAPORT_WRITE(sc, key[i]); 865173426Srpaulo if (asmc_wait(dev, 0x04)) 866173426Srpaulo goto out; 867173426Srpaulo } 868177972Srpaulo ASMC_DPRINTF(("data port: length\n")); 869177972Srpaulo ASMC_DATAPORT_WRITE(sc, len); 870173426Srpaulo 871177972Srpaulo ASMC_DPRINTF(("data port: buffer\n")); 872173426Srpaulo for (i = 0; i < len; i++) { 873173426Srpaulo if (asmc_wait(dev, 0x04)) 874173426Srpaulo goto out; 875177972Srpaulo ASMC_DATAPORT_WRITE(sc, buf[i]); 876173426Srpaulo } 877173426Srpaulo 878173426Srpaulo error = 0; 879173426Srpauloout: 880195046Srpaulo if (error) { 881195046Srpaulo if (++try < 10) goto begin; 882195046Srpaulo device_printf(dev,"%s for key %s failed %d times, giving up\n", 883195046Srpaulo __func__, key, try); 884195046Srpaulo } 885195046Srpaulo 886173426Srpaulo mtx_unlock_spin(&sc->sc_mtx); 887173426Srpaulo 888173426Srpaulo return (error); 889173426Srpaulo 890173426Srpaulo} 891173426Srpaulo 892173426Srpaulo/* 893173426Srpaulo * Fan control functions. 894173426Srpaulo */ 895173426Srpaulostatic int 896173426Srpauloasmc_fan_count(device_t dev) 897173426Srpaulo{ 898173426Srpaulo uint8_t buf[1]; 899173426Srpaulo 900173426Srpaulo if (asmc_key_read(dev, ASMC_KEY_FANCOUNT, buf, 1) < 0) 901173426Srpaulo return (-1); 902173426Srpaulo 903173426Srpaulo return (buf[0]); 904173426Srpaulo} 905173426Srpaulo 906173426Srpaulostatic int 907173426Srpauloasmc_fan_getvalue(device_t dev, const char *key, int fan) 908173426Srpaulo{ 909173426Srpaulo int speed; 910173426Srpaulo uint8_t buf[2]; 911173426Srpaulo char fankey[5]; 912173426Srpaulo 913173426Srpaulo snprintf(fankey, sizeof(fankey), key, fan); 914173426Srpaulo if (asmc_key_read(dev, fankey, buf, 2) < 0) 915173426Srpaulo return (-1); 916173426Srpaulo speed = (buf[0] << 6) | (buf[1] >> 2); 917173426Srpaulo 918173426Srpaulo return (speed); 919173426Srpaulo} 920173426Srpaulo 921173426Srpaulostatic int 922173426Srpauloasmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS) 923173426Srpaulo{ 924173426Srpaulo device_t dev = (device_t) arg1; 925173426Srpaulo int fan = arg2; 926173426Srpaulo int error; 927173426Srpaulo int32_t v; 928173426Srpaulo 929173426Srpaulo v = asmc_fan_getvalue(dev, ASMC_KEY_FANSPEED, fan); 930173426Srpaulo error = sysctl_handle_int(oidp, &v, 0, req); 931173426Srpaulo 932173426Srpaulo return (error); 933173426Srpaulo} 934173426Srpaulo 935173426Srpaulostatic int 936173426Srpauloasmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS) 937173426Srpaulo{ 938173426Srpaulo device_t dev = (device_t) arg1; 939173426Srpaulo int fan = arg2; 940173426Srpaulo int error; 941173426Srpaulo int32_t v; 942173426Srpaulo 943173426Srpaulo v = asmc_fan_getvalue(dev, ASMC_KEY_FANSAFESPEED, fan); 944173426Srpaulo error = sysctl_handle_int(oidp, &v, 0, req); 945173426Srpaulo 946173426Srpaulo return (error); 947173426Srpaulo} 948173426Srpaulo 949173426Srpaulo 950173426Srpaulostatic int 951173426Srpauloasmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS) 952173426Srpaulo{ 953173426Srpaulo device_t dev = (device_t) arg1; 954173426Srpaulo int fan = arg2; 955173426Srpaulo int error; 956173426Srpaulo int32_t v; 957173426Srpaulo 958173426Srpaulo v = asmc_fan_getvalue(dev, ASMC_KEY_FANMINSPEED, fan); 959173426Srpaulo error = sysctl_handle_int(oidp, &v, 0, req); 960173426Srpaulo 961173426Srpaulo return (error); 962173426Srpaulo} 963173426Srpaulo 964173426Srpaulostatic int 965173426Srpauloasmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS) 966173426Srpaulo{ 967173426Srpaulo device_t dev = (device_t) arg1; 968173426Srpaulo int fan = arg2; 969173426Srpaulo int error; 970173426Srpaulo int32_t v; 971173426Srpaulo 972173426Srpaulo v = asmc_fan_getvalue(dev, ASMC_KEY_FANMAXSPEED, fan); 973173426Srpaulo error = sysctl_handle_int(oidp, &v, 0, req); 974173426Srpaulo 975173426Srpaulo return (error); 976173426Srpaulo} 977173426Srpaulo 978173426Srpaulostatic int 979173426Srpauloasmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS) 980173426Srpaulo{ 981173426Srpaulo device_t dev = (device_t) arg1; 982173426Srpaulo int fan = arg2; 983173426Srpaulo int error; 984173426Srpaulo int32_t v; 985173426Srpaulo 986173426Srpaulo v = asmc_fan_getvalue(dev, ASMC_KEY_FANTARGETSPEED, fan); 987173426Srpaulo error = sysctl_handle_int(oidp, &v, 0, req); 988173426Srpaulo 989173426Srpaulo return (error); 990173426Srpaulo} 991173426Srpaulo 992173426Srpaulo/* 993173426Srpaulo * Temperature functions. 994173426Srpaulo */ 995173426Srpaulostatic int 996173426Srpauloasmc_temp_getvalue(device_t dev, const char *key) 997173426Srpaulo{ 998173426Srpaulo uint8_t buf[2]; 999173426Srpaulo 1000173426Srpaulo /* 1001173426Srpaulo * Check for invalid temperatures. 1002173426Srpaulo */ 1003173426Srpaulo if (asmc_key_read(dev, key, buf, 2) < 0) 1004173426Srpaulo return (-1); 1005173426Srpaulo 1006173426Srpaulo return (buf[0]); 1007173426Srpaulo} 1008173426Srpaulo 1009173426Srpaulostatic int 1010173426Srpauloasmc_temp_sysctl(SYSCTL_HANDLER_ARGS) 1011173426Srpaulo{ 1012173426Srpaulo device_t dev = (device_t) arg1; 1013173426Srpaulo struct asmc_softc *sc = device_get_softc(dev); 1014173426Srpaulo int error, val; 1015173426Srpaulo 1016173426Srpaulo val = asmc_temp_getvalue(dev, sc->sc_model->smc_temps[arg2]); 1017173426Srpaulo error = sysctl_handle_int(oidp, &val, 0, req); 1018173426Srpaulo 1019173426Srpaulo return (error); 1020173426Srpaulo} 1021173426Srpaulo 1022173426Srpaulo/* 1023173426Srpaulo * Sudden Motion Sensor functions. 1024173426Srpaulo */ 1025173426Srpaulostatic int 1026173426Srpauloasmc_sms_read(device_t dev, const char *key, int16_t *val) 1027173426Srpaulo{ 1028173426Srpaulo uint8_t buf[2]; 1029173426Srpaulo int error; 1030173426Srpaulo 1031173426Srpaulo /* no need to do locking here as asmc_key_read() already does it */ 1032173426Srpaulo switch (key[3]) { 1033173426Srpaulo case 'X': 1034173426Srpaulo case 'Y': 1035173426Srpaulo case 'Z': 1036173426Srpaulo error = asmc_key_read(dev, key, buf, 2); 1037173426Srpaulo break; 1038173426Srpaulo default: 1039173426Srpaulo device_printf(dev, "%s called with invalid argument %s\n", 1040173426Srpaulo __func__, key); 1041173426Srpaulo error = 1; 1042173426Srpaulo goto out; 1043173426Srpaulo } 1044173426Srpaulo *val = ((int16_t)buf[0] << 8) | buf[1]; 1045173426Srpauloout: 1046173426Srpaulo return (error); 1047173426Srpaulo} 1048173426Srpaulo 1049173426Srpaulostatic void 1050173426Srpauloasmc_sms_calibrate(device_t dev) 1051173426Srpaulo{ 1052173426Srpaulo struct asmc_softc *sc = device_get_softc(dev); 1053173426Srpaulo 1054173426Srpaulo asmc_sms_read(dev, ASMC_KEY_SMS_X, &sc->sms_rest_x); 1055173426Srpaulo asmc_sms_read(dev, ASMC_KEY_SMS_Y, &sc->sms_rest_y); 1056173426Srpaulo asmc_sms_read(dev, ASMC_KEY_SMS_Z, &sc->sms_rest_z); 1057173426Srpaulo} 1058173426Srpaulo 1059173426Srpaulostatic int 1060173426Srpauloasmc_sms_intrfast(void *arg) 1061173426Srpaulo{ 1062173426Srpaulo uint8_t type; 1063173426Srpaulo device_t dev = (device_t) arg; 1064173426Srpaulo struct asmc_softc *sc = device_get_softc(dev); 1065197190Srpaulo if (!sc->sc_sms_intr_works) 1066197190Srpaulo return (FILTER_HANDLED); 1067173426Srpaulo 1068173426Srpaulo mtx_lock_spin(&sc->sc_mtx); 1069177972Srpaulo type = ASMC_INTPORT_READ(sc); 1070173426Srpaulo mtx_unlock_spin(&sc->sc_mtx); 1071173426Srpaulo 1072173426Srpaulo sc->sc_sms_intrtype = type; 1073173426Srpaulo asmc_sms_printintr(dev, type); 1074173426Srpaulo 1075173426Srpaulo#ifdef INTR_FILTER 1076173426Srpaulo return (FILTER_SCHEDULE_THREAD | FILTER_HANDLED); 1077173426Srpaulo#else 1078173426Srpaulo taskqueue_enqueue(sc->sc_sms_tq, &sc->sc_sms_task); 1079173426Srpaulo#endif 1080173426Srpaulo return (FILTER_HANDLED); 1081173426Srpaulo} 1082173426Srpaulo 1083173426Srpaulo#ifdef INTR_FILTER 1084173426Srpaulostatic void 1085173426Srpauloasmc_sms_handler(void *arg) 1086173426Srpaulo{ 1087173426Srpaulo struct asmc_softc *sc = device_get_softc(arg); 1088173426Srpaulo 1089173426Srpaulo asmc_sms_task(sc, 0); 1090173426Srpaulo} 1091173426Srpaulo#endif 1092173426Srpaulo 1093173426Srpaulo 1094173426Srpaulostatic void 1095173426Srpauloasmc_sms_printintr(device_t dev, uint8_t type) 1096173426Srpaulo{ 1097173426Srpaulo 1098173426Srpaulo switch (type) { 1099173426Srpaulo case ASMC_SMS_INTFF: 1100173426Srpaulo device_printf(dev, "WARNING: possible free fall!\n"); 1101173426Srpaulo break; 1102173426Srpaulo case ASMC_SMS_INTHA: 1103173426Srpaulo device_printf(dev, "WARNING: high acceleration detected!\n"); 1104173426Srpaulo break; 1105173426Srpaulo case ASMC_SMS_INTSH: 1106173426Srpaulo device_printf(dev, "WARNING: possible shock!\n"); 1107173426Srpaulo break; 1108173426Srpaulo default: 1109173426Srpaulo device_printf(dev, "%s unknown interrupt\n", __func__); 1110173426Srpaulo } 1111173426Srpaulo} 1112173426Srpaulo 1113173426Srpaulostatic void 1114173426Srpauloasmc_sms_task(void *arg, int pending) 1115173426Srpaulo{ 1116173426Srpaulo struct asmc_softc *sc = (struct asmc_softc *)arg; 1117173426Srpaulo char notify[16]; 1118173426Srpaulo int type; 1119173426Srpaulo 1120173426Srpaulo switch (sc->sc_sms_intrtype) { 1121173426Srpaulo case ASMC_SMS_INTFF: 1122173426Srpaulo type = 2; 1123173426Srpaulo break; 1124173426Srpaulo case ASMC_SMS_INTHA: 1125173426Srpaulo type = 1; 1126173426Srpaulo break; 1127173426Srpaulo case ASMC_SMS_INTSH: 1128173426Srpaulo type = 0; 1129173426Srpaulo break; 1130173426Srpaulo default: 1131173426Srpaulo type = 255; 1132173426Srpaulo } 1133173426Srpaulo 1134173426Srpaulo snprintf(notify, sizeof(notify), " notify=0x%x", type); 1135177972Srpaulo devctl_notify("ACPI", "asmc", "SMS", notify); 1136173426Srpaulo} 1137173426Srpaulo 1138173426Srpaulostatic int 1139173426Srpauloasmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS) 1140173426Srpaulo{ 1141173426Srpaulo device_t dev = (device_t) arg1; 1142173426Srpaulo int error; 1143173426Srpaulo int16_t val; 1144173426Srpaulo int32_t v; 1145173426Srpaulo 1146173426Srpaulo asmc_sms_read(dev, ASMC_KEY_SMS_X, &val); 1147173426Srpaulo v = (int32_t) val; 1148173426Srpaulo error = sysctl_handle_int(oidp, &v, 0, req); 1149173426Srpaulo 1150173426Srpaulo return (error); 1151173426Srpaulo} 1152173426Srpaulo 1153173426Srpaulostatic int 1154173426Srpauloasmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS) 1155173426Srpaulo{ 1156173426Srpaulo device_t dev = (device_t) arg1; 1157173426Srpaulo int error; 1158173426Srpaulo int16_t val; 1159173426Srpaulo int32_t v; 1160173426Srpaulo 1161173426Srpaulo asmc_sms_read(dev, ASMC_KEY_SMS_Y, &val); 1162173426Srpaulo v = (int32_t) val; 1163173426Srpaulo error = sysctl_handle_int(oidp, &v, 0, req); 1164173426Srpaulo 1165173426Srpaulo return (error); 1166173426Srpaulo} 1167173426Srpaulo 1168173426Srpaulostatic int 1169173426Srpauloasmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS) 1170173426Srpaulo{ 1171173426Srpaulo device_t dev = (device_t) arg1; 1172173426Srpaulo int error; 1173173426Srpaulo int16_t val; 1174173426Srpaulo int32_t v; 1175173426Srpaulo 1176173426Srpaulo asmc_sms_read(dev, ASMC_KEY_SMS_Z, &val); 1177173426Srpaulo v = (int32_t) val; 1178173426Srpaulo error = sysctl_handle_int(oidp, &v, sizeof(v), req); 1179173426Srpaulo 1180173426Srpaulo return (error); 1181173426Srpaulo} 1182173426Srpaulo 1183173426Srpaulostatic int 1184173426Srpauloasmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS) 1185173426Srpaulo{ 1186173426Srpaulo device_t dev = (device_t) arg1; 1187173426Srpaulo uint8_t buf[6]; 1188173426Srpaulo int error; 1189173426Srpaulo int32_t v; 1190173426Srpaulo 1191173426Srpaulo asmc_key_read(dev, ASMC_KEY_LIGHTLEFT, buf, 6); 1192173426Srpaulo v = buf[2]; 1193173426Srpaulo error = sysctl_handle_int(oidp, &v, sizeof(v), req); 1194173426Srpaulo 1195173426Srpaulo return (error); 1196173426Srpaulo} 1197173426Srpaulo 1198173426Srpaulostatic int 1199173426Srpauloasmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS) 1200173426Srpaulo{ 1201173426Srpaulo device_t dev = (device_t) arg1; 1202173426Srpaulo uint8_t buf[6]; 1203173426Srpaulo int error; 1204173426Srpaulo int32_t v; 1205173426Srpaulo 1206173426Srpaulo asmc_key_read(dev, ASMC_KEY_LIGHTRIGHT, buf, 6); 1207173426Srpaulo v = buf[2]; 1208173426Srpaulo error = sysctl_handle_int(oidp, &v, sizeof(v), req); 1209195046Srpaulo 1210195046Srpaulo return (error); 1211195046Srpaulo} 1212195046Srpaulo 1213195046Srpaulostatic int 1214195046Srpauloasmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS) 1215195046Srpaulo{ 1216195046Srpaulo device_t dev = (device_t) arg1; 1217195046Srpaulo uint8_t buf[2]; 1218195046Srpaulo int error; 1219195046Srpaulo unsigned int level; 1220195046Srpaulo static int32_t v; 1221195046Srpaulo 1222195046Srpaulo error = sysctl_handle_int(oidp, &v, sizeof(v), req); 1223173426Srpaulo if (error == 0 && req->newptr != NULL) { 1224173426Srpaulo level = *(unsigned int *)req->newptr; 1225173426Srpaulo if (level > 255) 1226173426Srpaulo return (EINVAL); 1227195046Srpaulo v = level; 1228173426Srpaulo buf[0] = level; 1229173426Srpaulo buf[1] = 0x00; 1230173426Srpaulo asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, 2); 1231173426Srpaulo } 1232173426Srpaulo 1233173426Srpaulo return (error); 1234173426Srpaulo} 1235