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); 66*108e3076SAdrian Chadd static int asmc_resume(device_t dev); 6732a8088fSRui Paulo 6832a8088fSRui Paulo /* 6932a8088fSRui Paulo * SMC functions. 7032a8088fSRui Paulo */ 7132a8088fSRui Paulo static int asmc_init(device_t dev); 72be80e49aSRui Paulo static int asmc_command(device_t dev, uint8_t command); 7332a8088fSRui Paulo static int asmc_wait(device_t dev, uint8_t val); 74be80e49aSRui Paulo static int asmc_wait_ack(device_t dev, uint8_t val, int amount); 7532a8088fSRui Paulo static int asmc_key_write(device_t dev, const char *key, uint8_t *buf, 7632a8088fSRui Paulo uint8_t len); 7732a8088fSRui Paulo static int asmc_key_read(device_t dev, const char *key, uint8_t *buf, 7832a8088fSRui Paulo uint8_t); 7932a8088fSRui Paulo static int asmc_fan_count(device_t dev); 8032a8088fSRui Paulo static int asmc_fan_getvalue(device_t dev, const char *key, int fan); 81447666f0SRui Paulo static int asmc_fan_setvalue(device_t dev, const char *key, int fan, int speed); 8232a8088fSRui Paulo static int asmc_temp_getvalue(device_t dev, const char *key); 8332a8088fSRui Paulo static int asmc_sms_read(device_t, const char *key, int16_t *val); 8432a8088fSRui Paulo static void asmc_sms_calibrate(device_t dev); 8532a8088fSRui Paulo static int asmc_sms_intrfast(void *arg); 8632a8088fSRui Paulo #ifdef INTR_FILTER 8732a8088fSRui Paulo static void asmc_sms_handler(void *arg); 8832a8088fSRui Paulo #endif 8932a8088fSRui Paulo static void asmc_sms_printintr(device_t dev, uint8_t); 9032a8088fSRui Paulo static void asmc_sms_task(void *arg, int pending); 911269f4d4SRui Paulo #ifdef DEBUG 921269f4d4SRui Paulo void asmc_dumpall(device_t); 931269f4d4SRui Paulo static int asmc_key_dump(device_t, int); 941269f4d4SRui Paulo #endif 9532a8088fSRui Paulo 9632a8088fSRui Paulo /* 9732a8088fSRui Paulo * Model functions. 9832a8088fSRui Paulo */ 99447666f0SRui Paulo static int asmc_mb_sysctl_fanid(SYSCTL_HANDLER_ARGS); 10032a8088fSRui Paulo static int asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS); 10132a8088fSRui Paulo static int asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS); 10232a8088fSRui Paulo static int asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS); 10332a8088fSRui Paulo static int asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS); 10432a8088fSRui Paulo static int asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS); 10532a8088fSRui Paulo static int asmc_temp_sysctl(SYSCTL_HANDLER_ARGS); 10632a8088fSRui Paulo static int asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS); 10732a8088fSRui Paulo static int asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS); 10832a8088fSRui Paulo static int asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS); 10932a8088fSRui Paulo static int asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS); 11032a8088fSRui Paulo static int asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS); 111be80e49aSRui Paulo static int asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS); 11232a8088fSRui Paulo 11332a8088fSRui Paulo struct asmc_model { 11432a8088fSRui Paulo const char *smc_model; /* smbios.system.product env var. */ 11532a8088fSRui Paulo const char *smc_desc; /* driver description */ 11632a8088fSRui Paulo 11732a8088fSRui Paulo /* Helper functions */ 11832a8088fSRui Paulo int (*smc_sms_x)(SYSCTL_HANDLER_ARGS); 11932a8088fSRui Paulo int (*smc_sms_y)(SYSCTL_HANDLER_ARGS); 12032a8088fSRui Paulo int (*smc_sms_z)(SYSCTL_HANDLER_ARGS); 121447666f0SRui Paulo int (*smc_fan_id)(SYSCTL_HANDLER_ARGS); 12232a8088fSRui Paulo int (*smc_fan_speed)(SYSCTL_HANDLER_ARGS); 12332a8088fSRui Paulo int (*smc_fan_safespeed)(SYSCTL_HANDLER_ARGS); 12432a8088fSRui Paulo int (*smc_fan_minspeed)(SYSCTL_HANDLER_ARGS); 12532a8088fSRui Paulo int (*smc_fan_maxspeed)(SYSCTL_HANDLER_ARGS); 12632a8088fSRui Paulo int (*smc_fan_targetspeed)(SYSCTL_HANDLER_ARGS); 12732a8088fSRui Paulo int (*smc_light_left)(SYSCTL_HANDLER_ARGS); 12832a8088fSRui Paulo int (*smc_light_right)(SYSCTL_HANDLER_ARGS); 129be80e49aSRui Paulo int (*smc_light_control)(SYSCTL_HANDLER_ARGS); 13032a8088fSRui Paulo 131d8246db0SRui Paulo const char *smc_temps[ASMC_TEMP_MAX]; 132d8246db0SRui Paulo const char *smc_tempnames[ASMC_TEMP_MAX]; 133d8246db0SRui Paulo const char *smc_tempdescs[ASMC_TEMP_MAX]; 13432a8088fSRui Paulo }; 13532a8088fSRui Paulo 13632a8088fSRui Paulo static struct asmc_model *asmc_match(device_t dev); 13732a8088fSRui Paulo 13832a8088fSRui Paulo #define ASMC_SMS_FUNCS asmc_mb_sysctl_sms_x, asmc_mb_sysctl_sms_y, \ 13932a8088fSRui Paulo asmc_mb_sysctl_sms_z 14032a8088fSRui Paulo 141*108e3076SAdrian Chadd #define ASMC_SMS_FUNCS_DISABLED NULL,NULL,NULL 142*108e3076SAdrian Chadd 143447666f0SRui Paulo #define ASMC_FAN_FUNCS asmc_mb_sysctl_fanid, asmc_mb_sysctl_fanspeed, asmc_mb_sysctl_fansafespeed, \ 14432a8088fSRui Paulo asmc_mb_sysctl_fanminspeed, \ 14532a8088fSRui Paulo asmc_mb_sysctl_fanmaxspeed, \ 14632a8088fSRui Paulo asmc_mb_sysctl_fantargetspeed 147*108e3076SAdrian Chadd 148*108e3076SAdrian Chadd #define ASMC_FAN_FUNCS2 asmc_mb_sysctl_fanid, asmc_mb_sysctl_fanspeed, NULL, \ 149*108e3076SAdrian Chadd asmc_mb_sysctl_fanminspeed, \ 150*108e3076SAdrian Chadd asmc_mb_sysctl_fanmaxspeed, \ 151*108e3076SAdrian Chadd asmc_mb_sysctl_fantargetspeed 152*108e3076SAdrian Chadd 15332a8088fSRui Paulo #define ASMC_LIGHT_FUNCS asmc_mbp_sysctl_light_left, \ 154be80e49aSRui Paulo asmc_mbp_sysctl_light_right, \ 155be80e49aSRui Paulo asmc_mbp_sysctl_light_control 15632a8088fSRui Paulo 15732a8088fSRui Paulo struct asmc_model asmc_models[] = { 15832a8088fSRui Paulo { 15932a8088fSRui Paulo "MacBook1,1", "Apple SMC MacBook Core Duo", 160be80e49aSRui Paulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL, 16132a8088fSRui Paulo ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS 16232a8088fSRui Paulo }, 16332a8088fSRui Paulo 16432a8088fSRui Paulo { 16532a8088fSRui Paulo "MacBook2,1", "Apple SMC MacBook Core 2 Duo", 166be80e49aSRui Paulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL, 16732a8088fSRui Paulo ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS 16832a8088fSRui Paulo }, 16932a8088fSRui Paulo 17032a8088fSRui Paulo { 171*108e3076SAdrian Chadd "MacBook3,1", "Apple SMC MacBook Core 2 Duo", 172*108e3076SAdrian Chadd ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL, 173*108e3076SAdrian Chadd ASMC_MB31_TEMPS, ASMC_MB31_TEMPNAMES, ASMC_MB31_TEMPDESCS 174*108e3076SAdrian Chadd }, 175*108e3076SAdrian Chadd 176*108e3076SAdrian Chadd { 17732a8088fSRui Paulo "MacBookPro1,1", "Apple SMC MacBook Pro Core Duo (15-inch)", 17832a8088fSRui Paulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 17932a8088fSRui Paulo ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 18032a8088fSRui Paulo }, 18132a8088fSRui Paulo 18232a8088fSRui Paulo { 18332a8088fSRui Paulo "MacBookPro1,2", "Apple SMC MacBook Pro Core Duo (17-inch)", 18432a8088fSRui Paulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 18532a8088fSRui Paulo ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 18632a8088fSRui Paulo }, 18732a8088fSRui Paulo 18832a8088fSRui Paulo { 18932a8088fSRui Paulo "MacBookPro2,1", "Apple SMC MacBook Pro Core 2 Duo (17-inch)", 19032a8088fSRui Paulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 19132a8088fSRui Paulo ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 19232a8088fSRui Paulo }, 19332a8088fSRui Paulo 19432a8088fSRui Paulo { 19532a8088fSRui Paulo "MacBookPro2,2", "Apple SMC MacBook Pro Core 2 Duo (15-inch)", 19632a8088fSRui Paulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 19732a8088fSRui Paulo ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 19832a8088fSRui Paulo }, 19932a8088fSRui Paulo 20032a8088fSRui Paulo { 20132a8088fSRui Paulo "MacBookPro3,1", "Apple SMC MacBook Pro Core 2 Duo (15-inch LED)", 20232a8088fSRui Paulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 20332a8088fSRui Paulo ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 20432a8088fSRui Paulo }, 20532a8088fSRui Paulo 20632a8088fSRui Paulo { 20732a8088fSRui Paulo "MacBookPro3,2", "Apple SMC MacBook Pro Core 2 Duo (17-inch HD)", 20832a8088fSRui Paulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 20932a8088fSRui Paulo ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS 21032a8088fSRui Paulo }, 21132a8088fSRui Paulo 212be80e49aSRui Paulo { 213be80e49aSRui Paulo "MacBookPro4,1", "Apple SMC MacBook Pro Core 2 Duo (Penryn)", 214be80e49aSRui Paulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 215be80e49aSRui Paulo ASMC_MBP4_TEMPS, ASMC_MBP4_TEMPNAMES, ASMC_MBP4_TEMPDESCS 216be80e49aSRui Paulo }, 217be80e49aSRui Paulo 218447666f0SRui Paulo { 219447666f0SRui Paulo "MacBookPro8,2", "Apple SMC MacBook Pro (early 2011)", 220447666f0SRui Paulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 221447666f0SRui Paulo ASMC_MBP8_TEMPS, ASMC_MBP8_TEMPNAMES, ASMC_MBP8_TEMPDESCS 222447666f0SRui Paulo }, 223447666f0SRui Paulo 224447666f0SRui Paulo { 225447666f0SRui Paulo "MacBookPro11,3", "Apple SMC MacBook Pro Retina Core i7 (2013/2014)", 226447666f0SRui Paulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, 227447666f0SRui Paulo ASMC_MBP11_TEMPS, ASMC_MBP11_TEMPNAMES, ASMC_MBP11_TEMPDESCS 228447666f0SRui Paulo }, 229447666f0SRui Paulo 23032a8088fSRui Paulo /* The Mac Mini has no SMS */ 23132a8088fSRui Paulo { 23232a8088fSRui Paulo "Macmini1,1", "Apple SMC Mac Mini", 23332a8088fSRui Paulo NULL, NULL, NULL, 23432a8088fSRui Paulo ASMC_FAN_FUNCS, 235be80e49aSRui Paulo NULL, NULL, NULL, 23632a8088fSRui Paulo ASMC_MM_TEMPS, ASMC_MM_TEMPNAMES, ASMC_MM_TEMPDESCS 23732a8088fSRui Paulo }, 23832a8088fSRui Paulo 239764442e0SGavin Atkinson /* The Mac Mini 3,1 has no SMS */ 240764442e0SGavin Atkinson { 241764442e0SGavin Atkinson "Macmini3,1", "Apple SMC Mac Mini 3,1", 242764442e0SGavin Atkinson NULL, NULL, NULL, 243764442e0SGavin Atkinson ASMC_FAN_FUNCS, 244764442e0SGavin Atkinson NULL, NULL, NULL, 245764442e0SGavin Atkinson ASMC_MM31_TEMPS, ASMC_MM31_TEMPNAMES, ASMC_MM31_TEMPDESCS 246764442e0SGavin Atkinson }, 247764442e0SGavin Atkinson 248d8246db0SRui Paulo /* Idem for the MacPro */ 249d8246db0SRui Paulo { 250d8246db0SRui Paulo "MacPro2", "Apple SMC Mac Pro (8-core)", 251d8246db0SRui Paulo NULL, NULL, NULL, 252d8246db0SRui Paulo ASMC_FAN_FUNCS, 253be80e49aSRui Paulo NULL, NULL, NULL, 254d8246db0SRui Paulo ASMC_MP_TEMPS, ASMC_MP_TEMPNAMES, ASMC_MP_TEMPDESCS 255d8246db0SRui Paulo }, 256941f9f10SRui Paulo 257447666f0SRui Paulo /* Idem for the MacPro 2010*/ 258447666f0SRui Paulo { 259447666f0SRui Paulo "MacPro5,1", "Apple SMC MacPro (2010)", 260447666f0SRui Paulo NULL, NULL, NULL, 261447666f0SRui Paulo ASMC_FAN_FUNCS, 262447666f0SRui Paulo NULL, NULL, NULL, 263447666f0SRui Paulo ASMC_MP5_TEMPS, ASMC_MP5_TEMPNAMES, ASMC_MP5_TEMPDESCS 264447666f0SRui Paulo }, 265447666f0SRui Paulo 266941f9f10SRui Paulo { 267941f9f10SRui Paulo "MacBookAir1,1", "Apple SMC MacBook Air", 268be80e49aSRui Paulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL, 269941f9f10SRui Paulo ASMC_MBA_TEMPS, ASMC_MBA_TEMPNAMES, ASMC_MBA_TEMPDESCS 270941f9f10SRui Paulo }, 271941f9f10SRui Paulo 272447666f0SRui Paulo { 273447666f0SRui Paulo "MacBookAir3,1", "Apple SMC MacBook Air Core 2 Duo (Late 2010)", 274447666f0SRui Paulo ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL, 275447666f0SRui Paulo ASMC_MBA3_TEMPS, ASMC_MBA3_TEMPNAMES, ASMC_MBA3_TEMPDESCS 276447666f0SRui Paulo }, 277447666f0SRui Paulo 278*108e3076SAdrian Chadd { 279*108e3076SAdrian Chadd "MacBookAir5,1", "Apple SMC MacBook Air 11-inch (Mid 2012)", 280*108e3076SAdrian Chadd ASMC_SMS_FUNCS_DISABLED, 281*108e3076SAdrian Chadd ASMC_FAN_FUNCS2, 282*108e3076SAdrian Chadd ASMC_LIGHT_FUNCS, 283*108e3076SAdrian Chadd ASMC_MBA5_TEMPS, ASMC_MBA5_TEMPNAMES, ASMC_MBA5_TEMPDESCS 284*108e3076SAdrian Chadd }, 285*108e3076SAdrian Chadd 286*108e3076SAdrian Chadd { 287*108e3076SAdrian Chadd "MacBookAir5,2", "Apple SMC MacBook Air 13-inch (Mid 2012)", 288*108e3076SAdrian Chadd ASMC_SMS_FUNCS_DISABLED, 289*108e3076SAdrian Chadd ASMC_FAN_FUNCS2, 290*108e3076SAdrian Chadd ASMC_LIGHT_FUNCS, 291*108e3076SAdrian Chadd ASMC_MBA5_TEMPS, ASMC_MBA5_TEMPNAMES, ASMC_MBA5_TEMPDESCS 292*108e3076SAdrian Chadd }, 293*108e3076SAdrian Chadd 294d8246db0SRui Paulo 29532a8088fSRui Paulo { NULL, NULL } 29632a8088fSRui Paulo }; 29732a8088fSRui Paulo 29832a8088fSRui Paulo #undef ASMC_SMS_FUNCS 299*108e3076SAdrian Chadd #undef ASMC_SMS_FUNCS_DISABLED 30032a8088fSRui Paulo #undef ASMC_FAN_FUNCS 301*108e3076SAdrian Chadd #undef ASMC_FAN_FUNCS2 30232a8088fSRui Paulo #undef ASMC_LIGHT_FUNCS 30332a8088fSRui Paulo 30432a8088fSRui Paulo /* 30532a8088fSRui Paulo * Driver methods. 30632a8088fSRui Paulo */ 30732a8088fSRui Paulo static device_method_t asmc_methods[] = { 30832a8088fSRui Paulo DEVMETHOD(device_probe, asmc_probe), 30932a8088fSRui Paulo DEVMETHOD(device_attach, asmc_attach), 31032a8088fSRui Paulo DEVMETHOD(device_detach, asmc_detach), 311*108e3076SAdrian Chadd DEVMETHOD(device_resume, asmc_resume), 31232a8088fSRui Paulo 31332a8088fSRui Paulo { 0, 0 } 31432a8088fSRui Paulo }; 31532a8088fSRui Paulo 31632a8088fSRui Paulo static driver_t asmc_driver = { 31732a8088fSRui Paulo "asmc", 31832a8088fSRui Paulo asmc_methods, 31932a8088fSRui Paulo sizeof(struct asmc_softc) 32032a8088fSRui Paulo }; 32132a8088fSRui Paulo 3224470f0f3SRui Paulo /* 3234470f0f3SRui Paulo * Debugging 3244470f0f3SRui Paulo */ 3254470f0f3SRui Paulo #define _COMPONENT ACPI_OEM 3264470f0f3SRui Paulo ACPI_MODULE_NAME("ASMC") 3274470f0f3SRui Paulo #ifdef DEBUG 3284470f0f3SRui Paulo #define ASMC_DPRINTF(str) device_printf(dev, str) 3294fb9bf66SRui Paulo #else 3304fb9bf66SRui Paulo #define ASMC_DPRINTF(str) 3314470f0f3SRui Paulo #endif 3324470f0f3SRui Paulo 333be80e49aSRui Paulo /* NB: can't be const */ 3344470f0f3SRui Paulo static char *asmc_ids[] = { "APP0001", NULL }; 3354470f0f3SRui Paulo 33632a8088fSRui Paulo static devclass_t asmc_devclass; 33732a8088fSRui Paulo 338*108e3076SAdrian Chadd static unsigned int light_control = 0; 339*108e3076SAdrian Chadd 3404470f0f3SRui Paulo DRIVER_MODULE(asmc, acpi, asmc_driver, asmc_devclass, NULL, NULL); 3414470f0f3SRui Paulo MODULE_DEPEND(asmc, acpi, 1, 1, 1); 34232a8088fSRui Paulo 34332a8088fSRui Paulo static struct asmc_model * 34432a8088fSRui Paulo asmc_match(device_t dev) 34532a8088fSRui Paulo { 34632a8088fSRui Paulo int i; 34732a8088fSRui Paulo char *model; 34832a8088fSRui Paulo 3492be111bfSDavide Italiano model = kern_getenv("smbios.system.product"); 35047105877SRui Paulo if (model == NULL) 35147105877SRui Paulo return (NULL); 35247105877SRui Paulo 35332a8088fSRui Paulo for (i = 0; asmc_models[i].smc_model; i++) { 35432a8088fSRui Paulo if (!strncmp(model, asmc_models[i].smc_model, strlen(model))) { 35532a8088fSRui Paulo freeenv(model); 35632a8088fSRui Paulo return (&asmc_models[i]); 35732a8088fSRui Paulo } 35832a8088fSRui Paulo } 35932a8088fSRui Paulo freeenv(model); 36032a8088fSRui Paulo 36132a8088fSRui Paulo return (NULL); 36232a8088fSRui Paulo } 36332a8088fSRui Paulo 36432a8088fSRui Paulo static int 36532a8088fSRui Paulo asmc_probe(device_t dev) 36632a8088fSRui Paulo { 36732a8088fSRui Paulo struct asmc_model *model; 36832a8088fSRui Paulo 369a8de37b0SEitan Adler if (resource_disabled("asmc", 0)) 370a8de37b0SEitan Adler return (ENXIO); 3714470f0f3SRui Paulo if (ACPI_ID_PROBE(device_get_parent(dev), dev, asmc_ids) == NULL) 3724470f0f3SRui Paulo return (ENXIO); 3734470f0f3SRui Paulo 37432a8088fSRui Paulo model = asmc_match(dev); 3754470f0f3SRui Paulo if (!model) { 3764470f0f3SRui Paulo device_printf(dev, "model not recognized\n"); 37732a8088fSRui Paulo return (ENXIO); 3784470f0f3SRui Paulo } 37932a8088fSRui Paulo device_set_desc(dev, model->smc_desc); 38032a8088fSRui Paulo 38132a8088fSRui Paulo return (BUS_PROBE_DEFAULT); 38232a8088fSRui Paulo } 38332a8088fSRui Paulo 38432a8088fSRui Paulo static int 38532a8088fSRui Paulo asmc_attach(device_t dev) 38632a8088fSRui Paulo { 38732a8088fSRui Paulo int i, j; 38832a8088fSRui Paulo int ret; 38932a8088fSRui Paulo char name[2]; 39032a8088fSRui Paulo struct asmc_softc *sc = device_get_softc(dev); 39132a8088fSRui Paulo struct sysctl_ctx_list *sysctlctx; 39232a8088fSRui Paulo struct sysctl_oid *sysctlnode; 39332a8088fSRui Paulo struct asmc_model *model; 39432a8088fSRui Paulo 3954470f0f3SRui Paulo sc->sc_ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT, 3964470f0f3SRui Paulo &sc->sc_rid_port, RF_ACTIVE); 3974470f0f3SRui Paulo if (sc->sc_ioport == NULL) { 3984470f0f3SRui Paulo device_printf(dev, "unable to allocate IO port\n"); 3994470f0f3SRui Paulo return (ENOMEM); 4004470f0f3SRui Paulo } 4014470f0f3SRui Paulo 40232a8088fSRui Paulo sysctlctx = device_get_sysctl_ctx(dev); 40332a8088fSRui Paulo sysctlnode = device_get_sysctl_tree(dev); 40432a8088fSRui Paulo 40532a8088fSRui Paulo model = asmc_match(dev); 40632a8088fSRui Paulo 40732a8088fSRui Paulo mtx_init(&sc->sc_mtx, "asmc", NULL, MTX_SPIN); 40832a8088fSRui Paulo 40932a8088fSRui Paulo sc->sc_model = model; 41032a8088fSRui Paulo asmc_init(dev); 41132a8088fSRui Paulo 41232a8088fSRui Paulo /* 41332a8088fSRui Paulo * dev.asmc.n.fan.* tree. 41432a8088fSRui Paulo */ 41532a8088fSRui Paulo sc->sc_fan_tree[0] = SYSCTL_ADD_NODE(sysctlctx, 41632a8088fSRui Paulo SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "fan", 41732a8088fSRui Paulo CTLFLAG_RD, 0, "Fan Root Tree"); 41832a8088fSRui Paulo 41932a8088fSRui Paulo for (i = 1; i <= sc->sc_nfan; i++) { 42032a8088fSRui Paulo j = i - 1; 42132a8088fSRui Paulo name[0] = '0' + j; 42232a8088fSRui Paulo name[1] = 0; 42332a8088fSRui Paulo sc->sc_fan_tree[i] = SYSCTL_ADD_NODE(sysctlctx, 42432a8088fSRui Paulo SYSCTL_CHILDREN(sc->sc_fan_tree[0]), 42532a8088fSRui Paulo OID_AUTO, name, CTLFLAG_RD, 0, 42632a8088fSRui Paulo "Fan Subtree"); 42732a8088fSRui Paulo 42832a8088fSRui Paulo SYSCTL_ADD_PROC(sysctlctx, 42932a8088fSRui Paulo SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 430447666f0SRui Paulo OID_AUTO, "id", CTLTYPE_STRING | CTLFLAG_RD, 431447666f0SRui Paulo dev, j, model->smc_fan_id, "I", 432447666f0SRui Paulo "Fan ID"); 433447666f0SRui Paulo 434447666f0SRui Paulo SYSCTL_ADD_PROC(sysctlctx, 435447666f0SRui Paulo SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 43632a8088fSRui Paulo OID_AUTO, "speed", CTLTYPE_INT | CTLFLAG_RD, 43732a8088fSRui Paulo dev, j, model->smc_fan_speed, "I", 43832a8088fSRui Paulo "Fan speed in RPM"); 43932a8088fSRui Paulo 44032a8088fSRui Paulo SYSCTL_ADD_PROC(sysctlctx, 44132a8088fSRui Paulo SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 44232a8088fSRui Paulo OID_AUTO, "safespeed", 44332a8088fSRui Paulo CTLTYPE_INT | CTLFLAG_RD, 44432a8088fSRui Paulo dev, j, model->smc_fan_safespeed, "I", 44532a8088fSRui Paulo "Fan safe speed in RPM"); 44632a8088fSRui Paulo 44732a8088fSRui Paulo SYSCTL_ADD_PROC(sysctlctx, 44832a8088fSRui Paulo SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 44932a8088fSRui Paulo OID_AUTO, "minspeed", 450447666f0SRui Paulo CTLTYPE_INT | CTLFLAG_RW, 45132a8088fSRui Paulo dev, j, model->smc_fan_minspeed, "I", 45232a8088fSRui Paulo "Fan minimum speed in RPM"); 45332a8088fSRui Paulo 45432a8088fSRui Paulo SYSCTL_ADD_PROC(sysctlctx, 45532a8088fSRui Paulo SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 45632a8088fSRui Paulo OID_AUTO, "maxspeed", 457447666f0SRui Paulo CTLTYPE_INT | CTLFLAG_RW, 45832a8088fSRui Paulo dev, j, model->smc_fan_maxspeed, "I", 45932a8088fSRui Paulo "Fan maximum speed in RPM"); 46032a8088fSRui Paulo 46132a8088fSRui Paulo SYSCTL_ADD_PROC(sysctlctx, 46232a8088fSRui Paulo SYSCTL_CHILDREN(sc->sc_fan_tree[i]), 46332a8088fSRui Paulo OID_AUTO, "targetspeed", 464447666f0SRui Paulo CTLTYPE_INT | CTLFLAG_RW, 46532a8088fSRui Paulo dev, j, model->smc_fan_targetspeed, "I", 46632a8088fSRui Paulo "Fan target speed in RPM"); 46732a8088fSRui Paulo } 46832a8088fSRui Paulo 46932a8088fSRui Paulo /* 47032a8088fSRui Paulo * dev.asmc.n.temp tree. 47132a8088fSRui Paulo */ 47232a8088fSRui Paulo sc->sc_temp_tree = SYSCTL_ADD_NODE(sysctlctx, 47332a8088fSRui Paulo SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "temp", 47432a8088fSRui Paulo CTLFLAG_RD, 0, "Temperature sensors"); 47532a8088fSRui Paulo 47632a8088fSRui Paulo for (i = 0; model->smc_temps[i]; i++) { 47732a8088fSRui Paulo SYSCTL_ADD_PROC(sysctlctx, 47832a8088fSRui Paulo SYSCTL_CHILDREN(sc->sc_temp_tree), 47932a8088fSRui Paulo OID_AUTO, model->smc_tempnames[i], 48032a8088fSRui Paulo CTLTYPE_INT | CTLFLAG_RD, 48132a8088fSRui Paulo dev, i, asmc_temp_sysctl, "I", 48232a8088fSRui Paulo model->smc_tempdescs[i]); 48332a8088fSRui Paulo } 48432a8088fSRui Paulo 485be80e49aSRui Paulo /* 486be80e49aSRui Paulo * dev.asmc.n.light 487be80e49aSRui Paulo */ 488be80e49aSRui Paulo if (model->smc_light_left) { 489be80e49aSRui Paulo sc->sc_light_tree = SYSCTL_ADD_NODE(sysctlctx, 490be80e49aSRui Paulo SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "light", 491be80e49aSRui Paulo CTLFLAG_RD, 0, "Keyboard backlight sensors"); 492be80e49aSRui Paulo 493be80e49aSRui Paulo SYSCTL_ADD_PROC(sysctlctx, 494be80e49aSRui Paulo SYSCTL_CHILDREN(sc->sc_light_tree), 495be80e49aSRui Paulo OID_AUTO, "left", CTLTYPE_INT | CTLFLAG_RD, 496be80e49aSRui Paulo dev, 0, model->smc_light_left, "I", 497be80e49aSRui Paulo "Keyboard backlight left sensor"); 498be80e49aSRui Paulo 499be80e49aSRui Paulo SYSCTL_ADD_PROC(sysctlctx, 500be80e49aSRui Paulo SYSCTL_CHILDREN(sc->sc_light_tree), 501be80e49aSRui Paulo OID_AUTO, "right", CTLTYPE_INT | CTLFLAG_RD, 502be80e49aSRui Paulo dev, 0, model->smc_light_right, "I", 503be80e49aSRui Paulo "Keyboard backlight right sensor"); 504be80e49aSRui Paulo 505be80e49aSRui Paulo SYSCTL_ADD_PROC(sysctlctx, 506be80e49aSRui Paulo SYSCTL_CHILDREN(sc->sc_light_tree), 5073471c35dSRui Paulo OID_AUTO, "control", 5083471c35dSRui Paulo CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, 509be80e49aSRui Paulo dev, 0, model->smc_light_control, "I", 510be80e49aSRui Paulo "Keyboard backlight brightness control"); 511be80e49aSRui Paulo } 512be80e49aSRui Paulo 51332a8088fSRui Paulo if (model->smc_sms_x == NULL) 51432a8088fSRui Paulo goto nosms; 51532a8088fSRui Paulo 51632a8088fSRui Paulo /* 51732a8088fSRui Paulo * dev.asmc.n.sms tree. 51832a8088fSRui Paulo */ 51932a8088fSRui Paulo sc->sc_sms_tree = SYSCTL_ADD_NODE(sysctlctx, 52032a8088fSRui Paulo SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "sms", 52132a8088fSRui Paulo CTLFLAG_RD, 0, "Sudden Motion Sensor"); 52232a8088fSRui Paulo 52332a8088fSRui Paulo SYSCTL_ADD_PROC(sysctlctx, 52432a8088fSRui Paulo SYSCTL_CHILDREN(sc->sc_sms_tree), 52532a8088fSRui Paulo OID_AUTO, "x", CTLTYPE_INT | CTLFLAG_RD, 52632a8088fSRui Paulo dev, 0, model->smc_sms_x, "I", 52732a8088fSRui Paulo "Sudden Motion Sensor X value"); 52832a8088fSRui Paulo 52932a8088fSRui Paulo SYSCTL_ADD_PROC(sysctlctx, 53032a8088fSRui Paulo SYSCTL_CHILDREN(sc->sc_sms_tree), 53132a8088fSRui Paulo OID_AUTO, "y", CTLTYPE_INT | CTLFLAG_RD, 53232a8088fSRui Paulo dev, 0, model->smc_sms_y, "I", 53332a8088fSRui Paulo "Sudden Motion Sensor Y value"); 53432a8088fSRui Paulo 53532a8088fSRui Paulo SYSCTL_ADD_PROC(sysctlctx, 53632a8088fSRui Paulo SYSCTL_CHILDREN(sc->sc_sms_tree), 53732a8088fSRui Paulo OID_AUTO, "z", CTLTYPE_INT | CTLFLAG_RD, 53832a8088fSRui Paulo dev, 0, model->smc_sms_z, "I", 53932a8088fSRui Paulo "Sudden Motion Sensor Z value"); 54032a8088fSRui Paulo 54132a8088fSRui Paulo /* 54232a8088fSRui Paulo * Need a taskqueue to send devctl_notify() events 54332a8088fSRui Paulo * when the SMS interrupt us. 54432a8088fSRui Paulo * 54532a8088fSRui Paulo * PI_REALTIME is used due to the sensitivity of the 54632a8088fSRui Paulo * interrupt. An interrupt from the SMS means that the 54732a8088fSRui Paulo * disk heads should be turned off as quickly as possible. 54832a8088fSRui Paulo * 54932a8088fSRui Paulo * We only need to do this for the non INTR_FILTER case. 55032a8088fSRui Paulo */ 55132a8088fSRui Paulo sc->sc_sms_tq = NULL; 55232a8088fSRui Paulo #ifndef INTR_FILTER 55332a8088fSRui Paulo TASK_INIT(&sc->sc_sms_task, 0, asmc_sms_task, sc); 55432a8088fSRui Paulo sc->sc_sms_tq = taskqueue_create_fast("asmc_taskq", M_WAITOK, 55532a8088fSRui Paulo taskqueue_thread_enqueue, &sc->sc_sms_tq); 55632a8088fSRui Paulo taskqueue_start_threads(&sc->sc_sms_tq, 1, PI_REALTIME, "%s sms taskq", 55732a8088fSRui Paulo device_get_nameunit(dev)); 55832a8088fSRui Paulo #endif 55932a8088fSRui Paulo /* 56032a8088fSRui Paulo * Allocate an IRQ for the SMS. 56132a8088fSRui Paulo */ 5624470f0f3SRui Paulo sc->sc_rid_irq = 0; 5634470f0f3SRui Paulo sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, 5644470f0f3SRui Paulo &sc->sc_rid_irq, RF_ACTIVE); 5654470f0f3SRui Paulo if (sc->sc_irq == NULL) { 56632a8088fSRui Paulo device_printf(dev, "unable to allocate IRQ resource\n"); 56732a8088fSRui Paulo ret = ENXIO; 56832a8088fSRui Paulo goto err2; 56932a8088fSRui Paulo } 57032a8088fSRui Paulo 5714470f0f3SRui Paulo ret = bus_setup_intr(dev, sc->sc_irq, 57232a8088fSRui Paulo INTR_TYPE_MISC | INTR_MPSAFE, 57332a8088fSRui Paulo #ifdef INTR_FILTER 57432a8088fSRui Paulo asmc_sms_intrfast, asmc_sms_handler, 57532a8088fSRui Paulo #else 57632a8088fSRui Paulo asmc_sms_intrfast, NULL, 57732a8088fSRui Paulo #endif 57832a8088fSRui Paulo dev, &sc->sc_cookie); 57932a8088fSRui Paulo 58032a8088fSRui Paulo if (ret) { 58132a8088fSRui Paulo device_printf(dev, "unable to setup SMS IRQ\n"); 58232a8088fSRui Paulo goto err1; 58332a8088fSRui Paulo } 58432a8088fSRui Paulo nosms: 58532a8088fSRui Paulo return (0); 58632a8088fSRui Paulo err1: 5874470f0f3SRui Paulo bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq, sc->sc_irq); 58832a8088fSRui Paulo err2: 5894470f0f3SRui Paulo bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port, 5904470f0f3SRui Paulo sc->sc_ioport); 59132a8088fSRui Paulo mtx_destroy(&sc->sc_mtx); 59232a8088fSRui Paulo if (sc->sc_sms_tq) 59332a8088fSRui Paulo taskqueue_free(sc->sc_sms_tq); 59432a8088fSRui Paulo 59532a8088fSRui Paulo return (ret); 59632a8088fSRui Paulo } 59732a8088fSRui Paulo 59832a8088fSRui Paulo static int 59932a8088fSRui Paulo asmc_detach(device_t dev) 60032a8088fSRui Paulo { 60132a8088fSRui Paulo struct asmc_softc *sc = device_get_softc(dev); 60232a8088fSRui Paulo 60332a8088fSRui Paulo if (sc->sc_sms_tq) { 60432a8088fSRui Paulo taskqueue_drain(sc->sc_sms_tq, &sc->sc_sms_task); 60532a8088fSRui Paulo taskqueue_free(sc->sc_sms_tq); 60632a8088fSRui Paulo } 60732a8088fSRui Paulo if (sc->sc_cookie) 6084470f0f3SRui Paulo bus_teardown_intr(dev, sc->sc_irq, sc->sc_cookie); 6094470f0f3SRui Paulo if (sc->sc_irq) 6104470f0f3SRui Paulo bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq, 6114470f0f3SRui Paulo sc->sc_irq); 6124470f0f3SRui Paulo if (sc->sc_ioport) 6134470f0f3SRui Paulo bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port, 6144470f0f3SRui Paulo sc->sc_ioport); 61532a8088fSRui Paulo mtx_destroy(&sc->sc_mtx); 61632a8088fSRui Paulo 61732a8088fSRui Paulo return (0); 61832a8088fSRui Paulo } 61932a8088fSRui Paulo 620*108e3076SAdrian Chadd static int 621*108e3076SAdrian Chadd asmc_resume(device_t dev) 622*108e3076SAdrian Chadd { 623*108e3076SAdrian Chadd uint8_t buf[2]; 624*108e3076SAdrian Chadd buf[0] = light_control; 625*108e3076SAdrian Chadd buf[1] = 0x00; 626*108e3076SAdrian Chadd asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, sizeof buf); 627*108e3076SAdrian Chadd return (0); 628*108e3076SAdrian Chadd } 629*108e3076SAdrian Chadd 630*108e3076SAdrian Chadd 6311269f4d4SRui Paulo #ifdef DEBUG 6321269f4d4SRui Paulo void asmc_dumpall(device_t dev) 6331269f4d4SRui Paulo { 6341269f4d4SRui Paulo int i; 6351269f4d4SRui Paulo 6361269f4d4SRui Paulo /* XXX magic number */ 6371269f4d4SRui Paulo for (i=0; i < 0x100; i++) 6381269f4d4SRui Paulo asmc_key_dump(dev, i); 6391269f4d4SRui Paulo } 6401269f4d4SRui Paulo #endif 6411269f4d4SRui Paulo 64232a8088fSRui Paulo static int 64332a8088fSRui Paulo asmc_init(device_t dev) 64432a8088fSRui Paulo { 64532a8088fSRui Paulo struct asmc_softc *sc = device_get_softc(dev); 64632a8088fSRui Paulo int i, error = 1; 64732a8088fSRui Paulo uint8_t buf[4]; 64832a8088fSRui Paulo 64932a8088fSRui Paulo if (sc->sc_model->smc_sms_x == NULL) 65032a8088fSRui Paulo goto nosms; 65132a8088fSRui Paulo 65232a8088fSRui Paulo /* 65332a8088fSRui Paulo * We are ready to recieve interrupts from the SMS. 65432a8088fSRui Paulo */ 65532a8088fSRui Paulo buf[0] = 0x01; 6564470f0f3SRui Paulo ASMC_DPRINTF(("intok key\n")); 65732a8088fSRui Paulo asmc_key_write(dev, ASMC_KEY_INTOK, buf, 1); 65832a8088fSRui Paulo DELAY(50); 65932a8088fSRui Paulo 66032a8088fSRui Paulo /* 66132a8088fSRui Paulo * Initiate the polling intervals. 66232a8088fSRui Paulo */ 66332a8088fSRui Paulo buf[0] = 20; /* msecs */ 6644470f0f3SRui Paulo ASMC_DPRINTF(("low int key\n")); 66532a8088fSRui Paulo asmc_key_write(dev, ASMC_KEY_SMS_LOW_INT, buf, 1); 66632a8088fSRui Paulo DELAY(200); 66732a8088fSRui Paulo 66832a8088fSRui Paulo buf[0] = 20; /* msecs */ 6694470f0f3SRui Paulo ASMC_DPRINTF(("high int key\n")); 67032a8088fSRui Paulo asmc_key_write(dev, ASMC_KEY_SMS_HIGH_INT, buf, 1); 67132a8088fSRui Paulo DELAY(200); 67232a8088fSRui Paulo 67332a8088fSRui Paulo buf[0] = 0x00; 67432a8088fSRui Paulo buf[1] = 0x60; 6754470f0f3SRui Paulo ASMC_DPRINTF(("sms low key\n")); 67632a8088fSRui Paulo asmc_key_write(dev, ASMC_KEY_SMS_LOW, buf, 2); 67732a8088fSRui Paulo DELAY(200); 67832a8088fSRui Paulo 67932a8088fSRui Paulo buf[0] = 0x01; 68032a8088fSRui Paulo buf[1] = 0xc0; 6814470f0f3SRui Paulo ASMC_DPRINTF(("sms high key\n")); 68232a8088fSRui Paulo asmc_key_write(dev, ASMC_KEY_SMS_HIGH, buf, 2); 68332a8088fSRui Paulo DELAY(200); 68432a8088fSRui Paulo 68532a8088fSRui Paulo /* 68632a8088fSRui Paulo * I'm not sure what this key does, but it seems to be 68732a8088fSRui Paulo * required. 68832a8088fSRui Paulo */ 68932a8088fSRui Paulo buf[0] = 0x01; 6904470f0f3SRui Paulo ASMC_DPRINTF(("sms flag key\n")); 69132a8088fSRui Paulo asmc_key_write(dev, ASMC_KEY_SMS_FLAG, buf, 1); 692b75dfbe8SRui Paulo DELAY(100); 69332a8088fSRui Paulo 6941269f4d4SRui Paulo sc->sc_sms_intr_works = 0; 6951269f4d4SRui Paulo 69632a8088fSRui Paulo /* 6971269f4d4SRui Paulo * Retry SMS initialization 1000 times 6981269f4d4SRui Paulo * (takes approx. 2 seconds in worst case) 69932a8088fSRui Paulo */ 7001269f4d4SRui Paulo for (i = 0; i < 1000; i++) { 70132a8088fSRui Paulo if (asmc_key_read(dev, ASMC_KEY_SMS, buf, 2) == 0 && 7021269f4d4SRui Paulo (buf[0] == ASMC_SMS_INIT1 && buf[1] == ASMC_SMS_INIT2)) { 70332a8088fSRui Paulo error = 0; 7041269f4d4SRui Paulo sc->sc_sms_intr_works = 1; 7054fb9bf66SRui Paulo goto out; 70632a8088fSRui Paulo } 70732a8088fSRui Paulo buf[0] = ASMC_SMS_INIT1; 70832a8088fSRui Paulo buf[1] = ASMC_SMS_INIT2; 7094470f0f3SRui Paulo ASMC_DPRINTF(("sms key\n")); 71032a8088fSRui Paulo asmc_key_write(dev, ASMC_KEY_SMS, buf, 2); 71132a8088fSRui Paulo DELAY(50); 71232a8088fSRui Paulo } 7134fb9bf66SRui Paulo device_printf(dev, "WARNING: Sudden Motion Sensor not initialized!\n"); 71432a8088fSRui Paulo 7154fb9bf66SRui Paulo out: 71632a8088fSRui Paulo asmc_sms_calibrate(dev); 71732a8088fSRui Paulo nosms: 71832a8088fSRui Paulo sc->sc_nfan = asmc_fan_count(dev); 71932a8088fSRui Paulo if (sc->sc_nfan > ASMC_MAXFANS) { 72032a8088fSRui Paulo device_printf(dev, "more than %d fans were detected. Please " 72132a8088fSRui Paulo "report this.\n", ASMC_MAXFANS); 72232a8088fSRui Paulo sc->sc_nfan = ASMC_MAXFANS; 72332a8088fSRui Paulo } 72432a8088fSRui Paulo 72532a8088fSRui Paulo if (bootverbose) { 72632a8088fSRui Paulo /* 727447666f0SRui Paulo * The number of keys is a 32 bit buffer 72832a8088fSRui Paulo */ 72932a8088fSRui Paulo asmc_key_read(dev, ASMC_NKEYS, buf, 4); 730447666f0SRui Paulo device_printf(dev, "number of keys: %d\n", ntohl(*(uint32_t*)buf)); 73132a8088fSRui Paulo } 73232a8088fSRui Paulo 7331269f4d4SRui Paulo #ifdef DEBUG 7341269f4d4SRui Paulo asmc_dumpall(dev); 7351269f4d4SRui Paulo #endif 7361269f4d4SRui Paulo 73732a8088fSRui Paulo return (error); 73832a8088fSRui Paulo } 73932a8088fSRui Paulo 74032a8088fSRui Paulo /* 74132a8088fSRui Paulo * We need to make sure that the SMC acks the byte sent. 742be80e49aSRui Paulo * Just wait up to (amount * 10) ms. 74332a8088fSRui Paulo */ 74432a8088fSRui Paulo static int 745be80e49aSRui Paulo asmc_wait_ack(device_t dev, uint8_t val, int amount) 74632a8088fSRui Paulo { 7474470f0f3SRui Paulo struct asmc_softc *sc = device_get_softc(dev); 74832a8088fSRui Paulo u_int i; 74932a8088fSRui Paulo 75032a8088fSRui Paulo val = val & ASMC_STATUS_MASK; 75132a8088fSRui Paulo 752be80e49aSRui Paulo for (i = 0; i < amount; i++) { 7534470f0f3SRui Paulo if ((ASMC_CMDPORT_READ(sc) & ASMC_STATUS_MASK) == val) 75432a8088fSRui Paulo return (0); 75532a8088fSRui Paulo DELAY(10); 75632a8088fSRui Paulo } 75732a8088fSRui Paulo 758be80e49aSRui Paulo return (1); 759be80e49aSRui Paulo } 760be80e49aSRui Paulo 761be80e49aSRui Paulo /* 762be80e49aSRui Paulo * We need to make sure that the SMC acks the byte sent. 763be80e49aSRui Paulo * Just wait up to 100 ms. 764be80e49aSRui Paulo */ 765be80e49aSRui Paulo static int 766be80e49aSRui Paulo asmc_wait(device_t dev, uint8_t val) 767be80e49aSRui Paulo { 768be80e49aSRui Paulo struct asmc_softc *sc; 769be80e49aSRui Paulo 770be80e49aSRui Paulo if (asmc_wait_ack(dev, val, 1000) == 0) 771be80e49aSRui Paulo return (0); 772be80e49aSRui Paulo 773be80e49aSRui Paulo sc = device_get_softc(dev); 774be80e49aSRui Paulo val = val & ASMC_STATUS_MASK; 775be80e49aSRui Paulo 776be80e49aSRui Paulo #ifdef DEBUG 77732a8088fSRui Paulo device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, val, 7784470f0f3SRui Paulo ASMC_CMDPORT_READ(sc)); 779be80e49aSRui Paulo #endif 780be80e49aSRui Paulo return (1); 781be80e49aSRui Paulo } 78232a8088fSRui Paulo 783be80e49aSRui Paulo /* 784be80e49aSRui Paulo * Send the given command, retrying up to 10 times if 785be80e49aSRui Paulo * the acknowledgement fails. 786be80e49aSRui Paulo */ 787be80e49aSRui Paulo static int 788be80e49aSRui Paulo asmc_command(device_t dev, uint8_t command) { 789be80e49aSRui Paulo 790be80e49aSRui Paulo int i; 791be80e49aSRui Paulo struct asmc_softc *sc = device_get_softc(dev); 792be80e49aSRui Paulo 793be80e49aSRui Paulo for (i=0; i < 10; i++) { 794be80e49aSRui Paulo ASMC_CMDPORT_WRITE(sc, command); 795be80e49aSRui Paulo if (asmc_wait_ack(dev, 0x0c, 100) == 0) { 796be80e49aSRui Paulo return (0); 797be80e49aSRui Paulo } 798be80e49aSRui Paulo } 799be80e49aSRui Paulo 800be80e49aSRui Paulo #ifdef DEBUG 801be80e49aSRui Paulo device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, command, 802be80e49aSRui Paulo ASMC_CMDPORT_READ(sc)); 803be80e49aSRui Paulo #endif 80432a8088fSRui Paulo return (1); 80532a8088fSRui Paulo } 80632a8088fSRui Paulo 80732a8088fSRui Paulo static int 80832a8088fSRui Paulo asmc_key_read(device_t dev, const char *key, uint8_t *buf, uint8_t len) 80932a8088fSRui Paulo { 810be80e49aSRui Paulo int i, error = 1, try = 0; 81132a8088fSRui Paulo struct asmc_softc *sc = device_get_softc(dev); 81232a8088fSRui Paulo 81332a8088fSRui Paulo mtx_lock_spin(&sc->sc_mtx); 81432a8088fSRui Paulo 815be80e49aSRui Paulo begin: 816be80e49aSRui Paulo if (asmc_command(dev, ASMC_CMDREAD)) 81732a8088fSRui Paulo goto out; 81832a8088fSRui Paulo 81932a8088fSRui Paulo for (i = 0; i < 4; i++) { 8204470f0f3SRui Paulo ASMC_DATAPORT_WRITE(sc, key[i]); 82132a8088fSRui Paulo if (asmc_wait(dev, 0x04)) 82232a8088fSRui Paulo goto out; 82332a8088fSRui Paulo } 82432a8088fSRui Paulo 8254470f0f3SRui Paulo ASMC_DATAPORT_WRITE(sc, len); 82632a8088fSRui Paulo 82732a8088fSRui Paulo for (i = 0; i < len; i++) { 82832a8088fSRui Paulo if (asmc_wait(dev, 0x05)) 82932a8088fSRui Paulo goto out; 8304470f0f3SRui Paulo buf[i] = ASMC_DATAPORT_READ(sc); 83132a8088fSRui Paulo } 83232a8088fSRui Paulo 83332a8088fSRui Paulo error = 0; 83432a8088fSRui Paulo out: 835be80e49aSRui Paulo if (error) { 836be80e49aSRui Paulo if (++try < 10) goto begin; 837be80e49aSRui Paulo device_printf(dev,"%s for key %s failed %d times, giving up\n", 838be80e49aSRui Paulo __func__, key, try); 839be80e49aSRui Paulo } 840be80e49aSRui Paulo 84132a8088fSRui Paulo mtx_unlock_spin(&sc->sc_mtx); 84232a8088fSRui Paulo 84332a8088fSRui Paulo return (error); 84432a8088fSRui Paulo } 84532a8088fSRui Paulo 8461269f4d4SRui Paulo #ifdef DEBUG 8471269f4d4SRui Paulo static int 8481269f4d4SRui Paulo asmc_key_dump(device_t dev, int number) 8491269f4d4SRui Paulo { 8501269f4d4SRui Paulo struct asmc_softc *sc = device_get_softc(dev); 8511269f4d4SRui Paulo char key[5] = { 0 }; 8521269f4d4SRui Paulo char type[7] = { 0 }; 8531269f4d4SRui Paulo uint8_t index[4]; 8541269f4d4SRui Paulo uint8_t v[32]; 8551269f4d4SRui Paulo uint8_t maxlen; 8561269f4d4SRui Paulo int i, error = 1, try = 0; 8571269f4d4SRui Paulo 8581269f4d4SRui Paulo mtx_lock_spin(&sc->sc_mtx); 8591269f4d4SRui Paulo 8601269f4d4SRui Paulo index[0] = (number >> 24) & 0xff; 8611269f4d4SRui Paulo index[1] = (number >> 16) & 0xff; 8621269f4d4SRui Paulo index[2] = (number >> 8) & 0xff; 8631269f4d4SRui Paulo index[3] = (number) & 0xff; 8641269f4d4SRui Paulo 8651269f4d4SRui Paulo begin: 8661269f4d4SRui Paulo if (asmc_command(dev, 0x12)) 8671269f4d4SRui Paulo goto out; 8681269f4d4SRui Paulo 8691269f4d4SRui Paulo for (i = 0; i < 4; i++) { 8701269f4d4SRui Paulo ASMC_DATAPORT_WRITE(sc, index[i]); 8711269f4d4SRui Paulo if (asmc_wait(dev, 0x04)) 8721269f4d4SRui Paulo goto out; 8731269f4d4SRui Paulo } 8741269f4d4SRui Paulo 8751269f4d4SRui Paulo ASMC_DATAPORT_WRITE(sc, 4); 8761269f4d4SRui Paulo 8771269f4d4SRui Paulo for (i = 0; i < 4; i++) { 8781269f4d4SRui Paulo if (asmc_wait(dev, 0x05)) 8791269f4d4SRui Paulo goto out; 8801269f4d4SRui Paulo key[i] = ASMC_DATAPORT_READ(sc); 8811269f4d4SRui Paulo } 8821269f4d4SRui Paulo 8831269f4d4SRui Paulo /* get type */ 8841269f4d4SRui Paulo if (asmc_command(dev, 0x13)) 8851269f4d4SRui Paulo goto out; 8861269f4d4SRui Paulo 8871269f4d4SRui Paulo for (i = 0; i < 4; i++) { 8881269f4d4SRui Paulo ASMC_DATAPORT_WRITE(sc, key[i]); 8891269f4d4SRui Paulo if (asmc_wait(dev, 0x04)) 8901269f4d4SRui Paulo goto out; 8911269f4d4SRui Paulo } 8921269f4d4SRui Paulo 8931269f4d4SRui Paulo ASMC_DATAPORT_WRITE(sc, 6); 8941269f4d4SRui Paulo 8951269f4d4SRui Paulo for (i = 0; i < 6; i++) { 8961269f4d4SRui Paulo if (asmc_wait(dev, 0x05)) 8971269f4d4SRui Paulo goto out; 8981269f4d4SRui Paulo type[i] = ASMC_DATAPORT_READ(sc); 8991269f4d4SRui Paulo } 9001269f4d4SRui Paulo 9011269f4d4SRui Paulo error = 0; 9021269f4d4SRui Paulo out: 9031269f4d4SRui Paulo if (error) { 9041269f4d4SRui Paulo if (++try < 10) goto begin; 9051269f4d4SRui Paulo device_printf(dev,"%s for key %s failed %d times, giving up\n", 9061269f4d4SRui Paulo __func__, key, try); 9071269f4d4SRui Paulo mtx_unlock_spin(&sc->sc_mtx); 9081269f4d4SRui Paulo } 9091269f4d4SRui Paulo else { 9101269f4d4SRui Paulo char buf[1024]; 9111269f4d4SRui Paulo char buf2[8]; 9121269f4d4SRui Paulo mtx_unlock_spin(&sc->sc_mtx); 9131269f4d4SRui Paulo maxlen = type[0]; 9141269f4d4SRui Paulo type[0] = ' '; 9151269f4d4SRui Paulo type[5] = 0; 9161269f4d4SRui Paulo if (maxlen > sizeof(v)) { 917f17bca82SRui Paulo device_printf(dev, 918f17bca82SRui Paulo "WARNING: cropping maxlen from %d to %zu\n", 919f17bca82SRui Paulo maxlen, sizeof(v)); 9201269f4d4SRui Paulo maxlen = sizeof(v); 9211269f4d4SRui Paulo } 9221269f4d4SRui Paulo for (i = 0; i < sizeof(v); i++) { 9231269f4d4SRui Paulo v[i] = 0; 9241269f4d4SRui Paulo } 9251269f4d4SRui Paulo asmc_key_read(dev, key, v, maxlen); 9261269f4d4SRui Paulo snprintf(buf, sizeof(buf), "key %d is: %s, type %s " 9271269f4d4SRui Paulo "(len %d), data", number, key, type, maxlen); 9281269f4d4SRui Paulo for (i = 0; i < maxlen; i++) { 929*108e3076SAdrian Chadd snprintf(buf2, sizeof(buf2), " %02x", v[i]); 9301269f4d4SRui Paulo strlcat(buf, buf2, sizeof(buf)); 9311269f4d4SRui Paulo } 9321269f4d4SRui Paulo strlcat(buf, " \n", sizeof(buf)); 93346c76550SRoman Divacky device_printf(dev, "%s", buf); 9341269f4d4SRui Paulo } 9351269f4d4SRui Paulo 9361269f4d4SRui Paulo return (error); 9371269f4d4SRui Paulo } 9381269f4d4SRui Paulo #endif 9391269f4d4SRui Paulo 94032a8088fSRui Paulo static int 94132a8088fSRui Paulo asmc_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len) 94232a8088fSRui Paulo { 943be80e49aSRui Paulo int i, error = -1, try = 0; 94432a8088fSRui Paulo struct asmc_softc *sc = device_get_softc(dev); 94532a8088fSRui Paulo 94632a8088fSRui Paulo mtx_lock_spin(&sc->sc_mtx); 94732a8088fSRui Paulo 948be80e49aSRui Paulo begin: 9494470f0f3SRui Paulo ASMC_DPRINTF(("cmd port: cmd write\n")); 950be80e49aSRui Paulo if (asmc_command(dev, ASMC_CMDWRITE)) 95132a8088fSRui Paulo goto out; 95232a8088fSRui Paulo 9534470f0f3SRui Paulo ASMC_DPRINTF(("data port: key\n")); 95432a8088fSRui Paulo for (i = 0; i < 4; i++) { 9554470f0f3SRui Paulo ASMC_DATAPORT_WRITE(sc, key[i]); 95632a8088fSRui Paulo if (asmc_wait(dev, 0x04)) 95732a8088fSRui Paulo goto out; 95832a8088fSRui Paulo } 9594470f0f3SRui Paulo ASMC_DPRINTF(("data port: length\n")); 9604470f0f3SRui Paulo ASMC_DATAPORT_WRITE(sc, len); 96132a8088fSRui Paulo 9624470f0f3SRui Paulo ASMC_DPRINTF(("data port: buffer\n")); 96332a8088fSRui Paulo for (i = 0; i < len; i++) { 96432a8088fSRui Paulo if (asmc_wait(dev, 0x04)) 96532a8088fSRui Paulo goto out; 9664470f0f3SRui Paulo ASMC_DATAPORT_WRITE(sc, buf[i]); 96732a8088fSRui Paulo } 96832a8088fSRui Paulo 96932a8088fSRui Paulo error = 0; 97032a8088fSRui Paulo out: 971be80e49aSRui Paulo if (error) { 972be80e49aSRui Paulo if (++try < 10) goto begin; 973be80e49aSRui Paulo device_printf(dev,"%s for key %s failed %d times, giving up\n", 974be80e49aSRui Paulo __func__, key, try); 975be80e49aSRui Paulo } 976be80e49aSRui Paulo 97732a8088fSRui Paulo mtx_unlock_spin(&sc->sc_mtx); 97832a8088fSRui Paulo 97932a8088fSRui Paulo return (error); 98032a8088fSRui Paulo 98132a8088fSRui Paulo } 98232a8088fSRui Paulo 98332a8088fSRui Paulo /* 98432a8088fSRui Paulo * Fan control functions. 98532a8088fSRui Paulo */ 98632a8088fSRui Paulo static int 98732a8088fSRui Paulo asmc_fan_count(device_t dev) 98832a8088fSRui Paulo { 98932a8088fSRui Paulo uint8_t buf[1]; 99032a8088fSRui Paulo 991447666f0SRui Paulo if (asmc_key_read(dev, ASMC_KEY_FANCOUNT, buf, sizeof buf) < 0) 99232a8088fSRui Paulo return (-1); 99332a8088fSRui Paulo 99432a8088fSRui Paulo return (buf[0]); 99532a8088fSRui Paulo } 99632a8088fSRui Paulo 99732a8088fSRui Paulo static int 99832a8088fSRui Paulo asmc_fan_getvalue(device_t dev, const char *key, int fan) 99932a8088fSRui Paulo { 100032a8088fSRui Paulo int speed; 100132a8088fSRui Paulo uint8_t buf[2]; 100232a8088fSRui Paulo char fankey[5]; 100332a8088fSRui Paulo 100432a8088fSRui Paulo snprintf(fankey, sizeof(fankey), key, fan); 1005447666f0SRui Paulo if (asmc_key_read(dev, fankey, buf, sizeof buf) < 0) 100632a8088fSRui Paulo return (-1); 100732a8088fSRui Paulo speed = (buf[0] << 6) | (buf[1] >> 2); 100832a8088fSRui Paulo 100932a8088fSRui Paulo return (speed); 101032a8088fSRui Paulo } 101132a8088fSRui Paulo 1012447666f0SRui Paulo static char* 1013623534d6SUlrich Spörlein asmc_fan_getstring(device_t dev, const char *key, int fan, uint8_t *buf, uint8_t buflen) 1014447666f0SRui Paulo { 1015447666f0SRui Paulo char fankey[5]; 1016447666f0SRui Paulo char* desc; 1017447666f0SRui Paulo 1018447666f0SRui Paulo snprintf(fankey, sizeof(fankey), key, fan); 1019623534d6SUlrich Spörlein if (asmc_key_read(dev, fankey, buf, buflen) < 0) 1020447666f0SRui Paulo return (NULL); 1021447666f0SRui Paulo desc = buf+4; 1022447666f0SRui Paulo 1023447666f0SRui Paulo return (desc); 1024447666f0SRui Paulo } 1025447666f0SRui Paulo 1026447666f0SRui Paulo static int 1027447666f0SRui Paulo asmc_fan_setvalue(device_t dev, const char *key, int fan, int speed) 1028447666f0SRui Paulo { 1029447666f0SRui Paulo uint8_t buf[2]; 1030447666f0SRui Paulo char fankey[5]; 1031447666f0SRui Paulo 1032447666f0SRui Paulo speed *= 4; 1033447666f0SRui Paulo 1034447666f0SRui Paulo buf[0] = speed>>8; 1035447666f0SRui Paulo buf[1] = speed; 1036447666f0SRui Paulo 1037447666f0SRui Paulo snprintf(fankey, sizeof(fankey), key, fan); 1038447666f0SRui Paulo if (asmc_key_write(dev, fankey, buf, sizeof buf) < 0) 1039447666f0SRui Paulo return (-1); 1040447666f0SRui Paulo 1041447666f0SRui Paulo return (0); 1042447666f0SRui Paulo } 1043447666f0SRui Paulo 104432a8088fSRui Paulo static int 104532a8088fSRui Paulo asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS) 104632a8088fSRui Paulo { 104732a8088fSRui Paulo device_t dev = (device_t) arg1; 104832a8088fSRui Paulo int fan = arg2; 104932a8088fSRui Paulo int error; 105032a8088fSRui Paulo int32_t v; 105132a8088fSRui Paulo 105232a8088fSRui Paulo v = asmc_fan_getvalue(dev, ASMC_KEY_FANSPEED, fan); 105332a8088fSRui Paulo error = sysctl_handle_int(oidp, &v, 0, req); 105432a8088fSRui Paulo 105532a8088fSRui Paulo return (error); 105632a8088fSRui Paulo } 105732a8088fSRui Paulo 105832a8088fSRui Paulo static int 1059447666f0SRui Paulo asmc_mb_sysctl_fanid(SYSCTL_HANDLER_ARGS) 1060447666f0SRui Paulo { 1061623534d6SUlrich Spörlein uint8_t buf[16]; 1062447666f0SRui Paulo device_t dev = (device_t) arg1; 1063447666f0SRui Paulo int fan = arg2; 1064447666f0SRui Paulo int error = true; 1065447666f0SRui Paulo char* desc; 1066447666f0SRui Paulo 1067623534d6SUlrich Spörlein desc = asmc_fan_getstring(dev, ASMC_KEY_FANID, fan, buf, sizeof(buf)); 1068447666f0SRui Paulo 1069447666f0SRui Paulo if (desc != NULL) 1070447666f0SRui Paulo error = sysctl_handle_string(oidp, desc, 0, req); 1071447666f0SRui Paulo 1072447666f0SRui Paulo return (error); 1073447666f0SRui Paulo } 1074447666f0SRui Paulo 1075447666f0SRui Paulo static int 107632a8088fSRui Paulo asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS) 107732a8088fSRui Paulo { 107832a8088fSRui Paulo device_t dev = (device_t) arg1; 107932a8088fSRui Paulo int fan = arg2; 108032a8088fSRui Paulo int error; 108132a8088fSRui Paulo int32_t v; 108232a8088fSRui Paulo 108332a8088fSRui Paulo v = asmc_fan_getvalue(dev, ASMC_KEY_FANSAFESPEED, fan); 108432a8088fSRui Paulo error = sysctl_handle_int(oidp, &v, 0, req); 108532a8088fSRui Paulo 108632a8088fSRui Paulo return (error); 108732a8088fSRui Paulo } 108832a8088fSRui Paulo 108932a8088fSRui Paulo 109032a8088fSRui Paulo static int 109132a8088fSRui Paulo asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS) 109232a8088fSRui Paulo { 109332a8088fSRui Paulo device_t dev = (device_t) arg1; 109432a8088fSRui Paulo int fan = arg2; 109532a8088fSRui Paulo int error; 109632a8088fSRui Paulo int32_t v; 109732a8088fSRui Paulo 109832a8088fSRui Paulo v = asmc_fan_getvalue(dev, ASMC_KEY_FANMINSPEED, fan); 109932a8088fSRui Paulo error = sysctl_handle_int(oidp, &v, 0, req); 110032a8088fSRui Paulo 1101447666f0SRui Paulo if (error == 0 && req->newptr != NULL) { 11020e1152fcSHans Petter Selasky unsigned int newspeed = v; 1103447666f0SRui Paulo asmc_fan_setvalue(dev, ASMC_KEY_FANMINSPEED, fan, newspeed); 1104447666f0SRui Paulo } 1105447666f0SRui Paulo 110632a8088fSRui Paulo return (error); 110732a8088fSRui Paulo } 110832a8088fSRui Paulo 110932a8088fSRui Paulo static int 111032a8088fSRui Paulo asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS) 111132a8088fSRui Paulo { 111232a8088fSRui Paulo device_t dev = (device_t) arg1; 111332a8088fSRui Paulo int fan = arg2; 111432a8088fSRui Paulo int error; 111532a8088fSRui Paulo int32_t v; 111632a8088fSRui Paulo 111732a8088fSRui Paulo v = asmc_fan_getvalue(dev, ASMC_KEY_FANMAXSPEED, fan); 111832a8088fSRui Paulo error = sysctl_handle_int(oidp, &v, 0, req); 111932a8088fSRui Paulo 1120447666f0SRui Paulo if (error == 0 && req->newptr != NULL) { 11210e1152fcSHans Petter Selasky unsigned int newspeed = v; 1122447666f0SRui Paulo asmc_fan_setvalue(dev, ASMC_KEY_FANMAXSPEED, fan, newspeed); 1123447666f0SRui Paulo } 1124447666f0SRui Paulo 112532a8088fSRui Paulo return (error); 112632a8088fSRui Paulo } 112732a8088fSRui Paulo 112832a8088fSRui Paulo static int 112932a8088fSRui Paulo asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS) 113032a8088fSRui Paulo { 113132a8088fSRui Paulo device_t dev = (device_t) arg1; 113232a8088fSRui Paulo int fan = arg2; 113332a8088fSRui Paulo int error; 113432a8088fSRui Paulo int32_t v; 113532a8088fSRui Paulo 113632a8088fSRui Paulo v = asmc_fan_getvalue(dev, ASMC_KEY_FANTARGETSPEED, fan); 113732a8088fSRui Paulo error = sysctl_handle_int(oidp, &v, 0, req); 113832a8088fSRui Paulo 1139447666f0SRui Paulo if (error == 0 && req->newptr != NULL) { 11400e1152fcSHans Petter Selasky unsigned int newspeed = v; 1141447666f0SRui Paulo asmc_fan_setvalue(dev, ASMC_KEY_FANTARGETSPEED, fan, newspeed); 1142447666f0SRui Paulo } 1143447666f0SRui Paulo 114432a8088fSRui Paulo return (error); 114532a8088fSRui Paulo } 114632a8088fSRui Paulo 114732a8088fSRui Paulo /* 114832a8088fSRui Paulo * Temperature functions. 114932a8088fSRui Paulo */ 115032a8088fSRui Paulo static int 115132a8088fSRui Paulo asmc_temp_getvalue(device_t dev, const char *key) 115232a8088fSRui Paulo { 115332a8088fSRui Paulo uint8_t buf[2]; 115432a8088fSRui Paulo 115532a8088fSRui Paulo /* 115632a8088fSRui Paulo * Check for invalid temperatures. 115732a8088fSRui Paulo */ 1158447666f0SRui Paulo if (asmc_key_read(dev, key, buf, sizeof buf) < 0) 115932a8088fSRui Paulo return (-1); 116032a8088fSRui Paulo 116132a8088fSRui Paulo return (buf[0]); 116232a8088fSRui Paulo } 116332a8088fSRui Paulo 116432a8088fSRui Paulo static int 116532a8088fSRui Paulo asmc_temp_sysctl(SYSCTL_HANDLER_ARGS) 116632a8088fSRui Paulo { 116732a8088fSRui Paulo device_t dev = (device_t) arg1; 116832a8088fSRui Paulo struct asmc_softc *sc = device_get_softc(dev); 116932a8088fSRui Paulo int error, val; 117032a8088fSRui Paulo 117132a8088fSRui Paulo val = asmc_temp_getvalue(dev, sc->sc_model->smc_temps[arg2]); 117232a8088fSRui Paulo error = sysctl_handle_int(oidp, &val, 0, req); 117332a8088fSRui Paulo 117432a8088fSRui Paulo return (error); 117532a8088fSRui Paulo } 117632a8088fSRui Paulo 117732a8088fSRui Paulo /* 117832a8088fSRui Paulo * Sudden Motion Sensor functions. 117932a8088fSRui Paulo */ 118032a8088fSRui Paulo static int 118132a8088fSRui Paulo asmc_sms_read(device_t dev, const char *key, int16_t *val) 118232a8088fSRui Paulo { 118332a8088fSRui Paulo uint8_t buf[2]; 118432a8088fSRui Paulo int error; 118532a8088fSRui Paulo 118632a8088fSRui Paulo /* no need to do locking here as asmc_key_read() already does it */ 118732a8088fSRui Paulo switch (key[3]) { 118832a8088fSRui Paulo case 'X': 118932a8088fSRui Paulo case 'Y': 119032a8088fSRui Paulo case 'Z': 1191447666f0SRui Paulo error = asmc_key_read(dev, key, buf, sizeof buf); 119232a8088fSRui Paulo break; 119332a8088fSRui Paulo default: 119432a8088fSRui Paulo device_printf(dev, "%s called with invalid argument %s\n", 119532a8088fSRui Paulo __func__, key); 119632a8088fSRui Paulo error = 1; 119732a8088fSRui Paulo goto out; 119832a8088fSRui Paulo } 119932a8088fSRui Paulo *val = ((int16_t)buf[0] << 8) | buf[1]; 120032a8088fSRui Paulo out: 120132a8088fSRui Paulo return (error); 120232a8088fSRui Paulo } 120332a8088fSRui Paulo 120432a8088fSRui Paulo static void 120532a8088fSRui Paulo asmc_sms_calibrate(device_t dev) 120632a8088fSRui Paulo { 120732a8088fSRui Paulo struct asmc_softc *sc = device_get_softc(dev); 120832a8088fSRui Paulo 120932a8088fSRui Paulo asmc_sms_read(dev, ASMC_KEY_SMS_X, &sc->sms_rest_x); 121032a8088fSRui Paulo asmc_sms_read(dev, ASMC_KEY_SMS_Y, &sc->sms_rest_y); 121132a8088fSRui Paulo asmc_sms_read(dev, ASMC_KEY_SMS_Z, &sc->sms_rest_z); 121232a8088fSRui Paulo } 121332a8088fSRui Paulo 121432a8088fSRui Paulo static int 121532a8088fSRui Paulo asmc_sms_intrfast(void *arg) 121632a8088fSRui Paulo { 121732a8088fSRui Paulo uint8_t type; 121832a8088fSRui Paulo device_t dev = (device_t) arg; 121932a8088fSRui Paulo struct asmc_softc *sc = device_get_softc(dev); 12201269f4d4SRui Paulo if (!sc->sc_sms_intr_works) 12211269f4d4SRui Paulo return (FILTER_HANDLED); 122232a8088fSRui Paulo 122332a8088fSRui Paulo mtx_lock_spin(&sc->sc_mtx); 12244470f0f3SRui Paulo type = ASMC_INTPORT_READ(sc); 122532a8088fSRui Paulo mtx_unlock_spin(&sc->sc_mtx); 122632a8088fSRui Paulo 122732a8088fSRui Paulo sc->sc_sms_intrtype = type; 122832a8088fSRui Paulo asmc_sms_printintr(dev, type); 122932a8088fSRui Paulo 123032a8088fSRui Paulo #ifdef INTR_FILTER 123132a8088fSRui Paulo return (FILTER_SCHEDULE_THREAD | FILTER_HANDLED); 123232a8088fSRui Paulo #else 123332a8088fSRui Paulo taskqueue_enqueue(sc->sc_sms_tq, &sc->sc_sms_task); 123432a8088fSRui Paulo #endif 123532a8088fSRui Paulo return (FILTER_HANDLED); 123632a8088fSRui Paulo } 123732a8088fSRui Paulo 123832a8088fSRui Paulo #ifdef INTR_FILTER 123932a8088fSRui Paulo static void 124032a8088fSRui Paulo asmc_sms_handler(void *arg) 124132a8088fSRui Paulo { 124232a8088fSRui Paulo struct asmc_softc *sc = device_get_softc(arg); 124332a8088fSRui Paulo 124432a8088fSRui Paulo asmc_sms_task(sc, 0); 124532a8088fSRui Paulo } 124632a8088fSRui Paulo #endif 124732a8088fSRui Paulo 124832a8088fSRui Paulo 124932a8088fSRui Paulo static void 125032a8088fSRui Paulo asmc_sms_printintr(device_t dev, uint8_t type) 125132a8088fSRui Paulo { 125232a8088fSRui Paulo 125332a8088fSRui Paulo switch (type) { 125432a8088fSRui Paulo case ASMC_SMS_INTFF: 125532a8088fSRui Paulo device_printf(dev, "WARNING: possible free fall!\n"); 125632a8088fSRui Paulo break; 125732a8088fSRui Paulo case ASMC_SMS_INTHA: 125832a8088fSRui Paulo device_printf(dev, "WARNING: high acceleration detected!\n"); 125932a8088fSRui Paulo break; 126032a8088fSRui Paulo case ASMC_SMS_INTSH: 126132a8088fSRui Paulo device_printf(dev, "WARNING: possible shock!\n"); 126232a8088fSRui Paulo break; 126332a8088fSRui Paulo default: 126432a8088fSRui Paulo device_printf(dev, "%s unknown interrupt\n", __func__); 126532a8088fSRui Paulo } 126632a8088fSRui Paulo } 126732a8088fSRui Paulo 126832a8088fSRui Paulo static void 126932a8088fSRui Paulo asmc_sms_task(void *arg, int pending) 127032a8088fSRui Paulo { 127132a8088fSRui Paulo struct asmc_softc *sc = (struct asmc_softc *)arg; 127232a8088fSRui Paulo char notify[16]; 127332a8088fSRui Paulo int type; 127432a8088fSRui Paulo 127532a8088fSRui Paulo switch (sc->sc_sms_intrtype) { 127632a8088fSRui Paulo case ASMC_SMS_INTFF: 127732a8088fSRui Paulo type = 2; 127832a8088fSRui Paulo break; 127932a8088fSRui Paulo case ASMC_SMS_INTHA: 128032a8088fSRui Paulo type = 1; 128132a8088fSRui Paulo break; 128232a8088fSRui Paulo case ASMC_SMS_INTSH: 128332a8088fSRui Paulo type = 0; 128432a8088fSRui Paulo break; 128532a8088fSRui Paulo default: 128632a8088fSRui Paulo type = 255; 128732a8088fSRui Paulo } 128832a8088fSRui Paulo 128932a8088fSRui Paulo snprintf(notify, sizeof(notify), " notify=0x%x", type); 12904470f0f3SRui Paulo devctl_notify("ACPI", "asmc", "SMS", notify); 129132a8088fSRui Paulo } 129232a8088fSRui Paulo 129332a8088fSRui Paulo static int 129432a8088fSRui Paulo asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS) 129532a8088fSRui Paulo { 129632a8088fSRui Paulo device_t dev = (device_t) arg1; 129732a8088fSRui Paulo int error; 129832a8088fSRui Paulo int16_t val; 129932a8088fSRui Paulo int32_t v; 130032a8088fSRui Paulo 130132a8088fSRui Paulo asmc_sms_read(dev, ASMC_KEY_SMS_X, &val); 130232a8088fSRui Paulo v = (int32_t) val; 130332a8088fSRui Paulo error = sysctl_handle_int(oidp, &v, 0, req); 130432a8088fSRui Paulo 130532a8088fSRui Paulo return (error); 130632a8088fSRui Paulo } 130732a8088fSRui Paulo 130832a8088fSRui Paulo static int 130932a8088fSRui Paulo asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS) 131032a8088fSRui Paulo { 131132a8088fSRui Paulo device_t dev = (device_t) arg1; 131232a8088fSRui Paulo int error; 131332a8088fSRui Paulo int16_t val; 131432a8088fSRui Paulo int32_t v; 131532a8088fSRui Paulo 131632a8088fSRui Paulo asmc_sms_read(dev, ASMC_KEY_SMS_Y, &val); 131732a8088fSRui Paulo v = (int32_t) val; 131832a8088fSRui Paulo error = sysctl_handle_int(oidp, &v, 0, req); 131932a8088fSRui Paulo 132032a8088fSRui Paulo return (error); 132132a8088fSRui Paulo } 132232a8088fSRui Paulo 132332a8088fSRui Paulo static int 132432a8088fSRui Paulo asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS) 132532a8088fSRui Paulo { 132632a8088fSRui Paulo device_t dev = (device_t) arg1; 132732a8088fSRui Paulo int error; 132832a8088fSRui Paulo int16_t val; 132932a8088fSRui Paulo int32_t v; 133032a8088fSRui Paulo 133132a8088fSRui Paulo asmc_sms_read(dev, ASMC_KEY_SMS_Z, &val); 133232a8088fSRui Paulo v = (int32_t) val; 13330e1152fcSHans Petter Selasky error = sysctl_handle_int(oidp, &v, 0, req); 133432a8088fSRui Paulo 133532a8088fSRui Paulo return (error); 133632a8088fSRui Paulo } 133732a8088fSRui Paulo 133832a8088fSRui Paulo static int 133932a8088fSRui Paulo asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS) 134032a8088fSRui Paulo { 134132a8088fSRui Paulo device_t dev = (device_t) arg1; 134232a8088fSRui Paulo uint8_t buf[6]; 134332a8088fSRui Paulo int error; 134432a8088fSRui Paulo int32_t v; 134532a8088fSRui Paulo 1346447666f0SRui Paulo asmc_key_read(dev, ASMC_KEY_LIGHTLEFT, buf, sizeof buf); 134732a8088fSRui Paulo v = buf[2]; 13480e1152fcSHans Petter Selasky error = sysctl_handle_int(oidp, &v, 0, req); 134932a8088fSRui Paulo 135032a8088fSRui Paulo return (error); 135132a8088fSRui Paulo } 135232a8088fSRui Paulo 135332a8088fSRui Paulo static int 135432a8088fSRui Paulo asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS) 135532a8088fSRui Paulo { 135632a8088fSRui Paulo device_t dev = (device_t) arg1; 135732a8088fSRui Paulo uint8_t buf[6]; 135832a8088fSRui Paulo int error; 135932a8088fSRui Paulo int32_t v; 136032a8088fSRui Paulo 1361447666f0SRui Paulo asmc_key_read(dev, ASMC_KEY_LIGHTRIGHT, buf, sizeof buf); 136232a8088fSRui Paulo v = buf[2]; 13630e1152fcSHans Petter Selasky error = sysctl_handle_int(oidp, &v, 0, req); 1364be80e49aSRui Paulo 1365be80e49aSRui Paulo return (error); 1366be80e49aSRui Paulo } 1367be80e49aSRui Paulo 1368be80e49aSRui Paulo static int 1369be80e49aSRui Paulo asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS) 1370be80e49aSRui Paulo { 1371be80e49aSRui Paulo device_t dev = (device_t) arg1; 1372be80e49aSRui Paulo uint8_t buf[2]; 1373be80e49aSRui Paulo int error; 13740e1152fcSHans Petter Selasky int v; 1375be80e49aSRui Paulo 1376*108e3076SAdrian Chadd v = light_control; 13770e1152fcSHans Petter Selasky error = sysctl_handle_int(oidp, &v, 0, req); 13780e1152fcSHans Petter Selasky 13790e1152fcSHans Petter Selasky if (error == 0 && req->newptr != NULL) { 13800e1152fcSHans Petter Selasky if (v < 0 || v > 255) 13810e1152fcSHans Petter Selasky return (EINVAL); 1382*108e3076SAdrian Chadd light_control = v; 1383*108e3076SAdrian Chadd buf[0] = light_control; 138432a8088fSRui Paulo buf[1] = 0x00; 1385447666f0SRui Paulo asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, sizeof buf); 138632a8088fSRui Paulo } 138732a8088fSRui Paulo return (error); 138832a8088fSRui Paulo } 1389