132a8088fSRui Paulo /*- 24470f0f3SRui Paulo * Copyright (c) 2007, 2008 Rui Paulo <rpaulo@FreeBSD.org> 332a8088fSRui Paulo * All rights reserved. 432a8088fSRui Paulo * 532a8088fSRui Paulo * Redistribution and use in source and binary forms, with or without 632a8088fSRui Paulo * modification, are permitted provided that the following conditions 732a8088fSRui Paulo * are met: 832a8088fSRui Paulo * 1. Redistributions of source code must retain the above copyright 932a8088fSRui Paulo * notice, this list of conditions and the following disclaimer. 1032a8088fSRui Paulo * 2. Redistributions in binary form must reproduce the above copyright 1132a8088fSRui Paulo * notice, this list of conditions and the following disclaimer in the 1232a8088fSRui Paulo * documentation and/or other materials provided with the distribution. 1332a8088fSRui Paulo * 1432a8088fSRui Paulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1532a8088fSRui Paulo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 1632a8088fSRui Paulo * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 1732a8088fSRui Paulo * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 1832a8088fSRui Paulo * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 1932a8088fSRui Paulo * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 2032a8088fSRui Paulo * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2132a8088fSRui Paulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 2232a8088fSRui Paulo * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 2332a8088fSRui Paulo * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2432a8088fSRui Paulo * POSSIBILITY OF SUCH DAMAGE. 2532a8088fSRui Paulo * 2632a8088fSRui Paulo */ 2732a8088fSRui Paulo 2832a8088fSRui Paulo /* 2932a8088fSRui Paulo * Driver for Apple's System Management Console (SMC). 3032a8088fSRui Paulo * SMC can be found on the MacBook, MacBook Pro and Mac Mini. 3132a8088fSRui Paulo * 3232a8088fSRui Paulo * Inspired by the Linux applesmc driver. 3332a8088fSRui Paulo */ 3432a8088fSRui Paulo 3532a8088fSRui Paulo #include <sys/cdefs.h> 3632a8088fSRui Paulo __FBSDID("$FreeBSD$"); 3732a8088fSRui Paulo 3832a8088fSRui Paulo #include <sys/param.h> 3932a8088fSRui Paulo #include <sys/bus.h> 4032a8088fSRui Paulo #include <sys/conf.h> 4132a8088fSRui Paulo #include <sys/kernel.h> 4232a8088fSRui Paulo #include <sys/lock.h> 4332a8088fSRui Paulo #include <sys/malloc.h> 4432a8088fSRui Paulo #include <sys/module.h> 4532a8088fSRui Paulo #include <sys/mutex.h> 4632a8088fSRui Paulo #include <sys/sysctl.h> 4732a8088fSRui Paulo #include <sys/systm.h> 4832a8088fSRui Paulo #include <sys/taskqueue.h> 4932a8088fSRui Paulo #include <sys/rman.h> 504c061448SRui Paulo 5132a8088fSRui Paulo #include <machine/resource.h> 52129d3046SJung-uk Kim 53129d3046SJung-uk Kim #include <contrib/dev/acpica/include/acpi.h> 54129d3046SJung-uk Kim 554470f0f3SRui Paulo #include <dev/acpica/acpivar.h> 5632a8088fSRui Paulo #include <dev/asmc/asmcvar.h> 5732a8088fSRui Paulo 584c061448SRui Paulo #include "opt_intr_filter.h" 594c061448SRui Paulo 6032a8088fSRui Paulo /* 6132a8088fSRui Paulo * Device interface. 6232a8088fSRui Paulo */ 6332a8088fSRui Paulo static int asmc_probe(device_t dev); 6432a8088fSRui Paulo static int asmc_attach(device_t dev); 6532a8088fSRui Paulo static int asmc_detach(device_t dev); 6632a8088fSRui Paulo 6732a8088fSRui Paulo /* 6832a8088fSRui Paulo * SMC functions. 6932a8088fSRui Paulo */ 7032a8088fSRui Paulo static int asmc_init(device_t dev); 7132a8088fSRui Paulo static int asmc_wait(device_t dev, uint8_t val); 7232a8088fSRui Paulo static int asmc_key_write(device_t dev, const char *key, uint8_t *buf, 7332a8088fSRui Paulo uint8_t len); 7432a8088fSRui Paulo static int asmc_key_read(device_t dev, const char *key, uint8_t *buf, 7532a8088fSRui Paulo uint8_t); 7632a8088fSRui Paulo static int asmc_fan_count(device_t dev); 7732a8088fSRui Paulo static int asmc_fan_getvalue(device_t dev, const char *key, int fan); 7832a8088fSRui Paulo static int asmc_temp_getvalue(device_t dev, const char *key); 7932a8088fSRui Paulo static int asmc_sms_read(device_t, const char *key, int16_t *val); 8032a8088fSRui Paulo static void asmc_sms_calibrate(device_t dev); 8132a8088fSRui Paulo static int asmc_sms_intrfast(void *arg); 8232a8088fSRui Paulo #ifdef INTR_FILTER 8332a8088fSRui Paulo static void asmc_sms_handler(void *arg); 8432a8088fSRui Paulo #endif 8532a8088fSRui Paulo static void asmc_sms_printintr(device_t dev, uint8_t); 8632a8088fSRui Paulo static void asmc_sms_task(void *arg, int pending); 8732a8088fSRui Paulo 8832a8088fSRui Paulo /* 8932a8088fSRui Paulo * Model functions. 9032a8088fSRui Paulo */ 9132a8088fSRui Paulo static int asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS); 9232a8088fSRui Paulo static int asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS); 9332a8088fSRui Paulo static int asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS); 9432a8088fSRui Paulo static int asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS); 9532a8088fSRui Paulo static int asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS); 9632a8088fSRui Paulo static int asmc_temp_sysctl(SYSCTL_HANDLER_ARGS); 9732a8088fSRui Paulo static int asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS); 9832a8088fSRui Paulo static int asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS); 9932a8088fSRui Paulo static int asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS); 10032a8088fSRui Paulo static int asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS); 10132a8088fSRui Paulo static int asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS); 10232a8088fSRui Paulo 10332a8088fSRui Paulo struct asmc_model { 10432a8088fSRui Paulo const char *smc_model; /* smbios.system.product env var. */ 10532a8088fSRui Paulo const char *smc_desc; /* driver description */ 10632a8088fSRui Paulo 10732a8088fSRui Paulo /* Helper functions */ 10832a8088fSRui Paulo int (*smc_sms_x)(SYSCTL_HANDLER_ARGS); 10932a8088fSRui Paulo int (*smc_sms_y)(SYSCTL_HANDLER_ARGS); 11032a8088fSRui Paulo int (*smc_sms_z)(SYSCTL_HANDLER_ARGS); 11132a8088fSRui Paulo int (*smc_fan_speed)(SYSCTL_HANDLER_ARGS); 11232a8088fSRui Paulo int (*smc_fan_safespeed)(SYSCTL_HANDLER_ARGS); 11332a8088fSRui Paulo int (*smc_fan_minspeed)(SYSCTL_HANDLER_ARGS); 11432a8088fSRui Paulo int (*smc_fan_maxspeed)(SYSCTL_HANDLER_ARGS); 11532a8088fSRui Paulo int (*smc_fan_targetspeed)(SYSCTL_HANDLER_ARGS); 11632a8088fSRui Paulo int (*smc_light_left)(SYSCTL_HANDLER_ARGS); 11732a8088fSRui Paulo int (*smc_light_right)(SYSCTL_HANDLER_ARGS); 11832a8088fSRui Paulo 119d8246db0SRui Paulo const char *smc_temps[ASMC_TEMP_MAX]; 120d8246db0SRui Paulo const char *smc_tempnames[ASMC_TEMP_MAX]; 121d8246db0SRui Paulo const char *smc_tempdescs[ASMC_TEMP_MAX]; 12232a8088fSRui Paulo }; 12332a8088fSRui Paulo 12432a8088fSRui Paulo static struct asmc_model *asmc_match(device_t dev); 12532a8088fSRui Paulo 12632a8088fSRui Paulo #define ASMC_SMS_FUNCS asmc_mb_sysctl_sms_x, asmc_mb_sysctl_sms_y, \ 12732a8088fSRui Paulo asmc_mb_sysctl_sms_z 12832a8088fSRui Paulo 12932a8088fSRui Paulo #define ASMC_FAN_FUNCS asmc_mb_sysctl_fanspeed, asmc_mb_sysctl_fansafespeed, \ 13032a8088fSRui Paulo asmc_mb_sysctl_fanminspeed, \ 13132a8088fSRui Paulo asmc_mb_sysctl_fanmaxspeed, \ 13232a8088fSRui Paulo asmc_mb_sysctl_fantargetspeed 13332a8088fSRui Paulo #define ASMC_LIGHT_FUNCS asmc_mbp_sysctl_light_left, \ 13432a8088fSRui Paulo asmc_mbp_sysctl_light_right 13532a8088fSRui Paulo 13632a8088fSRui Paulo struct asmc_model asmc_models[] = { 13732a8088fSRui Paulo { 13832a8088fSRui Paulo "MacBook1,1", "Apple SMC MacBook Core Duo", 13932a8088fSRui Paulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, 14032a8088fSRui Paulo ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS 14132a8088fSRui Paulo }, 14232a8088fSRui Paulo 14332a8088fSRui Paulo { 14432a8088fSRui Paulo "MacBook2,1", "Apple SMC MacBook Core 2 Duo", 14532a8088fSRui Paulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, 14632a8088fSRui Paulo ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS 14732a8088fSRui Paulo }, 14832a8088fSRui Paulo 14932a8088fSRui Paulo { 15032a8088fSRui Paulo "MacBookPro1,1", "Apple SMC MacBook Pro Core Duo (15-inch)", 15132a8088fSRui Paulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 15232a8088fSRui Paulo ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 15332a8088fSRui Paulo }, 15432a8088fSRui Paulo 15532a8088fSRui Paulo { 15632a8088fSRui Paulo "MacBookPro1,2", "Apple SMC MacBook Pro Core Duo (17-inch)", 15732a8088fSRui Paulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 15832a8088fSRui Paulo ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 15932a8088fSRui Paulo }, 16032a8088fSRui Paulo 16132a8088fSRui Paulo { 16232a8088fSRui Paulo "MacBookPro2,1", "Apple SMC MacBook Pro Core 2 Duo (17-inch)", 16332a8088fSRui Paulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 16432a8088fSRui Paulo ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 16532a8088fSRui Paulo }, 16632a8088fSRui Paulo 16732a8088fSRui Paulo { 16832a8088fSRui Paulo "MacBookPro2,2", "Apple SMC MacBook Pro Core 2 Duo (15-inch)", 16932a8088fSRui Paulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 17032a8088fSRui Paulo ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 17132a8088fSRui Paulo }, 17232a8088fSRui Paulo 17332a8088fSRui Paulo { 17432a8088fSRui Paulo "MacBookPro3,1", "Apple SMC MacBook Pro Core 2 Duo (15-inch LED)", 17532a8088fSRui Paulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 17632a8088fSRui Paulo ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 17732a8088fSRui Paulo }, 17832a8088fSRui Paulo 17932a8088fSRui Paulo { 18032a8088fSRui Paulo "MacBookPro3,2", "Apple SMC MacBook Pro Core 2 Duo (17-inch HD)", 18132a8088fSRui Paulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 18232a8088fSRui Paulo ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 18332a8088fSRui Paulo }, 18432a8088fSRui Paulo 18532a8088fSRui Paulo /* The Mac Mini has no SMS */ 18632a8088fSRui Paulo { 18732a8088fSRui Paulo "Macmini1,1", "Apple SMC Mac Mini", 18832a8088fSRui Paulo NULL, NULL, NULL, 18932a8088fSRui Paulo ASMC_FAN_FUNCS, 1904b07bf84SRui Paulo NULL, NULL, 19132a8088fSRui Paulo ASMC_MM_TEMPS, ASMC_MM_TEMPNAMES, ASMC_MM_TEMPDESCS 19232a8088fSRui Paulo }, 19332a8088fSRui Paulo 194d8246db0SRui Paulo /* Idem for the MacPro */ 195d8246db0SRui Paulo { 196d8246db0SRui Paulo "MacPro2", "Apple SMC Mac Pro (8-core)", 197d8246db0SRui Paulo NULL, NULL, NULL, 198d8246db0SRui Paulo ASMC_FAN_FUNCS, 199d8246db0SRui Paulo NULL, NULL, 200d8246db0SRui Paulo ASMC_MP_TEMPS, ASMC_MP_TEMPNAMES, ASMC_MP_TEMPDESCS 201d8246db0SRui Paulo }, 202941f9f10SRui Paulo 203941f9f10SRui Paulo { 204941f9f10SRui Paulo "MacBookAir1,1", "Apple SMC MacBook Air", 205941f9f10SRui Paulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, 206941f9f10SRui Paulo ASMC_MBA_TEMPS, ASMC_MBA_TEMPNAMES, ASMC_MBA_TEMPDESCS 207941f9f10SRui Paulo }, 208941f9f10SRui Paulo 209d8246db0SRui Paulo 21032a8088fSRui Paulo { NULL, NULL } 21132a8088fSRui Paulo }; 21232a8088fSRui Paulo 21332a8088fSRui Paulo #undef ASMC_SMS_FUNCS 21432a8088fSRui Paulo #undef ASMC_FAN_FUNCS 21532a8088fSRui Paulo #undef ASMC_LIGHT_FUNCS 21632a8088fSRui Paulo 21732a8088fSRui Paulo /* 21832a8088fSRui Paulo * Driver methods. 21932a8088fSRui Paulo */ 22032a8088fSRui Paulo static device_method_t asmc_methods[] = { 22132a8088fSRui Paulo DEVMETHOD(device_probe, asmc_probe), 22232a8088fSRui Paulo DEVMETHOD(device_attach, asmc_attach), 22332a8088fSRui Paulo DEVMETHOD(device_detach, asmc_detach), 22432a8088fSRui Paulo 22532a8088fSRui Paulo { 0, 0 } 22632a8088fSRui Paulo }; 22732a8088fSRui Paulo 22832a8088fSRui Paulo static driver_t asmc_driver = { 22932a8088fSRui Paulo "asmc", 23032a8088fSRui Paulo asmc_methods, 23132a8088fSRui Paulo sizeof(struct asmc_softc) 23232a8088fSRui Paulo }; 23332a8088fSRui Paulo 2344470f0f3SRui Paulo /* 2354470f0f3SRui Paulo * Debugging 2364470f0f3SRui Paulo */ 2374470f0f3SRui Paulo #define _COMPONENT ACPI_OEM 2384470f0f3SRui Paulo ACPI_MODULE_NAME("ASMC") 2394470f0f3SRui Paulo #ifdef DEBUG 2404470f0f3SRui Paulo #define ASMC_DPRINTF(str) device_printf(dev, str) 2414fb9bf66SRui Paulo #else 2424fb9bf66SRui Paulo #define ASMC_DPRINTF(str) 2434470f0f3SRui Paulo #endif 2444470f0f3SRui Paulo 2454470f0f3SRui Paulo static char *asmc_ids[] = { "APP0001", NULL }; 2464470f0f3SRui Paulo 24732a8088fSRui Paulo static devclass_t asmc_devclass; 24832a8088fSRui Paulo 2494470f0f3SRui Paulo DRIVER_MODULE(asmc, acpi, asmc_driver, asmc_devclass, NULL, NULL); 2504470f0f3SRui Paulo MODULE_DEPEND(asmc, acpi, 1, 1, 1); 25132a8088fSRui Paulo 25232a8088fSRui Paulo static struct asmc_model * 25332a8088fSRui Paulo asmc_match(device_t dev) 25432a8088fSRui Paulo { 25532a8088fSRui Paulo int i; 25632a8088fSRui Paulo char *model; 25732a8088fSRui Paulo 25832a8088fSRui Paulo model = getenv("smbios.system.product"); 25947105877SRui Paulo if (model == NULL) 26047105877SRui Paulo return (NULL); 26147105877SRui Paulo 26232a8088fSRui Paulo for (i = 0; asmc_models[i].smc_model; i++) { 26332a8088fSRui Paulo if (!strncmp(model, asmc_models[i].smc_model, strlen(model))) { 26432a8088fSRui Paulo freeenv(model); 26532a8088fSRui Paulo return (&asmc_models[i]); 26632a8088fSRui Paulo } 26732a8088fSRui Paulo } 26832a8088fSRui Paulo freeenv(model); 26932a8088fSRui Paulo 27032a8088fSRui Paulo return (NULL); 27132a8088fSRui Paulo } 27232a8088fSRui Paulo 27332a8088fSRui Paulo static int 27432a8088fSRui Paulo asmc_probe(device_t dev) 27532a8088fSRui Paulo { 27632a8088fSRui Paulo struct asmc_model *model; 27732a8088fSRui Paulo 2789cb3ef6eSRui Paulo if (resource_disabled("asmc", 0)) 27932a8088fSRui Paulo return (ENXIO); 2804470f0f3SRui Paulo if (ACPI_ID_PROBE(device_get_parent(dev), dev, asmc_ids) == NULL) 2814470f0f3SRui Paulo return (ENXIO); 2824470f0f3SRui Paulo 28332a8088fSRui Paulo model = asmc_match(dev); 2844470f0f3SRui Paulo if (!model) { 2854470f0f3SRui Paulo device_printf(dev, "model not recognized\n"); 28632a8088fSRui Paulo return (ENXIO); 2874470f0f3SRui Paulo } 28832a8088fSRui Paulo device_set_desc(dev, model->smc_desc); 28932a8088fSRui Paulo 29032a8088fSRui Paulo return (BUS_PROBE_DEFAULT); 29132a8088fSRui Paulo } 29232a8088fSRui Paulo 29332a8088fSRui Paulo static int 29432a8088fSRui Paulo asmc_attach(device_t dev) 29532a8088fSRui Paulo { 29632a8088fSRui Paulo int i, j; 29732a8088fSRui Paulo int ret; 29832a8088fSRui Paulo char name[2]; 29932a8088fSRui Paulo struct asmc_softc *sc = device_get_softc(dev); 30032a8088fSRui Paulo struct sysctl_ctx_list *sysctlctx; 30132a8088fSRui Paulo struct sysctl_oid *sysctlnode; 30232a8088fSRui Paulo struct asmc_model *model; 30332a8088fSRui Paulo 3044470f0f3SRui Paulo sc->sc_ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT, 3054470f0f3SRui Paulo &sc->sc_rid_port, RF_ACTIVE); 3064470f0f3SRui Paulo if (sc->sc_ioport == NULL) { 3074470f0f3SRui Paulo device_printf(dev, "unable to allocate IO port\n"); 3084470f0f3SRui Paulo return (ENOMEM); 3094470f0f3SRui Paulo } 3104470f0f3SRui Paulo 31132a8088fSRui Paulo sysctlctx = device_get_sysctl_ctx(dev); 31232a8088fSRui Paulo sysctlnode = device_get_sysctl_tree(dev); 31332a8088fSRui Paulo 31432a8088fSRui Paulo model = asmc_match(dev); 31532a8088fSRui Paulo 31632a8088fSRui Paulo mtx_init(&sc->sc_mtx, "asmc", NULL, MTX_SPIN); 31732a8088fSRui Paulo 31832a8088fSRui Paulo sc->sc_model = model; 31932a8088fSRui Paulo asmc_init(dev); 32032a8088fSRui Paulo 32132a8088fSRui Paulo /* 32232a8088fSRui Paulo * dev.asmc.n.fan.* tree. 32332a8088fSRui Paulo */ 32432a8088fSRui Paulo sc->sc_fan_tree[0] = SYSCTL_ADD_NODE(sysctlctx, 32532a8088fSRui Paulo SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "fan", 32632a8088fSRui Paulo CTLFLAG_RD, 0, "Fan Root Tree"); 32732a8088fSRui Paulo 32832a8088fSRui Paulo for (i = 1; i <= sc->sc_nfan; i++) { 32932a8088fSRui Paulo j = i - 1; 33032a8088fSRui Paulo name[0] = '0' + j; 33132a8088fSRui Paulo name[1] = 0; 33232a8088fSRui Paulo sc->sc_fan_tree[i] = SYSCTL_ADD_NODE(sysctlctx, 33332a8088fSRui Paulo SYSCTL_CHILDREN(sc->sc_fan_tree[0]), 33432a8088fSRui Paulo OID_AUTO, name, CTLFLAG_RD, 0, 33532a8088fSRui Paulo "Fan Subtree"); 33632a8088fSRui Paulo 33732a8088fSRui Paulo SYSCTL_ADD_PROC(sysctlctx, 33832a8088fSRui Paulo SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 33932a8088fSRui Paulo OID_AUTO, "speed", CTLTYPE_INT | CTLFLAG_RD, 34032a8088fSRui Paulo dev, j, model->smc_fan_speed, "I", 34132a8088fSRui Paulo "Fan speed in RPM"); 34232a8088fSRui Paulo 34332a8088fSRui Paulo SYSCTL_ADD_PROC(sysctlctx, 34432a8088fSRui Paulo SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 34532a8088fSRui Paulo OID_AUTO, "safespeed", 34632a8088fSRui Paulo CTLTYPE_INT | CTLFLAG_RD, 34732a8088fSRui Paulo dev, j, model->smc_fan_safespeed, "I", 34832a8088fSRui Paulo "Fan safe speed in RPM"); 34932a8088fSRui Paulo 35032a8088fSRui Paulo SYSCTL_ADD_PROC(sysctlctx, 35132a8088fSRui Paulo SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 35232a8088fSRui Paulo OID_AUTO, "minspeed", 35332a8088fSRui Paulo CTLTYPE_INT | CTLFLAG_RD, 35432a8088fSRui Paulo dev, j, model->smc_fan_minspeed, "I", 35532a8088fSRui Paulo "Fan minimum speed in RPM"); 35632a8088fSRui Paulo 35732a8088fSRui Paulo SYSCTL_ADD_PROC(sysctlctx, 35832a8088fSRui Paulo SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 35932a8088fSRui Paulo OID_AUTO, "maxspeed", 36032a8088fSRui Paulo CTLTYPE_INT | CTLFLAG_RD, 36132a8088fSRui Paulo dev, j, model->smc_fan_maxspeed, "I", 36232a8088fSRui Paulo "Fan maximum speed in RPM"); 36332a8088fSRui Paulo 36432a8088fSRui Paulo SYSCTL_ADD_PROC(sysctlctx, 36532a8088fSRui Paulo SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 36632a8088fSRui Paulo OID_AUTO, "targetspeed", 36732a8088fSRui Paulo CTLTYPE_INT | CTLFLAG_RD, 36832a8088fSRui Paulo dev, j, model->smc_fan_targetspeed, "I", 36932a8088fSRui Paulo "Fan target speed in RPM"); 37032a8088fSRui Paulo } 37132a8088fSRui Paulo 37232a8088fSRui Paulo /* 37332a8088fSRui Paulo * dev.asmc.n.temp tree. 37432a8088fSRui Paulo */ 37532a8088fSRui Paulo sc->sc_temp_tree = SYSCTL_ADD_NODE(sysctlctx, 37632a8088fSRui Paulo SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "temp", 37732a8088fSRui Paulo CTLFLAG_RD, 0, "Temperature sensors"); 37832a8088fSRui Paulo 37932a8088fSRui Paulo for (i = 0; model->smc_temps[i]; i++) { 38032a8088fSRui Paulo SYSCTL_ADD_PROC(sysctlctx, 38132a8088fSRui Paulo SYSCTL_CHILDREN(sc->sc_temp_tree), 38232a8088fSRui Paulo OID_AUTO, model->smc_tempnames[i], 38332a8088fSRui Paulo CTLTYPE_INT | CTLFLAG_RD, 38432a8088fSRui Paulo dev, i, asmc_temp_sysctl, "I", 38532a8088fSRui Paulo model->smc_tempdescs[i]); 38632a8088fSRui Paulo } 38732a8088fSRui Paulo 38832a8088fSRui Paulo if (model->smc_sms_x == NULL) 38932a8088fSRui Paulo goto nosms; 39032a8088fSRui Paulo 39132a8088fSRui Paulo /* 39232a8088fSRui Paulo * dev.asmc.n.sms tree. 39332a8088fSRui Paulo */ 39432a8088fSRui Paulo sc->sc_sms_tree = SYSCTL_ADD_NODE(sysctlctx, 39532a8088fSRui Paulo SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "sms", 39632a8088fSRui Paulo CTLFLAG_RD, 0, "Sudden Motion Sensor"); 39732a8088fSRui Paulo 39832a8088fSRui Paulo SYSCTL_ADD_PROC(sysctlctx, 39932a8088fSRui Paulo SYSCTL_CHILDREN(sc->sc_sms_tree), 40032a8088fSRui Paulo OID_AUTO, "x", CTLTYPE_INT | CTLFLAG_RD, 40132a8088fSRui Paulo dev, 0, model->smc_sms_x, "I", 40232a8088fSRui Paulo "Sudden Motion Sensor X value"); 40332a8088fSRui Paulo 40432a8088fSRui Paulo SYSCTL_ADD_PROC(sysctlctx, 40532a8088fSRui Paulo SYSCTL_CHILDREN(sc->sc_sms_tree), 40632a8088fSRui Paulo OID_AUTO, "y", CTLTYPE_INT | CTLFLAG_RD, 40732a8088fSRui Paulo dev, 0, model->smc_sms_y, "I", 40832a8088fSRui Paulo "Sudden Motion Sensor Y value"); 40932a8088fSRui Paulo 41032a8088fSRui Paulo SYSCTL_ADD_PROC(sysctlctx, 41132a8088fSRui Paulo SYSCTL_CHILDREN(sc->sc_sms_tree), 41232a8088fSRui Paulo OID_AUTO, "z", CTLTYPE_INT | CTLFLAG_RD, 41332a8088fSRui Paulo dev, 0, model->smc_sms_z, "I", 41432a8088fSRui Paulo "Sudden Motion Sensor Z value"); 41532a8088fSRui Paulo 41632a8088fSRui Paulo /* 41732a8088fSRui Paulo * dev.asmc.n.light 41832a8088fSRui Paulo */ 41932a8088fSRui Paulo if (model->smc_light_left) { 42032a8088fSRui Paulo sc->sc_light_tree = SYSCTL_ADD_NODE(sysctlctx, 42132a8088fSRui Paulo SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "light", 42232a8088fSRui Paulo CTLFLAG_RD, 0, "Keyboard backlight sensors"); 42332a8088fSRui Paulo 42432a8088fSRui Paulo SYSCTL_ADD_PROC(sysctlctx, 42532a8088fSRui Paulo SYSCTL_CHILDREN(sc->sc_light_tree), 42632a8088fSRui Paulo OID_AUTO, "left", CTLTYPE_INT | CTLFLAG_RW, 42732a8088fSRui Paulo dev, 0, model->smc_light_left, "I", 42832a8088fSRui Paulo "Keyboard backlight left sensor"); 42932a8088fSRui Paulo 43032a8088fSRui Paulo SYSCTL_ADD_PROC(sysctlctx, 43132a8088fSRui Paulo SYSCTL_CHILDREN(sc->sc_light_tree), 43232a8088fSRui Paulo OID_AUTO, "right", CTLTYPE_INT | CTLFLAG_RW, 43332a8088fSRui Paulo dev, 0, model->smc_light_right, "I", 43432a8088fSRui Paulo "Keyboard backlight right sensor"); 43532a8088fSRui Paulo } 43632a8088fSRui Paulo 43732a8088fSRui Paulo /* 43832a8088fSRui Paulo * Need a taskqueue to send devctl_notify() events 43932a8088fSRui Paulo * when the SMS interrupt us. 44032a8088fSRui Paulo * 44132a8088fSRui Paulo * PI_REALTIME is used due to the sensitivity of the 44232a8088fSRui Paulo * interrupt. An interrupt from the SMS means that the 44332a8088fSRui Paulo * disk heads should be turned off as quickly as possible. 44432a8088fSRui Paulo * 44532a8088fSRui Paulo * We only need to do this for the non INTR_FILTER case. 44632a8088fSRui Paulo */ 44732a8088fSRui Paulo sc->sc_sms_tq = NULL; 44832a8088fSRui Paulo #ifndef INTR_FILTER 44932a8088fSRui Paulo TASK_INIT(&sc->sc_sms_task, 0, asmc_sms_task, sc); 45032a8088fSRui Paulo sc->sc_sms_tq = taskqueue_create_fast("asmc_taskq", M_WAITOK, 45132a8088fSRui Paulo taskqueue_thread_enqueue, &sc->sc_sms_tq); 45232a8088fSRui Paulo taskqueue_start_threads(&sc->sc_sms_tq, 1, PI_REALTIME, "%s sms taskq", 45332a8088fSRui Paulo device_get_nameunit(dev)); 45432a8088fSRui Paulo #endif 45532a8088fSRui Paulo /* 45632a8088fSRui Paulo * Allocate an IRQ for the SMS. 45732a8088fSRui Paulo */ 4584470f0f3SRui Paulo sc->sc_rid_irq = 0; 4594470f0f3SRui Paulo sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, 4604470f0f3SRui Paulo &sc->sc_rid_irq, RF_ACTIVE); 4614470f0f3SRui Paulo if (sc->sc_irq == NULL) { 46232a8088fSRui Paulo device_printf(dev, "unable to allocate IRQ resource\n"); 46332a8088fSRui Paulo ret = ENXIO; 46432a8088fSRui Paulo goto err2; 46532a8088fSRui Paulo } 46632a8088fSRui Paulo 4674470f0f3SRui Paulo ret = bus_setup_intr(dev, sc->sc_irq, 46832a8088fSRui Paulo INTR_TYPE_MISC | INTR_MPSAFE, 46932a8088fSRui Paulo #ifdef INTR_FILTER 47032a8088fSRui Paulo asmc_sms_intrfast, asmc_sms_handler, 47132a8088fSRui Paulo #else 47232a8088fSRui Paulo asmc_sms_intrfast, NULL, 47332a8088fSRui Paulo #endif 47432a8088fSRui Paulo dev, &sc->sc_cookie); 47532a8088fSRui Paulo 47632a8088fSRui Paulo if (ret) { 47732a8088fSRui Paulo device_printf(dev, "unable to setup SMS IRQ\n"); 47832a8088fSRui Paulo goto err1; 47932a8088fSRui Paulo } 48032a8088fSRui Paulo nosms: 48132a8088fSRui Paulo return (0); 48232a8088fSRui Paulo err1: 4834470f0f3SRui Paulo bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq, sc->sc_irq); 48432a8088fSRui Paulo err2: 4854470f0f3SRui Paulo bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port, 4864470f0f3SRui Paulo sc->sc_ioport); 48732a8088fSRui Paulo mtx_destroy(&sc->sc_mtx); 48832a8088fSRui Paulo if (sc->sc_sms_tq) 48932a8088fSRui Paulo taskqueue_free(sc->sc_sms_tq); 49032a8088fSRui Paulo 49132a8088fSRui Paulo return (ret); 49232a8088fSRui Paulo } 49332a8088fSRui Paulo 49432a8088fSRui Paulo static int 49532a8088fSRui Paulo asmc_detach(device_t dev) 49632a8088fSRui Paulo { 49732a8088fSRui Paulo struct asmc_softc *sc = device_get_softc(dev); 49832a8088fSRui Paulo 49932a8088fSRui Paulo if (sc->sc_sms_tq) { 50032a8088fSRui Paulo taskqueue_drain(sc->sc_sms_tq, &sc->sc_sms_task); 50132a8088fSRui Paulo taskqueue_free(sc->sc_sms_tq); 50232a8088fSRui Paulo } 50332a8088fSRui Paulo if (sc->sc_cookie) 5044470f0f3SRui Paulo bus_teardown_intr(dev, sc->sc_irq, sc->sc_cookie); 5054470f0f3SRui Paulo if (sc->sc_irq) 5064470f0f3SRui Paulo bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq, 5074470f0f3SRui Paulo sc->sc_irq); 5084470f0f3SRui Paulo if (sc->sc_ioport) 5094470f0f3SRui Paulo bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port, 5104470f0f3SRui Paulo sc->sc_ioport); 51132a8088fSRui Paulo mtx_destroy(&sc->sc_mtx); 51232a8088fSRui Paulo 51332a8088fSRui Paulo return (0); 51432a8088fSRui Paulo } 51532a8088fSRui Paulo 51632a8088fSRui Paulo static int 51732a8088fSRui Paulo asmc_init(device_t dev) 51832a8088fSRui Paulo { 51932a8088fSRui Paulo struct asmc_softc *sc = device_get_softc(dev); 52032a8088fSRui Paulo int i, error = 1; 52132a8088fSRui Paulo uint8_t buf[4]; 52232a8088fSRui Paulo 52332a8088fSRui Paulo if (sc->sc_model->smc_sms_x == NULL) 52432a8088fSRui Paulo goto nosms; 52532a8088fSRui Paulo 52632a8088fSRui Paulo /* 52732a8088fSRui Paulo * We are ready to recieve interrupts from the SMS. 52832a8088fSRui Paulo */ 52932a8088fSRui Paulo buf[0] = 0x01; 5304470f0f3SRui Paulo ASMC_DPRINTF(("intok key\n")); 53132a8088fSRui Paulo asmc_key_write(dev, ASMC_KEY_INTOK, buf, 1); 53232a8088fSRui Paulo DELAY(50); 53332a8088fSRui Paulo 53432a8088fSRui Paulo /* 53532a8088fSRui Paulo * Initiate the polling intervals. 53632a8088fSRui Paulo */ 53732a8088fSRui Paulo buf[0] = 20; /* msecs */ 5384470f0f3SRui Paulo ASMC_DPRINTF(("low int key\n")); 53932a8088fSRui Paulo asmc_key_write(dev, ASMC_KEY_SMS_LOW_INT, buf, 1); 54032a8088fSRui Paulo DELAY(200); 54132a8088fSRui Paulo 54232a8088fSRui Paulo buf[0] = 20; /* msecs */ 5434470f0f3SRui Paulo ASMC_DPRINTF(("high int key\n")); 54432a8088fSRui Paulo asmc_key_write(dev, ASMC_KEY_SMS_HIGH_INT, buf, 1); 54532a8088fSRui Paulo DELAY(200); 54632a8088fSRui Paulo 54732a8088fSRui Paulo buf[0] = 0x00; 54832a8088fSRui Paulo buf[1] = 0x60; 5494470f0f3SRui Paulo ASMC_DPRINTF(("sms low key\n")); 55032a8088fSRui Paulo asmc_key_write(dev, ASMC_KEY_SMS_LOW, buf, 2); 55132a8088fSRui Paulo DELAY(200); 55232a8088fSRui Paulo 55332a8088fSRui Paulo buf[0] = 0x01; 55432a8088fSRui Paulo buf[1] = 0xc0; 5554470f0f3SRui Paulo ASMC_DPRINTF(("sms high key\n")); 55632a8088fSRui Paulo asmc_key_write(dev, ASMC_KEY_SMS_HIGH, buf, 2); 55732a8088fSRui Paulo DELAY(200); 55832a8088fSRui Paulo 55932a8088fSRui Paulo /* 56032a8088fSRui Paulo * I'm not sure what this key does, but it seems to be 56132a8088fSRui Paulo * required. 56232a8088fSRui Paulo */ 56332a8088fSRui Paulo buf[0] = 0x01; 5644470f0f3SRui Paulo ASMC_DPRINTF(("sms flag key\n")); 56532a8088fSRui Paulo asmc_key_write(dev, ASMC_KEY_SMS_FLAG, buf, 1); 566b75dfbe8SRui Paulo DELAY(100); 56732a8088fSRui Paulo 56832a8088fSRui Paulo /* 56932a8088fSRui Paulo * Wait up to 5 seconds for SMS initialization. 57032a8088fSRui Paulo */ 57132a8088fSRui Paulo for (i = 0; i < 10000; i++) { 57232a8088fSRui Paulo if (asmc_key_read(dev, ASMC_KEY_SMS, buf, 2) == 0 && 57332a8088fSRui Paulo (buf[0] != 0x00 || buf[1] != 0x00)) { 57432a8088fSRui Paulo error = 0; 5754fb9bf66SRui Paulo goto out; 57632a8088fSRui Paulo } 57732a8088fSRui Paulo buf[0] = ASMC_SMS_INIT1; 57832a8088fSRui Paulo buf[1] = ASMC_SMS_INIT2; 5794470f0f3SRui Paulo ASMC_DPRINTF(("sms key\n")); 58032a8088fSRui Paulo asmc_key_write(dev, ASMC_KEY_SMS, buf, 2); 58132a8088fSRui Paulo DELAY(50); 58232a8088fSRui Paulo } 5834fb9bf66SRui Paulo device_printf(dev, "WARNING: Sudden Motion Sensor not initialized!\n"); 58432a8088fSRui Paulo 5854fb9bf66SRui Paulo out: 58632a8088fSRui Paulo asmc_sms_calibrate(dev); 58732a8088fSRui Paulo nosms: 58832a8088fSRui Paulo sc->sc_nfan = asmc_fan_count(dev); 58932a8088fSRui Paulo if (sc->sc_nfan > ASMC_MAXFANS) { 59032a8088fSRui Paulo device_printf(dev, "more than %d fans were detected. Please " 59132a8088fSRui Paulo "report this.\n", ASMC_MAXFANS); 59232a8088fSRui Paulo sc->sc_nfan = ASMC_MAXFANS; 59332a8088fSRui Paulo } 59432a8088fSRui Paulo 59532a8088fSRui Paulo if (bootverbose) { 59632a8088fSRui Paulo /* 59732a8088fSRui Paulo * XXX: The number of keys is a 32 bit buffer, but 59832a8088fSRui Paulo * right now Apple only uses the last 8 bit. 59932a8088fSRui Paulo */ 60032a8088fSRui Paulo asmc_key_read(dev, ASMC_NKEYS, buf, 4); 60132a8088fSRui Paulo device_printf(dev, "number of keys: %d\n", buf[3]); 60232a8088fSRui Paulo } 60332a8088fSRui Paulo 60432a8088fSRui Paulo return (error); 60532a8088fSRui Paulo } 60632a8088fSRui Paulo 60732a8088fSRui Paulo /* 60832a8088fSRui Paulo * We need to make sure that the SMC acks the byte sent. 60932a8088fSRui Paulo * Just wait up to 100 ms. 61032a8088fSRui Paulo */ 61132a8088fSRui Paulo static int 61232a8088fSRui Paulo asmc_wait(device_t dev, uint8_t val) 61332a8088fSRui Paulo { 6144470f0f3SRui Paulo struct asmc_softc *sc = device_get_softc(dev); 61532a8088fSRui Paulo u_int i; 61632a8088fSRui Paulo 61732a8088fSRui Paulo val = val & ASMC_STATUS_MASK; 61832a8088fSRui Paulo 61932a8088fSRui Paulo for (i = 0; i < 1000; i++) { 6204470f0f3SRui Paulo if ((ASMC_CMDPORT_READ(sc) & ASMC_STATUS_MASK) == val) 62132a8088fSRui Paulo return (0); 62232a8088fSRui Paulo DELAY(10); 62332a8088fSRui Paulo } 62432a8088fSRui Paulo 62532a8088fSRui Paulo device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, val, 6264470f0f3SRui Paulo ASMC_CMDPORT_READ(sc)); 62732a8088fSRui Paulo 62832a8088fSRui Paulo return (1); 62932a8088fSRui Paulo } 63032a8088fSRui Paulo 63132a8088fSRui Paulo static int 63232a8088fSRui Paulo asmc_key_read(device_t dev, const char *key, uint8_t *buf, uint8_t len) 63332a8088fSRui Paulo { 63432a8088fSRui Paulo int i, error = 1; 63532a8088fSRui Paulo struct asmc_softc *sc = device_get_softc(dev); 63632a8088fSRui Paulo 63732a8088fSRui Paulo mtx_lock_spin(&sc->sc_mtx); 63832a8088fSRui Paulo 6394470f0f3SRui Paulo ASMC_CMDPORT_WRITE(sc, ASMC_CMDREAD); 64032a8088fSRui Paulo if (asmc_wait(dev, 0x0c)) 64132a8088fSRui Paulo goto out; 64232a8088fSRui Paulo 64332a8088fSRui Paulo for (i = 0; i < 4; i++) { 6444470f0f3SRui Paulo ASMC_DATAPORT_WRITE(sc, key[i]); 64532a8088fSRui Paulo if (asmc_wait(dev, 0x04)) 64632a8088fSRui Paulo goto out; 64732a8088fSRui Paulo } 64832a8088fSRui Paulo 6494470f0f3SRui Paulo ASMC_DATAPORT_WRITE(sc, len); 65032a8088fSRui Paulo 65132a8088fSRui Paulo for (i = 0; i < len; i++) { 65232a8088fSRui Paulo if (asmc_wait(dev, 0x05)) 65332a8088fSRui Paulo goto out; 6544470f0f3SRui Paulo buf[i] = ASMC_DATAPORT_READ(sc); 65532a8088fSRui Paulo } 65632a8088fSRui Paulo 65732a8088fSRui Paulo error = 0; 65832a8088fSRui Paulo out: 65932a8088fSRui Paulo mtx_unlock_spin(&sc->sc_mtx); 66032a8088fSRui Paulo 66132a8088fSRui Paulo return (error); 66232a8088fSRui Paulo } 66332a8088fSRui Paulo 66432a8088fSRui Paulo static int 66532a8088fSRui Paulo asmc_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len) 66632a8088fSRui Paulo { 66732a8088fSRui Paulo int i, error = -1; 66832a8088fSRui Paulo struct asmc_softc *sc = device_get_softc(dev); 66932a8088fSRui Paulo 67032a8088fSRui Paulo mtx_lock_spin(&sc->sc_mtx); 67132a8088fSRui Paulo 6724470f0f3SRui Paulo ASMC_DPRINTF(("cmd port: cmd write\n")); 6734470f0f3SRui Paulo ASMC_CMDPORT_WRITE(sc, ASMC_CMDWRITE); 67432a8088fSRui Paulo if (asmc_wait(dev, 0x0c)) 67532a8088fSRui Paulo goto out; 67632a8088fSRui Paulo 6774470f0f3SRui Paulo ASMC_DPRINTF(("data port: key\n")); 67832a8088fSRui Paulo for (i = 0; i < 4; i++) { 6794470f0f3SRui Paulo ASMC_DATAPORT_WRITE(sc, key[i]); 68032a8088fSRui Paulo if (asmc_wait(dev, 0x04)) 68132a8088fSRui Paulo goto out; 68232a8088fSRui Paulo } 6834470f0f3SRui Paulo ASMC_DPRINTF(("data port: length\n")); 6844470f0f3SRui Paulo ASMC_DATAPORT_WRITE(sc, len); 68532a8088fSRui Paulo 6864470f0f3SRui Paulo ASMC_DPRINTF(("data port: buffer\n")); 68732a8088fSRui Paulo for (i = 0; i < len; i++) { 68832a8088fSRui Paulo if (asmc_wait(dev, 0x04)) 68932a8088fSRui Paulo goto out; 6904470f0f3SRui Paulo ASMC_DATAPORT_WRITE(sc, buf[i]); 69132a8088fSRui Paulo } 69232a8088fSRui Paulo 69332a8088fSRui Paulo error = 0; 69432a8088fSRui Paulo out: 69532a8088fSRui Paulo mtx_unlock_spin(&sc->sc_mtx); 69632a8088fSRui Paulo 69732a8088fSRui Paulo return (error); 69832a8088fSRui Paulo 69932a8088fSRui Paulo } 70032a8088fSRui Paulo 70132a8088fSRui Paulo /* 70232a8088fSRui Paulo * Fan control functions. 70332a8088fSRui Paulo */ 70432a8088fSRui Paulo static int 70532a8088fSRui Paulo asmc_fan_count(device_t dev) 70632a8088fSRui Paulo { 70732a8088fSRui Paulo uint8_t buf[1]; 70832a8088fSRui Paulo 70932a8088fSRui Paulo if (asmc_key_read(dev, ASMC_KEY_FANCOUNT, buf, 1) < 0) 71032a8088fSRui Paulo return (-1); 71132a8088fSRui Paulo 71232a8088fSRui Paulo return (buf[0]); 71332a8088fSRui Paulo } 71432a8088fSRui Paulo 71532a8088fSRui Paulo static int 71632a8088fSRui Paulo asmc_fan_getvalue(device_t dev, const char *key, int fan) 71732a8088fSRui Paulo { 71832a8088fSRui Paulo int speed; 71932a8088fSRui Paulo uint8_t buf[2]; 72032a8088fSRui Paulo char fankey[5]; 72132a8088fSRui Paulo 72232a8088fSRui Paulo snprintf(fankey, sizeof(fankey), key, fan); 72332a8088fSRui Paulo if (asmc_key_read(dev, fankey, buf, 2) < 0) 72432a8088fSRui Paulo return (-1); 72532a8088fSRui Paulo speed = (buf[0] << 6) | (buf[1] >> 2); 72632a8088fSRui Paulo 72732a8088fSRui Paulo return (speed); 72832a8088fSRui Paulo } 72932a8088fSRui Paulo 73032a8088fSRui Paulo static int 73132a8088fSRui Paulo asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS) 73232a8088fSRui Paulo { 73332a8088fSRui Paulo device_t dev = (device_t) arg1; 73432a8088fSRui Paulo int fan = arg2; 73532a8088fSRui Paulo int error; 73632a8088fSRui Paulo int32_t v; 73732a8088fSRui Paulo 73832a8088fSRui Paulo v = asmc_fan_getvalue(dev, ASMC_KEY_FANSPEED, fan); 73932a8088fSRui Paulo error = sysctl_handle_int(oidp, &v, 0, req); 74032a8088fSRui Paulo 74132a8088fSRui Paulo return (error); 74232a8088fSRui Paulo } 74332a8088fSRui Paulo 74432a8088fSRui Paulo static int 74532a8088fSRui Paulo asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS) 74632a8088fSRui Paulo { 74732a8088fSRui Paulo device_t dev = (device_t) arg1; 74832a8088fSRui Paulo int fan = arg2; 74932a8088fSRui Paulo int error; 75032a8088fSRui Paulo int32_t v; 75132a8088fSRui Paulo 75232a8088fSRui Paulo v = asmc_fan_getvalue(dev, ASMC_KEY_FANSAFESPEED, fan); 75332a8088fSRui Paulo error = sysctl_handle_int(oidp, &v, 0, req); 75432a8088fSRui Paulo 75532a8088fSRui Paulo return (error); 75632a8088fSRui Paulo } 75732a8088fSRui Paulo 75832a8088fSRui Paulo 75932a8088fSRui Paulo static int 76032a8088fSRui Paulo asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS) 76132a8088fSRui Paulo { 76232a8088fSRui Paulo device_t dev = (device_t) arg1; 76332a8088fSRui Paulo int fan = arg2; 76432a8088fSRui Paulo int error; 76532a8088fSRui Paulo int32_t v; 76632a8088fSRui Paulo 76732a8088fSRui Paulo v = asmc_fan_getvalue(dev, ASMC_KEY_FANMINSPEED, fan); 76832a8088fSRui Paulo error = sysctl_handle_int(oidp, &v, 0, req); 76932a8088fSRui Paulo 77032a8088fSRui Paulo return (error); 77132a8088fSRui Paulo } 77232a8088fSRui Paulo 77332a8088fSRui Paulo static int 77432a8088fSRui Paulo asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS) 77532a8088fSRui Paulo { 77632a8088fSRui Paulo device_t dev = (device_t) arg1; 77732a8088fSRui Paulo int fan = arg2; 77832a8088fSRui Paulo int error; 77932a8088fSRui Paulo int32_t v; 78032a8088fSRui Paulo 78132a8088fSRui Paulo v = asmc_fan_getvalue(dev, ASMC_KEY_FANMAXSPEED, fan); 78232a8088fSRui Paulo error = sysctl_handle_int(oidp, &v, 0, req); 78332a8088fSRui Paulo 78432a8088fSRui Paulo return (error); 78532a8088fSRui Paulo } 78632a8088fSRui Paulo 78732a8088fSRui Paulo static int 78832a8088fSRui Paulo asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS) 78932a8088fSRui Paulo { 79032a8088fSRui Paulo device_t dev = (device_t) arg1; 79132a8088fSRui Paulo int fan = arg2; 79232a8088fSRui Paulo int error; 79332a8088fSRui Paulo int32_t v; 79432a8088fSRui Paulo 79532a8088fSRui Paulo v = asmc_fan_getvalue(dev, ASMC_KEY_FANTARGETSPEED, fan); 79632a8088fSRui Paulo error = sysctl_handle_int(oidp, &v, 0, req); 79732a8088fSRui Paulo 79832a8088fSRui Paulo return (error); 79932a8088fSRui Paulo } 80032a8088fSRui Paulo 80132a8088fSRui Paulo /* 80232a8088fSRui Paulo * Temperature functions. 80332a8088fSRui Paulo */ 80432a8088fSRui Paulo static int 80532a8088fSRui Paulo asmc_temp_getvalue(device_t dev, const char *key) 80632a8088fSRui Paulo { 80732a8088fSRui Paulo uint8_t buf[2]; 80832a8088fSRui Paulo 80932a8088fSRui Paulo /* 81032a8088fSRui Paulo * Check for invalid temperatures. 81132a8088fSRui Paulo */ 81232a8088fSRui Paulo if (asmc_key_read(dev, key, buf, 2) < 0) 81332a8088fSRui Paulo return (-1); 81432a8088fSRui Paulo 81532a8088fSRui Paulo return (buf[0]); 81632a8088fSRui Paulo } 81732a8088fSRui Paulo 81832a8088fSRui Paulo static int 81932a8088fSRui Paulo asmc_temp_sysctl(SYSCTL_HANDLER_ARGS) 82032a8088fSRui Paulo { 82132a8088fSRui Paulo device_t dev = (device_t) arg1; 82232a8088fSRui Paulo struct asmc_softc *sc = device_get_softc(dev); 82332a8088fSRui Paulo int error, val; 82432a8088fSRui Paulo 82532a8088fSRui Paulo val = asmc_temp_getvalue(dev, sc->sc_model->smc_temps[arg2]); 82632a8088fSRui Paulo error = sysctl_handle_int(oidp, &val, 0, req); 82732a8088fSRui Paulo 82832a8088fSRui Paulo return (error); 82932a8088fSRui Paulo } 83032a8088fSRui Paulo 83132a8088fSRui Paulo /* 83232a8088fSRui Paulo * Sudden Motion Sensor functions. 83332a8088fSRui Paulo */ 83432a8088fSRui Paulo static int 83532a8088fSRui Paulo asmc_sms_read(device_t dev, const char *key, int16_t *val) 83632a8088fSRui Paulo { 83732a8088fSRui Paulo uint8_t buf[2]; 83832a8088fSRui Paulo int error; 83932a8088fSRui Paulo 84032a8088fSRui Paulo /* no need to do locking here as asmc_key_read() already does it */ 84132a8088fSRui Paulo switch (key[3]) { 84232a8088fSRui Paulo case 'X': 84332a8088fSRui Paulo case 'Y': 84432a8088fSRui Paulo case 'Z': 84532a8088fSRui Paulo error = asmc_key_read(dev, key, buf, 2); 84632a8088fSRui Paulo break; 84732a8088fSRui Paulo default: 84832a8088fSRui Paulo device_printf(dev, "%s called with invalid argument %s\n", 84932a8088fSRui Paulo __func__, key); 85032a8088fSRui Paulo error = 1; 85132a8088fSRui Paulo goto out; 85232a8088fSRui Paulo } 85332a8088fSRui Paulo *val = ((int16_t)buf[0] << 8) | buf[1]; 85432a8088fSRui Paulo out: 85532a8088fSRui Paulo return (error); 85632a8088fSRui Paulo } 85732a8088fSRui Paulo 85832a8088fSRui Paulo static void 85932a8088fSRui Paulo asmc_sms_calibrate(device_t dev) 86032a8088fSRui Paulo { 86132a8088fSRui Paulo struct asmc_softc *sc = device_get_softc(dev); 86232a8088fSRui Paulo 86332a8088fSRui Paulo asmc_sms_read(dev, ASMC_KEY_SMS_X, &sc->sms_rest_x); 86432a8088fSRui Paulo asmc_sms_read(dev, ASMC_KEY_SMS_Y, &sc->sms_rest_y); 86532a8088fSRui Paulo asmc_sms_read(dev, ASMC_KEY_SMS_Z, &sc->sms_rest_z); 86632a8088fSRui Paulo } 86732a8088fSRui Paulo 86832a8088fSRui Paulo static int 86932a8088fSRui Paulo asmc_sms_intrfast(void *arg) 87032a8088fSRui Paulo { 87132a8088fSRui Paulo uint8_t type; 87232a8088fSRui Paulo device_t dev = (device_t) arg; 87332a8088fSRui Paulo struct asmc_softc *sc = device_get_softc(dev); 87432a8088fSRui Paulo 87532a8088fSRui Paulo mtx_lock_spin(&sc->sc_mtx); 8764470f0f3SRui Paulo type = ASMC_INTPORT_READ(sc); 87732a8088fSRui Paulo mtx_unlock_spin(&sc->sc_mtx); 87832a8088fSRui Paulo 87932a8088fSRui Paulo sc->sc_sms_intrtype = type; 88032a8088fSRui Paulo asmc_sms_printintr(dev, type); 88132a8088fSRui Paulo 88232a8088fSRui Paulo #ifdef INTR_FILTER 88332a8088fSRui Paulo return (FILTER_SCHEDULE_THREAD | FILTER_HANDLED); 88432a8088fSRui Paulo #else 88532a8088fSRui Paulo taskqueue_enqueue(sc->sc_sms_tq, &sc->sc_sms_task); 88632a8088fSRui Paulo #endif 88732a8088fSRui Paulo return (FILTER_HANDLED); 88832a8088fSRui Paulo } 88932a8088fSRui Paulo 89032a8088fSRui Paulo #ifdef INTR_FILTER 89132a8088fSRui Paulo static void 89232a8088fSRui Paulo asmc_sms_handler(void *arg) 89332a8088fSRui Paulo { 89432a8088fSRui Paulo struct asmc_softc *sc = device_get_softc(arg); 89532a8088fSRui Paulo 89632a8088fSRui Paulo asmc_sms_task(sc, 0); 89732a8088fSRui Paulo } 89832a8088fSRui Paulo #endif 89932a8088fSRui Paulo 90032a8088fSRui Paulo 90132a8088fSRui Paulo static void 90232a8088fSRui Paulo asmc_sms_printintr(device_t dev, uint8_t type) 90332a8088fSRui Paulo { 90432a8088fSRui Paulo 90532a8088fSRui Paulo switch (type) { 90632a8088fSRui Paulo case ASMC_SMS_INTFF: 90732a8088fSRui Paulo device_printf(dev, "WARNING: possible free fall!\n"); 90832a8088fSRui Paulo break; 90932a8088fSRui Paulo case ASMC_SMS_INTHA: 91032a8088fSRui Paulo device_printf(dev, "WARNING: high acceleration detected!\n"); 91132a8088fSRui Paulo break; 91232a8088fSRui Paulo case ASMC_SMS_INTSH: 91332a8088fSRui Paulo device_printf(dev, "WARNING: possible shock!\n"); 91432a8088fSRui Paulo break; 91532a8088fSRui Paulo default: 91632a8088fSRui Paulo device_printf(dev, "%s unknown interrupt\n", __func__); 91732a8088fSRui Paulo } 91832a8088fSRui Paulo } 91932a8088fSRui Paulo 92032a8088fSRui Paulo static void 92132a8088fSRui Paulo asmc_sms_task(void *arg, int pending) 92232a8088fSRui Paulo { 92332a8088fSRui Paulo struct asmc_softc *sc = (struct asmc_softc *)arg; 92432a8088fSRui Paulo char notify[16]; 92532a8088fSRui Paulo int type; 92632a8088fSRui Paulo 92732a8088fSRui Paulo switch (sc->sc_sms_intrtype) { 92832a8088fSRui Paulo case ASMC_SMS_INTFF: 92932a8088fSRui Paulo type = 2; 93032a8088fSRui Paulo break; 93132a8088fSRui Paulo case ASMC_SMS_INTHA: 93232a8088fSRui Paulo type = 1; 93332a8088fSRui Paulo break; 93432a8088fSRui Paulo case ASMC_SMS_INTSH: 93532a8088fSRui Paulo type = 0; 93632a8088fSRui Paulo break; 93732a8088fSRui Paulo default: 93832a8088fSRui Paulo type = 255; 93932a8088fSRui Paulo } 94032a8088fSRui Paulo 94132a8088fSRui Paulo snprintf(notify, sizeof(notify), " notify=0x%x", type); 9424470f0f3SRui Paulo devctl_notify("ACPI", "asmc", "SMS", notify); 94332a8088fSRui Paulo } 94432a8088fSRui Paulo 94532a8088fSRui Paulo static int 94632a8088fSRui Paulo asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS) 94732a8088fSRui Paulo { 94832a8088fSRui Paulo device_t dev = (device_t) arg1; 94932a8088fSRui Paulo int error; 95032a8088fSRui Paulo int16_t val; 95132a8088fSRui Paulo int32_t v; 95232a8088fSRui Paulo 95332a8088fSRui Paulo asmc_sms_read(dev, ASMC_KEY_SMS_X, &val); 95432a8088fSRui Paulo v = (int32_t) val; 95532a8088fSRui Paulo error = sysctl_handle_int(oidp, &v, 0, req); 95632a8088fSRui Paulo 95732a8088fSRui Paulo return (error); 95832a8088fSRui Paulo } 95932a8088fSRui Paulo 96032a8088fSRui Paulo static int 96132a8088fSRui Paulo asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS) 96232a8088fSRui Paulo { 96332a8088fSRui Paulo device_t dev = (device_t) arg1; 96432a8088fSRui Paulo int error; 96532a8088fSRui Paulo int16_t val; 96632a8088fSRui Paulo int32_t v; 96732a8088fSRui Paulo 96832a8088fSRui Paulo asmc_sms_read(dev, ASMC_KEY_SMS_Y, &val); 96932a8088fSRui Paulo v = (int32_t) val; 97032a8088fSRui Paulo error = sysctl_handle_int(oidp, &v, 0, req); 97132a8088fSRui Paulo 97232a8088fSRui Paulo return (error); 97332a8088fSRui Paulo } 97432a8088fSRui Paulo 97532a8088fSRui Paulo static int 97632a8088fSRui Paulo asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS) 97732a8088fSRui Paulo { 97832a8088fSRui Paulo device_t dev = (device_t) arg1; 97932a8088fSRui Paulo int error; 98032a8088fSRui Paulo int16_t val; 98132a8088fSRui Paulo int32_t v; 98232a8088fSRui Paulo 98332a8088fSRui Paulo asmc_sms_read(dev, ASMC_KEY_SMS_Z, &val); 98432a8088fSRui Paulo v = (int32_t) val; 98532a8088fSRui Paulo error = sysctl_handle_int(oidp, &v, sizeof(v), req); 98632a8088fSRui Paulo 98732a8088fSRui Paulo return (error); 98832a8088fSRui Paulo } 98932a8088fSRui Paulo 99032a8088fSRui Paulo static int 99132a8088fSRui Paulo asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS) 99232a8088fSRui Paulo { 99332a8088fSRui Paulo device_t dev = (device_t) arg1; 99432a8088fSRui Paulo uint8_t buf[6]; 99532a8088fSRui Paulo int error; 99632a8088fSRui Paulo unsigned int level; 99732a8088fSRui Paulo int32_t v; 99832a8088fSRui Paulo 99932a8088fSRui Paulo asmc_key_read(dev, ASMC_KEY_LIGHTLEFT, buf, 6); 100032a8088fSRui Paulo v = buf[2]; 100132a8088fSRui Paulo error = sysctl_handle_int(oidp, &v, sizeof(v), req); 100232a8088fSRui Paulo if (error == 0 && req->newptr != NULL) { 100332a8088fSRui Paulo level = *(unsigned int *)req->newptr; 100432a8088fSRui Paulo if (level > 255) 100532a8088fSRui Paulo return (EINVAL); 100632a8088fSRui Paulo buf[0] = level; 100732a8088fSRui Paulo buf[1] = 0x00; 100832a8088fSRui Paulo asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, 2); 100932a8088fSRui Paulo } 101032a8088fSRui Paulo 101132a8088fSRui Paulo return (error); 101232a8088fSRui Paulo } 101332a8088fSRui Paulo 101432a8088fSRui Paulo static int 101532a8088fSRui Paulo asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS) 101632a8088fSRui Paulo { 101732a8088fSRui Paulo device_t dev = (device_t) arg1; 101832a8088fSRui Paulo uint8_t buf[6]; 101932a8088fSRui Paulo int error; 102032a8088fSRui Paulo unsigned int level; 102132a8088fSRui Paulo int32_t v; 102232a8088fSRui Paulo 102332a8088fSRui Paulo asmc_key_read(dev, ASMC_KEY_LIGHTRIGHT, buf, 6); 102432a8088fSRui Paulo v = buf[2]; 102532a8088fSRui Paulo error = sysctl_handle_int(oidp, &v, sizeof(v), req); 102632a8088fSRui Paulo if (error == 0 && req->newptr != NULL) { 102732a8088fSRui Paulo level = *(unsigned int *)req->newptr; 102832a8088fSRui Paulo if (level > 255) 102932a8088fSRui Paulo return (EINVAL); 103032a8088fSRui Paulo buf[0] = level; 103132a8088fSRui Paulo buf[1] = 0x00; 103232a8088fSRui Paulo asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, 2); 103332a8088fSRui Paulo } 103432a8088fSRui Paulo 103532a8088fSRui Paulo return (error); 103632a8088fSRui Paulo } 1037