diff options
Diffstat (limited to 'sys/dev/asmc')
| -rw-r--r-- | sys/dev/asmc/asmc.c | 2364 | ||||
| -rw-r--r-- | sys/dev/asmc/asmcmmio.c | 402 | ||||
| -rw-r--r-- | sys/dev/asmc/asmcmmio.h | 56 | ||||
| -rw-r--r-- | sys/dev/asmc/asmcvar.h | 919 |
4 files changed, 2326 insertions, 1415 deletions
diff --git a/sys/dev/asmc/asmc.c b/sys/dev/asmc/asmc.c index d99c1d56e67c..d55c585419fb 100644 --- a/sys/dev/asmc/asmc.c +++ b/sys/dev/asmc/asmc.c @@ -34,6 +34,8 @@ * Inspired by the Linux applesmc driver. */ +#include "opt_asmc.h" + #include <sys/param.h> #include <sys/bus.h> #include <sys/conf.h> @@ -43,17 +45,23 @@ #include <sys/malloc.h> #include <sys/module.h> #include <sys/mutex.h> +#include <sys/sbuf.h> #include <sys/sysctl.h> #include <sys/systm.h> #include <sys/taskqueue.h> #include <sys/rman.h> #include <machine/resource.h> +#include <netinet/in.h> #include <contrib/dev/acpica/include/acpi.h> #include <dev/acpica/acpivar.h> #include <dev/asmc/asmcvar.h> +#include <dev/asmc/asmcmmio.h> + +#include <dev/backlight/backlight.h> +#include "backlight_if.h" /* * Device interface. @@ -64,6 +72,15 @@ static int asmc_detach(device_t dev); static int asmc_resume(device_t dev); /* + * Backlight interface. + */ +static int asmc_backlight_update_status(device_t dev, + struct backlight_props *props); +static int asmc_backlight_get_status(device_t dev, + struct backlight_props *props); +static int asmc_backlight_get_info(device_t dev, struct backlight_info *info); + +/* * SMC functions. */ static int asmc_init(device_t dev); @@ -83,13 +100,15 @@ static void asmc_sms_calibrate(device_t dev); static int asmc_sms_intrfast(void *arg); static void asmc_sms_printintr(device_t dev, uint8_t); static void asmc_sms_task(void *arg, int pending); -#ifdef DEBUG +static void asmc_sms_init(device_t dev); +static void asmc_detect_capabilities(device_t dev); +#ifdef ASMC_DEBUG void asmc_dumpall(device_t); static int asmc_key_dump(device_t, int); #endif /* - * Model functions. + * Sysctl handlers. */ static int asmc_mb_sysctl_fanid(SYSCTL_HANDLER_ARGS); static int asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS); @@ -97,6 +116,7 @@ static int asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS); static int asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS); static int asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS); static int asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS); +static int asmc_mb_sysctl_fanmanual(SYSCTL_HANDLER_ARGS); static int asmc_temp_sysctl(SYSCTL_HANDLER_ARGS); static int asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS); static int asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS); @@ -105,392 +125,245 @@ static int asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS); static int asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS); static int asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS); static int asmc_mbp_sysctl_light_left_10byte(SYSCTL_HANDLER_ARGS); +static int asmc_aupo_sysctl(SYSCTL_HANDLER_ARGS); -struct asmc_model { - const char *smc_model; /* smbios.system.product env var. */ - const char *smc_desc; /* driver description */ - - /* Helper functions */ - int (*smc_sms_x)(SYSCTL_HANDLER_ARGS); - int (*smc_sms_y)(SYSCTL_HANDLER_ARGS); - int (*smc_sms_z)(SYSCTL_HANDLER_ARGS); - int (*smc_fan_id)(SYSCTL_HANDLER_ARGS); - int (*smc_fan_speed)(SYSCTL_HANDLER_ARGS); - int (*smc_fan_safespeed)(SYSCTL_HANDLER_ARGS); - int (*smc_fan_minspeed)(SYSCTL_HANDLER_ARGS); - int (*smc_fan_maxspeed)(SYSCTL_HANDLER_ARGS); - int (*smc_fan_targetspeed)(SYSCTL_HANDLER_ARGS); - int (*smc_light_left)(SYSCTL_HANDLER_ARGS); - int (*smc_light_right)(SYSCTL_HANDLER_ARGS); - int (*smc_light_control)(SYSCTL_HANDLER_ARGS); - - const char *smc_temps[ASMC_TEMP_MAX]; - const char *smc_tempnames[ASMC_TEMP_MAX]; - const char *smc_tempdescs[ASMC_TEMP_MAX]; -}; - -static const struct asmc_model *asmc_match(device_t dev); - -#define ASMC_SMS_FUNCS asmc_mb_sysctl_sms_x, asmc_mb_sysctl_sms_y, \ - asmc_mb_sysctl_sms_z - -#define ASMC_SMS_FUNCS_DISABLED NULL,NULL,NULL - -#define ASMC_FAN_FUNCS asmc_mb_sysctl_fanid, asmc_mb_sysctl_fanspeed, asmc_mb_sysctl_fansafespeed, \ - asmc_mb_sysctl_fanminspeed, \ - asmc_mb_sysctl_fanmaxspeed, \ - asmc_mb_sysctl_fantargetspeed - -#define ASMC_FAN_FUNCS2 asmc_mb_sysctl_fanid, asmc_mb_sysctl_fanspeed, NULL, \ - asmc_mb_sysctl_fanminspeed, \ - asmc_mb_sysctl_fanmaxspeed, \ - asmc_mb_sysctl_fantargetspeed - -#define ASMC_LIGHT_FUNCS asmc_mbp_sysctl_light_left, \ - asmc_mbp_sysctl_light_right, \ - asmc_mbp_sysctl_light_control - -#define ASMC_LIGHT_FUNCS_10BYTE \ - asmc_mbp_sysctl_light_left_10byte, \ - NULL, \ - asmc_mbp_sysctl_light_control - -#define ASMC_LIGHT_FUNCS_DISABLED NULL, NULL, NULL - -static const struct asmc_model asmc_models[] = { - { - "MacBook1,1", "Apple SMC MacBook Core Duo", - ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL, - ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS - }, - - { - "MacBook2,1", "Apple SMC MacBook Core 2 Duo", - ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL, - ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS - }, - - { - "MacBook3,1", "Apple SMC MacBook Core 2 Duo", - ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL, - ASMC_MB31_TEMPS, ASMC_MB31_TEMPNAMES, ASMC_MB31_TEMPDESCS - }, - - { - "MacBook7,1", "Apple SMC MacBook Core 2 Duo (mid 2010)", - ASMC_SMS_FUNCS, ASMC_FAN_FUNCS2, ASMC_LIGHT_FUNCS_DISABLED, - ASMC_MB71_TEMPS, ASMC_MB71_TEMPNAMES, ASMC_MB71_TEMPDESCS - }, - - { - "MacBookPro1,1", "Apple SMC MacBook Pro Core Duo (15-inch)", - ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, - ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS - }, - - { - "MacBookPro1,2", "Apple SMC MacBook Pro Core Duo (17-inch)", - ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, - ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS - }, - - { - "MacBookPro2,1", "Apple SMC MacBook Pro Core 2 Duo (17-inch)", - ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, - ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS - }, - - { - "MacBookPro2,2", "Apple SMC MacBook Pro Core 2 Duo (15-inch)", - ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, - ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS - }, - - { - "MacBookPro3,1", "Apple SMC MacBook Pro Core 2 Duo (15-inch LED)", - ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, - ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS - }, - - { - "MacBookPro3,2", "Apple SMC MacBook Pro Core 2 Duo (17-inch HD)", - ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, - ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS - }, - - { - "MacBookPro4,1", "Apple SMC MacBook Pro Core 2 Duo (Penryn)", - ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, - ASMC_MBP4_TEMPS, ASMC_MBP4_TEMPNAMES, ASMC_MBP4_TEMPDESCS - }, - - { - "MacBookPro5,1", "Apple SMC MacBook Pro Core 2 Duo (2008/2009)", - ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, - ASMC_MBP51_TEMPS, ASMC_MBP51_TEMPNAMES, ASMC_MBP51_TEMPDESCS - }, - - { - "MacBookPro5,5", "Apple SMC MacBook Pro Core 2 Duo (Mid 2009)", - ASMC_SMS_FUNCS, ASMC_FAN_FUNCS2, ASMC_LIGHT_FUNCS, - ASMC_MBP55_TEMPS, ASMC_MBP55_TEMPNAMES, ASMC_MBP55_TEMPDESCS - }, - - { - "MacBookPro6,2", "Apple SMC MacBook Pro (Mid 2010, 15-inch)", - ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, - ASMC_MBP62_TEMPS, ASMC_MBP62_TEMPNAMES, ASMC_MBP62_TEMPDESCS - }, +static int asmc_key_getinfo(device_t, const char *, uint8_t *, char *); - { - "MacBookPro8,1", "Apple SMC MacBook Pro (early 2011, 13-inch)", - ASMC_SMS_FUNCS_DISABLED, ASMC_FAN_FUNCS2, ASMC_LIGHT_FUNCS, - ASMC_MBP81_TEMPS, ASMC_MBP81_TEMPNAMES, ASMC_MBP81_TEMPDESCS - }, - - { - "MacBookPro8,2", "Apple SMC MacBook Pro (early 2011)", - ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, - ASMC_MBP82_TEMPS, ASMC_MBP82_TEMPNAMES, ASMC_MBP82_TEMPDESCS - }, +/* System state / board identity sysctls */ +static int asmc_cause_sysctl(SYSCTL_HANDLER_ARGS); +static int asmc_msal_sysctl(SYSCTL_HANDLER_ARGS); +static int asmc_clkt_sysctl(SYSCTL_HANDLER_ARGS); +static int asmc_msps_sysctl(SYSCTL_HANDLER_ARGS); +static int asmc_rplt_sysctl(SYSCTL_HANDLER_ARGS); +static int asmc_rgen_sysctl(SYSCTL_HANDLER_ARGS); - { - "MacBookPro9,1", "Apple SMC MacBook Pro (mid 2012, 15-inch)", - ASMC_SMS_FUNCS_DISABLED, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, - ASMC_MBP91_TEMPS, ASMC_MBP91_TEMPNAMES, ASMC_MBP91_TEMPDESCS - }, - - { - "MacBookPro9,2", "Apple SMC MacBook Pro (mid 2012, 13-inch)", - ASMC_SMS_FUNCS_DISABLED, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, - ASMC_MBP92_TEMPS, ASMC_MBP92_TEMPNAMES, ASMC_MBP92_TEMPDESCS - }, - - { - "MacBookPro11,2", "Apple SMC MacBook Pro Retina Core i7 (2013/2014)", - ASMC_SMS_FUNCS_DISABLED, ASMC_FAN_FUNCS2, ASMC_LIGHT_FUNCS, - ASMC_MBP112_TEMPS, ASMC_MBP112_TEMPNAMES, ASMC_MBP112_TEMPDESCS - }, - - { - "MacBookPro11,3", "Apple SMC MacBook Pro Retina Core i7 (2013/2014)", - ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, - ASMC_MBP113_TEMPS, ASMC_MBP113_TEMPNAMES, ASMC_MBP113_TEMPDESCS - }, - - { - "MacBookPro11,4", "Apple SMC MacBook Pro Retina Core i7 (mid 2015, 15-inch)", - ASMC_SMS_FUNCS_DISABLED, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS, - ASMC_MBP114_TEMPS, ASMC_MBP114_TEMPNAMES, ASMC_MBP114_TEMPDESCS - }, - - /* The Mac Mini has no SMS */ - { - "Macmini1,1", "Apple SMC Mac Mini", - NULL, NULL, NULL, - ASMC_FAN_FUNCS, - NULL, NULL, NULL, - ASMC_MM_TEMPS, ASMC_MM_TEMPNAMES, ASMC_MM_TEMPDESCS - }, - - /* The Mac Mini 2,1 has no SMS */ - { - "Macmini2,1", "Apple SMC Mac Mini 2,1", - ASMC_SMS_FUNCS_DISABLED, - ASMC_FAN_FUNCS, - ASMC_LIGHT_FUNCS_DISABLED, - ASMC_MM21_TEMPS, ASMC_MM21_TEMPNAMES, ASMC_MM21_TEMPDESCS - }, - - /* The Mac Mini 3,1 has no SMS */ - { - "Macmini3,1", "Apple SMC Mac Mini 3,1", - NULL, NULL, NULL, - ASMC_FAN_FUNCS, - NULL, NULL, NULL, - ASMC_MM31_TEMPS, ASMC_MM31_TEMPNAMES, ASMC_MM31_TEMPDESCS - }, - - /* The Mac Mini 4,1 (Mid-2010) has no SMS */ - { - "Macmini4,1", "Apple SMC Mac mini 4,1 (Mid-2010)", - ASMC_SMS_FUNCS_DISABLED, - ASMC_FAN_FUNCS, - ASMC_LIGHT_FUNCS_DISABLED, - ASMC_MM41_TEMPS, ASMC_MM41_TEMPNAMES, ASMC_MM41_TEMPDESCS - }, - - /* The Mac Mini 5,1 has no SMS */ - /* - same sensors as Mac Mini 5,2 */ - { - "Macmini5,1", "Apple SMC Mac Mini 5,1", - NULL, NULL, NULL, - ASMC_FAN_FUNCS2, - NULL, NULL, NULL, - ASMC_MM52_TEMPS, ASMC_MM52_TEMPNAMES, ASMC_MM52_TEMPDESCS - }, - - /* The Mac Mini 5,2 has no SMS */ - { - "Macmini5,2", "Apple SMC Mac Mini 5,2", - NULL, NULL, NULL, - ASMC_FAN_FUNCS2, - NULL, NULL, NULL, - ASMC_MM52_TEMPS, ASMC_MM52_TEMPNAMES, ASMC_MM52_TEMPDESCS - }, - - /* The Mac Mini 5,3 has no SMS */ - /* - same sensors as Mac Mini 5,2 */ - { - "Macmini5,3", "Apple SMC Mac Mini 5,3", - NULL, NULL, NULL, - ASMC_FAN_FUNCS2, - NULL, NULL, NULL, - ASMC_MM52_TEMPS, ASMC_MM52_TEMPNAMES, ASMC_MM52_TEMPDESCS - }, - - /* The Mac Mini 6,1 has no SMS */ - { - "Macmini6,1", "Apple SMC Mac Mini 6,1", - NULL, NULL, NULL, - ASMC_FAN_FUNCS2, - NULL, NULL, NULL, - ASMC_MM61_TEMPS, ASMC_MM61_TEMPNAMES, ASMC_MM61_TEMPDESCS - }, - - /* The Mac Mini 6,2 has no SMS */ - { - "Macmini6,2", "Apple SMC Mac Mini 6,2", - NULL, NULL, NULL, - ASMC_FAN_FUNCS2, - NULL, NULL, NULL, - ASMC_MM62_TEMPS, ASMC_MM62_TEMPNAMES, ASMC_MM62_TEMPDESCS - }, - - /* The Mac Mini 7,1 has no SMS */ - { - "Macmini7,1", "Apple SMC Mac Mini 7,1", - NULL, NULL, NULL, - ASMC_FAN_FUNCS2, - NULL, NULL, NULL, - ASMC_MM71_TEMPS, ASMC_MM71_TEMPNAMES, ASMC_MM71_TEMPDESCS - }, - - /* Idem for the Mac Pro "Quad Core" (original) */ - { - "MacPro1,1", "Apple SMC Mac Pro (Quad Core)", - NULL, NULL, NULL, - ASMC_FAN_FUNCS, - NULL, NULL, NULL, - ASMC_MP1_TEMPS, ASMC_MP1_TEMPNAMES, ASMC_MP1_TEMPDESCS - }, - - /* Idem for the Mac Pro (8-core) */ - { - "MacPro2", "Apple SMC Mac Pro (8-core)", - NULL, NULL, NULL, - ASMC_FAN_FUNCS, - NULL, NULL, NULL, - ASMC_MP2_TEMPS, ASMC_MP2_TEMPNAMES, ASMC_MP2_TEMPDESCS - }, - - /* Idem for the MacPro 2010*/ - { - "MacPro5,1", "Apple SMC MacPro (2010)", - NULL, NULL, NULL, - ASMC_FAN_FUNCS, - NULL, NULL, NULL, - ASMC_MP5_TEMPS, ASMC_MP5_TEMPNAMES, ASMC_MP5_TEMPDESCS - }, - - /* Idem for the Mac Pro 2013 (cylinder) */ - { - "MacPro6,1", "Apple SMC Mac Pro (2013)", - ASMC_SMS_FUNCS_DISABLED, - ASMC_FAN_FUNCS2, - ASMC_LIGHT_FUNCS_DISABLED, - ASMC_MP6_TEMPS, ASMC_MP6_TEMPNAMES, ASMC_MP6_TEMPDESCS - }, - - { - "MacBookAir1,1", "Apple SMC MacBook Air", - ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL, - ASMC_MBA_TEMPS, ASMC_MBA_TEMPNAMES, ASMC_MBA_TEMPDESCS - }, - - { - "MacBookAir3,1", "Apple SMC MacBook Air Core 2 Duo (Late 2010)", - ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL, - ASMC_MBA3_TEMPS, ASMC_MBA3_TEMPNAMES, ASMC_MBA3_TEMPDESCS - }, +#ifdef ASMC_DEBUG +/* Raw key access */ +static int asmc_raw_key_sysctl(SYSCTL_HANDLER_ARGS); +static int asmc_raw_value_sysctl(SYSCTL_HANDLER_ARGS); +static int asmc_raw_len_sysctl(SYSCTL_HANDLER_ARGS); +static int asmc_raw_type_sysctl(SYSCTL_HANDLER_ARGS); +#endif - { - "MacBookAir4,1", "Apple SMC Macbook Air 11-inch (Mid 2011)", - ASMC_SMS_FUNCS_DISABLED, - ASMC_FAN_FUNCS2, - ASMC_LIGHT_FUNCS, - ASMC_MBA4_TEMPS, ASMC_MBA4_TEMPNAMES, ASMC_MBA4_TEMPDESCS - }, +/* Voltage/Current/Power/Light sensor support */ +static int asmc_sensor_read(device_t, const char *, int *); +static int asmc_sensor_sysctl(SYSCTL_HANDLER_ARGS); +static int asmc_detect_sensors(device_t); +static int asmc_key_dump_by_index(device_t, int, char *, char *, uint8_t *); +static int asmc_key_search(device_t, const char *, unsigned int *); +static const char *asmc_temp_desc(const char *key); - { - "MacBookAir4,2", "Apple SMC Macbook Air 13-inch (Mid 2011)", - ASMC_SMS_FUNCS_DISABLED, - ASMC_FAN_FUNCS2, - ASMC_LIGHT_FUNCS, - ASMC_MBA4_TEMPS, ASMC_MBA4_TEMPNAMES, ASMC_MBA4_TEMPDESCS - }, - - { - "MacBookAir5,1", "Apple SMC MacBook Air 11-inch (Mid 2012)", - ASMC_SMS_FUNCS_DISABLED, - ASMC_FAN_FUNCS2, - ASMC_LIGHT_FUNCS, - ASMC_MBA5_TEMPS, ASMC_MBA5_TEMPNAMES, ASMC_MBA5_TEMPDESCS - }, - - { - "MacBookAir5,2", "Apple SMC MacBook Air 13-inch (Mid 2012)", - ASMC_SMS_FUNCS_DISABLED, - ASMC_FAN_FUNCS2, - ASMC_LIGHT_FUNCS, - ASMC_MBA5_TEMPS, ASMC_MBA5_TEMPNAMES, ASMC_MBA5_TEMPDESCS - }, - { - "MacBookAir6,1", "Apple SMC MacBook Air 11-inch (Early 2013)", - ASMC_SMS_FUNCS_DISABLED, - ASMC_FAN_FUNCS2, - ASMC_LIGHT_FUNCS_10BYTE, - ASMC_MBA6_TEMPS, ASMC_MBA6_TEMPNAMES, ASMC_MBA6_TEMPDESCS - }, - { - "MacBookAir6,2", "Apple SMC MacBook Air 13-inch (Early 2013)", - ASMC_SMS_FUNCS_DISABLED, - ASMC_FAN_FUNCS2, - ASMC_LIGHT_FUNCS_10BYTE, - ASMC_MBA6_TEMPS, ASMC_MBA6_TEMPNAMES, ASMC_MBA6_TEMPDESCS - }, - { - "MacBookAir7,1", "Apple SMC MacBook Air 11-inch (Early 2015)", - ASMC_SMS_FUNCS_DISABLED, - ASMC_FAN_FUNCS2, - ASMC_LIGHT_FUNCS, - ASMC_MBA7_TEMPS, ASMC_MBA7_TEMPNAMES, ASMC_MBA7_TEMPDESCS - }, - { - "MacBookAir7,2", "Apple SMC MacBook Air 13-inch (Early 2015)", - ASMC_SMS_FUNCS_DISABLED, - ASMC_FAN_FUNCS2, - ASMC_LIGHT_FUNCS, - ASMC_MBA7_TEMPS, ASMC_MBA7_TEMPNAMES, ASMC_MBA7_TEMPDESCS - }, - { NULL, NULL } +/* + * SMC temperature key descriptions. + * These are universal across all Intel Apple hardware. + */ +static const struct { + const char *key; + const char *desc; +} asmc_temp_descs[] = { + /* Ambient / airflow */ + { "TA0P", "Ambient" }, + { "TA0S", "PCIe Slot 1 Ambient" }, + { "TA0p", "Ambient Air" }, + { "TA1P", "Ambient 2" }, + { "TA1S", "PCIe Slot 1 PCB" }, + { "TA1p", "Ambient Air 2" }, + { "TA2P", "Ambient 3" }, + { "TA2S", "PCIe Slot 2 Ambient" }, + { "TA3S", "PCIe Slot 2 PCB" }, + { "TA0V", "Ambient" }, + { "TALP", "Ambient Light Proximity" }, + { "TaLC", "Airflow Left" }, + { "TaRC", "Airflow Right" }, + { "Ta0P", "Airflow Proximity" }, + /* Battery / enclosure */ + { "TB0T", "Enclosure Bottom" }, + { "TB1T", "Battery 1" }, + { "TB2T", "Battery 2" }, + { "TB3T", "Battery 3" }, + { "TBXT", "Battery" }, + { "Tb0P", "BLC Proximity" }, + /* CPU */ + { "TC0C", "CPU Core 1" }, + { "TC0D", "CPU Die" }, + { "TC0E", "CPU 1" }, + { "TC0F", "CPU 2" }, + { "TC0G", "CPU Package GPU" }, + { "TC0H", "CPU Heatsink" }, + { "TC0h", "CPU Heatsink" }, + { "TC0J", "CPU" }, + { "TC0P", "CPU Proximity" }, + { "TC0c", "CPU Core 1 PECI" }, + { "TC0d", "CPU Die PECI" }, + { "TC0p", "CPU Proximity" }, + { "TC1C", "CPU Core 2" }, + { "TC1c", "CPU Core 2 PECI" }, + { "TC1P", "CPU Proximity 2" }, + { "TC2C", "CPU Core 3" }, + { "TC2P", "CPU Proximity 3" }, + { "TC2c", "CPU Core 3 PECI" }, + { "TC3C", "CPU Core 4" }, + { "TC3P", "CPU Proximity 4" }, + { "TC3c", "CPU Core 4 PECI" }, + { "TC4C", "CPU Core 5" }, + { "TC5C", "CPU Core 6" }, + { "TC6C", "CPU Core 7" }, + { "TC7C", "CPU Core 8" }, + { "TC8C", "CPU Core 9" }, + { "TCGC", "PECI GPU" }, + { "TCGc", "PECI GPU" }, + { "TCHP", "Charger Proximity" }, + { "TCSA", "PECI SA" }, + { "TCSC", "PECI SA" }, + { "TCSc", "PECI SA" }, + { "TCTD", "CPU DTS" }, + { "TCXC", "PECI CPU" }, + { "TCXc", "PECI CPU" }, + { "TCPG", "CPU Package GPU" }, + { "TCXR", "CPU PECI DTS" }, + /* CPU dual-socket (Mac Pro) */ + { "TCAG", "CPU A Package" }, + { "TCAH", "CPU A Heatsink" }, + { "TCBG", "CPU B Package" }, + { "TCBH", "CPU B Heatsink" }, + /* GPU */ + { "TG0C", "GPU Core" }, + { "TG0D", "GPU Diode" }, + { "TG0H", "GPU Heatsink" }, + { "TG0M", "GPU Memory" }, + { "TG0P", "GPU Proximity" }, + { "TG0T", "GPU Diode" }, + { "TG0V", "GPU" }, + { "TG0d", "GPU Die" }, + { "TG0h", "GPU Heatsink" }, + { "TG0p", "GPU Proximity" }, + { "TGTV", "GPU" }, + { "TG1D", "GPU 2 Diode" }, + { "TG1H", "GPU 2 Heatsink" }, + { "TG1P", "GPU 2 Proximity" }, + { "TG1d", "GPU 2 Die" }, + { "TGVP", "GPU Memory Proximity" }, + /* Storage */ + { "TH0A", "SSD A" }, + { "TH0B", "SSD B" }, + { "TH0C", "SSD C" }, + { "TH0F", "SSD" }, + { "TH0O", "HDD" }, + { "TH0P", "HDD Proximity" }, + { "TH0R", "SSD" }, + { "TH0V", "SSD" }, + { "TH0a", "SSD A" }, + { "TH0b", "SSD B" }, + { "TH0c", "SSD C" }, + { "TH1O", "HDD 2" }, + { "TH1P", "HDD Bay 2" }, + { "TH2P", "HDD Bay 3" }, + { "TH3P", "HDD Bay 4" }, + { "Th0H", "Heatpipe 1" }, + { "Th0N", "SSD" }, + { "Th1H", "Heatpipe 2" }, + { "Th2H", "Heatpipe 3" }, + /* Thunderbolt */ + { "THSP", "Thunderbolt Proximity" }, + { "TI0P", "Thunderbolt 1" }, + { "TI0p", "Thunderbolt 1" }, + { "TI1P", "Thunderbolt 2" }, + { "TI1p", "Thunderbolt 2" }, + { "TTLD", "Thunderbolt Left" }, + { "TTRD", "Thunderbolt Right" }, + { "Te0T", "Thunderbolt Diode" }, + { "Te0t", "Thunderbolt Diode" }, + /* LCD */ + { "TL0P", "LCD Proximity" }, + { "TL0V", "LCD" }, + { "TL0p", "LCD Proximity" }, + { "TL1P", "LCD Panel 1" }, + { "TL1V", "LCD 1" }, + { "TL1p", "LCD Panel 1" }, + { "TL1v", "LCD 1" }, + { "TL2V", "LCD 2" }, + { "TLAV", "LCD" }, + { "TLBV", "LCD" }, + { "TLCV", "LCD" }, + /* Memory */ + { "TM0P", "Memory Proximity" }, + { "TM0S", "Memory Slot 1" }, + { "TM0p", "Memory Proximity" }, + { "TM1P", "Memory Riser A 2" }, + { "TM1S", "Memory Slot 2" }, + { "Tm0P", "Memory Proximity" }, + { "Tm0p", "Memory Proximity" }, + { "Tm1P", "Memory Proximity 2" }, + { "TMBS", "Memory Bank" }, + { "TMCD", "Memory DIMM" }, + /* Northbridge / MCH */ + { "TN0C", "Northbridge Core" }, + { "TN0D", "Northbridge Diode" }, + { "TN0H", "MCH Heatsink" }, + { "TN0P", "Northbridge Proximity" }, + { "TN1D", "MCH Die 2" }, + { "TN1P", "Northbridge Proximity 2" }, + /* PCH */ + { "TP0P", "PCH Proximity" }, + { "TP0p", "PCH Proximity" }, + { "TPCD", "PCH Die" }, + { "TPCd", "PCH Die" }, + /* Optical drive */ + { "TO0P", "Optical Drive" }, + { "TO0p", "Optical Drive" }, + /* Power supply */ + { "Tp0C", "Power Supply" }, + { "Tp0P", "Power Supply Proximity" }, + { "Tp1C", "Power Supply 2" }, + { "Tp1P", "Power Supply Component" }, + { "Tp1p", "Power Supply Component" }, + { "Tp2P", "Power Supply 2" }, + { "Tp2h", "Power Supply 2" }, + { "Tp2H", "Power Supply 2" }, + { "Tp3P", "Power Supply 3 Inlet" }, + { "Tp3h", "Power Supply 3" }, + { "Tp3H", "Power Supply 3" }, + { "Tp4P", "Power Supply 4" }, + { "Tp5P", "Power Supply 5" }, + /* Palm rest / trackpad */ + { "Ts0P", "Palm Rest" }, + { "Ts0S", "Memory Proximity" }, + { "Ts1P", "Palm Rest 2" }, + { "Ts1S", "Palm Rest 2" }, + /* Wireless */ + { "TW0P", "Wireless Proximity" }, + { "TW0p", "Wireless Proximity" }, + { "TBLR", "Bluetooth" }, + /* Camera */ + { "TS2P", "Camera Proximity" }, + { "TS2V", "Camera" }, + { "TS2p", "Camera Proximity" }, + /* Expansion */ + { "TS0C", "Expansion Slots" }, + { "TS0P", "Expansion Proximity" }, + { "TS0V", "Expansion" }, + { "TS0p", "Expansion Proximity" }, + /* Air vent */ + { "TV0P", "Air Vent" }, + /* VRM */ + { "Tv0S", "VRM 1" }, + { "Tv1S", "VRM 2" }, + /* Misc */ + { "TTF0", "Fan" }, + { "TMLB", "Logic Board" }, }; -#undef ASMC_SMS_FUNCS -#undef ASMC_SMS_FUNCS_DISABLED -#undef ASMC_FAN_FUNCS -#undef ASMC_FAN_FUNCS2 -#undef ASMC_LIGHT_FUNCS +static const char * +asmc_temp_desc(const char *key) +{ + unsigned int i; + + for (i = 0; i < nitems(asmc_temp_descs); i++) { + if (strcmp(asmc_temp_descs[i].key, key) == 0) + return (asmc_temp_descs[i].desc); + } + return ("Temperature"); +} /* * Driver methods. @@ -500,7 +373,13 @@ static device_method_t asmc_methods[] = { DEVMETHOD(device_attach, asmc_attach), DEVMETHOD(device_detach, asmc_detach), DEVMETHOD(device_resume, asmc_resume), - { 0, 0 } + + /* Backlight interface */ + DEVMETHOD(backlight_update_status, asmc_backlight_update_status), + DEVMETHOD(backlight_get_status, asmc_backlight_get_status), + DEVMETHOD(backlight_get_info, asmc_backlight_get_info), + + DEVMETHOD_END }; static driver_t asmc_driver = { @@ -514,10 +393,10 @@ static driver_t asmc_driver = { */ #define _COMPONENT ACPI_OEM ACPI_MODULE_NAME("ASMC") -#ifdef DEBUG -#define ASMC_DPRINTF(str) device_printf(dev, str) +#ifdef ASMC_DEBUG +#define ASMC_DPRINTF(str, ...) device_printf(dev, str, ##__VA_ARGS__) #else -#define ASMC_DPRINTF(str) +#define ASMC_DPRINTF(str, ...) #endif /* NB: can't be const */ @@ -525,34 +404,15 @@ static char *asmc_ids[] = { "APP0001", NULL }; static unsigned int light_control = 0; +ACPI_PNP_INFO(asmc_ids); DRIVER_MODULE(asmc, acpi, asmc_driver, NULL, NULL); MODULE_DEPEND(asmc, acpi, 1, 1, 1); - -static const struct asmc_model * -asmc_match(device_t dev) -{ - int i; - char *model; - - model = kern_getenv("smbios.system.product"); - if (model == NULL) - return (NULL); - - for (i = 0; asmc_models[i].smc_model; i++) { - if (!strncmp(model, asmc_models[i].smc_model, strlen(model))) { - freeenv(model); - return (&asmc_models[i]); - } - } - freeenv(model); - - return (NULL); -} +MODULE_DEPEND(asmc, backlight, 1, 1, 1); static int asmc_probe(device_t dev) { - const struct asmc_model *model; + char *product; int rv; if (resource_disabled("asmc", 0)) @@ -560,15 +420,41 @@ asmc_probe(device_t dev) rv = ACPI_ID_PROBE(device_get_parent(dev), dev, asmc_ids, NULL); if (rv > 0) return (rv); + product = kern_getenv("smbios.system.product"); + device_set_descf(dev, "Apple %s", product ? product : "SMC"); + freeenv(product); + return (rv); +} - model = asmc_match(dev); - if (!model) { - device_printf(dev, "model not recognized\n"); - return (ENXIO); +/* + * Try PIO first; fall back to MMIO for T2 Macs. + */ +static int +asmc_try_probe(device_t dev) +{ + struct asmc_softc *sc = device_get_softc(dev); + + sc->sc_ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT, + &sc->sc_rid_port, RF_ACTIVE); + if (sc->sc_ioport != NULL) + return (0); + + sc->sc_rid_mem = 0; + sc->sc_iomem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->sc_rid_mem, RF_ACTIVE); + if (sc->sc_iomem != NULL) { + if (asmc_mmio_probe(dev) == 0) { + sc->sc_is_mmio = true; + device_printf(dev, "using MMIO backend (T2)\n"); + return (0); + } + bus_release_resource(dev, SYS_RES_MEMORY, + sc->sc_rid_mem, sc->sc_iomem); + sc->sc_iomem = NULL; } - device_set_desc(dev, model->smc_desc); - return (rv); + device_printf(dev, "unable to allocate IO port\n"); + return (ENOMEM); } static int @@ -580,24 +466,30 @@ asmc_attach(device_t dev) struct asmc_softc *sc = device_get_softc(dev); struct sysctl_ctx_list *sysctlctx; struct sysctl_oid *sysctlnode; - const struct asmc_model *model; - sc->sc_ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT, - &sc->sc_rid_port, RF_ACTIVE); - if (sc->sc_ioport == NULL) { - device_printf(dev, "unable to allocate IO port\n"); - return (ENOMEM); - } + ret = asmc_try_probe(dev); + if (ret != 0) + goto err; - sysctlctx = device_get_sysctl_ctx(dev); + sysctlctx = device_get_sysctl_ctx(dev); sysctlnode = device_get_sysctl_tree(dev); - model = asmc_match(dev); + /* Mutex may already be initialized by asmc_mmio_probe() */ + if (!mtx_initialized(&sc->sc_mtx)) + mtx_init(&sc->sc_mtx, "asmc", NULL, MTX_SPIN); + + /* Read SMC revision, key count, fan count */ + ret = asmc_init(dev); + if (ret != 0) { + device_printf(dev, "SMC not responding\n"); + goto err; + } - mtx_init(&sc->sc_mtx, "asmc", NULL, MTX_SPIN); + /* Probe SMC keys to detect capabilities */ + asmc_detect_capabilities(dev); - sc->sc_model = model; - asmc_init(dev); + /* Auto-detect and register voltage/current/power/ambient/temp sensors */ + asmc_detect_sensors(dev); /* * dev.asmc.n.fan.* tree. @@ -611,51 +503,57 @@ asmc_attach(device_t dev) name[0] = '0' + j; name[1] = 0; sc->sc_fan_tree[i] = SYSCTL_ADD_NODE(sysctlctx, - SYSCTL_CHILDREN(sc->sc_fan_tree[0]), - OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, - "Fan Subtree"); + SYSCTL_CHILDREN(sc->sc_fan_tree[0]), OID_AUTO, name, + CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Fan Subtree"); SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(sc->sc_fan_tree[i]), OID_AUTO, "id", - CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT, - dev, j, model->smc_fan_id, "I", - "Fan ID"); + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, j, + asmc_mb_sysctl_fanid, "I", "Fan ID"); SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(sc->sc_fan_tree[i]), OID_AUTO, "speed", - CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, - dev, j, model->smc_fan_speed, "I", - "Fan speed in RPM"); + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, j, + asmc_mb_sysctl_fanspeed, "I", "Fan speed in RPM"); - SYSCTL_ADD_PROC(sysctlctx, - SYSCTL_CHILDREN(sc->sc_fan_tree[i]), - OID_AUTO, "safespeed", - CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, - dev, j, model->smc_fan_safespeed, "I", - "Fan safe speed in RPM"); + if (sc->sc_has_safespeed) { + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sc->sc_fan_tree[i]), + OID_AUTO, "safespeed", + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, j, + asmc_mb_sysctl_fansafespeed, "I", + "Fan safe speed in RPM"); + } SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(sc->sc_fan_tree[i]), OID_AUTO, "minspeed", - CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, - dev, j, model->smc_fan_minspeed, "I", + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev, j, + asmc_mb_sysctl_fanminspeed, "I", "Fan minimum speed in RPM"); SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(sc->sc_fan_tree[i]), OID_AUTO, "maxspeed", - CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, - dev, j, model->smc_fan_maxspeed, "I", + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev, j, + asmc_mb_sysctl_fanmaxspeed, "I", "Fan maximum speed in RPM"); SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(sc->sc_fan_tree[i]), OID_AUTO, "targetspeed", - CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, - dev, j, model->smc_fan_targetspeed, "I", + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev, j, + asmc_mb_sysctl_fantargetspeed, "I", "Fan target speed in RPM"); + + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sc->sc_fan_tree[i]), + OID_AUTO, "manual", + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev, j, + asmc_mb_sysctl_fanmanual, "I", + "Fan manual mode (0=auto, 1=manual)"); } /* @@ -665,19 +563,19 @@ asmc_attach(device_t dev) SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "temp", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Temperature sensors"); - for (i = 0; model->smc_temps[i]; i++) { + for (i = 0; i < sc->sc_temp_count; i++) { SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(sc->sc_temp_tree), - OID_AUTO, model->smc_tempnames[i], - CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, - dev, i, asmc_temp_sysctl, "I", - model->smc_tempdescs[i]); + OID_AUTO, sc->sc_temp_sensors[i], + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, i, + asmc_temp_sysctl, "I", + asmc_temp_desc(sc->sc_temp_sensors[i])); } /* * dev.asmc.n.light */ - if (model->smc_light_left) { + if (sc->sc_has_light) { sc->sc_light_tree = SYSCTL_ADD_NODE(sysctlctx, SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "light", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, @@ -686,30 +584,164 @@ asmc_attach(device_t dev) SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(sc->sc_light_tree), OID_AUTO, "left", - CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, - dev, 0, model->smc_light_left, "I", - "Keyboard backlight left sensor"); + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, + dev, 0, + sc->sc_light_len == ASMC_LIGHT_LONGLEN ? + asmc_mbp_sysctl_light_left_10byte : + asmc_mbp_sysctl_light_left, + "I", "Keyboard backlight left sensor"); - SYSCTL_ADD_PROC(sysctlctx, - SYSCTL_CHILDREN(sc->sc_light_tree), - OID_AUTO, "right", - CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, - dev, 0, model->smc_light_right, "I", - "Keyboard backlight right sensor"); + if (sc->sc_light_len != ASMC_LIGHT_LONGLEN && + asmc_key_getinfo(dev, ASMC_KEY_LIGHTRIGHT, + NULL, NULL) == 0) { + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sc->sc_light_tree), + OID_AUTO, "right", + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, + dev, 0, + asmc_mbp_sysctl_light_right, "I", + "Keyboard backlight right sensor"); + } SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(sc->sc_light_tree), OID_AUTO, "control", - CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | - CTLFLAG_NEEDGIANT, dev, 0, - model->smc_light_control, "I", + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MPSAFE, + dev, 0, asmc_mbp_sysctl_light_control, "I", "Keyboard backlight brightness control"); + + sc->sc_kbd_bkl = backlight_register("asmc", dev); + if (sc->sc_kbd_bkl == NULL) { + device_printf(dev, "Can not register backlight\n"); + ret = ENXIO; + goto err; + } + } + +#ifdef ASMC_DEBUG + /* + * Raw SMC key access for debugging. + */ + sc->sc_raw_tree = SYSCTL_ADD_NODE(sysctlctx, + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "raw", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Raw SMC key access"); + + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sc->sc_raw_tree), + OID_AUTO, "key", + CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, + dev, 0, asmc_raw_key_sysctl, "A", + "SMC key name (4 chars)"); + + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sc->sc_raw_tree), + OID_AUTO, "value", + CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, + dev, 0, asmc_raw_value_sysctl, "A", + "SMC key value (hex string)"); + + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sc->sc_raw_tree), + OID_AUTO, "len", + CTLTYPE_U8 | CTLFLAG_RD | CTLFLAG_MPSAFE, + dev, 0, asmc_raw_len_sysctl, "CU", + "SMC key value length"); + + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sc->sc_raw_tree), + OID_AUTO, "type", + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, + dev, 0, asmc_raw_type_sysctl, "A", + "SMC key type (4 chars)"); +#endif + + /* + * Battery charge limit (T2 Macs). + */ + if (sc->sc_is_t2 && + asmc_key_getinfo(dev, ASMC_KEY_BCLM, NULL, NULL) == 0) { + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "battery_charge_limit", + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, + dev, 0, asmc_bclm_sysctl, "I", + "Battery charge limit (0-100)"); } - if (model->smc_sms_x == NULL) + /* System state / board identity subtree. */ + { + struct sysctl_oid *sys_tree; + uint8_t msps_len; + + sys_tree = SYSCTL_ADD_NODE(sysctlctx, + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "system", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, + "System state and board identity"); + if (sys_tree == NULL) { + device_printf(dev, + "failed to create system sysctl node\n"); + goto nosms; + } + + if (asmc_key_getinfo(dev, ASMC_KEY_MSSD, NULL, NULL) == 0) + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sys_tree), OID_AUTO, "shutdown_cause", + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, + dev, 0, asmc_cause_sysctl, "A", + "Last shutdown cause (MSSD)"); + + if (asmc_key_getinfo(dev, ASMC_KEY_MSSP, NULL, NULL) == 0) + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sys_tree), OID_AUTO, "sleep_cause", + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, + dev, 1, asmc_cause_sysctl, "A", + "Last sleep cause (MSSP)"); + + if (asmc_key_getinfo(dev, ASMC_KEY_MSAL, NULL, NULL) == 0) + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sys_tree), OID_AUTO, "thermal_status", + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, + dev, 0, asmc_msal_sysctl, "A", + "Thermal subsystem status flags (MSAL)"); + + if (asmc_key_getinfo(dev, ASMC_KEY_CLKT, NULL, NULL) == 0) + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sys_tree), OID_AUTO, "time_of_day", + CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_MPSAFE, + dev, 0, asmc_clkt_sysctl, "IU", + "Seconds since midnight per SMC clock (CLKT)"); + + if (asmc_key_getinfo(dev, ASMC_KEY_MSPS, &msps_len, NULL) == 0 && + (msps_len == 1 || msps_len == 2)) + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sys_tree), OID_AUTO, "power_state", + CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_MPSAFE, + dev, 0, asmc_msps_sysctl, "IU", + "SMC power state index (MSPS)"); + + if (asmc_key_getinfo(dev, ASMC_KEY_RPLT, NULL, NULL) == 0) + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sys_tree), OID_AUTO, "board_id", + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, + dev, 0, asmc_rplt_sysctl, "A", + "Apple internal board codename (RPlt)"); + + if (asmc_key_getinfo(dev, ASMC_KEY_RGEN, NULL, NULL) == 0) + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sys_tree), OID_AUTO, "chip_gen", + CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_MPSAFE, + dev, 0, asmc_rgen_sysctl, "IU", + "Apple security chip generation (RGEN; 3=T2)"); + } + + if (!sc->sc_has_sms) goto nosms; /* + * Initialize SMS hardware. + */ + asmc_sms_init(dev); + + /* * dev.asmc.n.sms tree. */ sc->sc_sms_tree = SYSCTL_ADD_NODE(sysctlctx, @@ -719,22 +751,22 @@ asmc_attach(device_t dev) SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(sc->sc_sms_tree), OID_AUTO, "x", - CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, - dev, 0, model->smc_sms_x, "I", + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, + dev, 0, asmc_mb_sysctl_sms_x, "I", "Sudden Motion Sensor X value"); SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(sc->sc_sms_tree), OID_AUTO, "y", - CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, - dev, 0, model->smc_sms_y, "I", + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, + dev, 0, asmc_mb_sysctl_sms_y, "I", "Sudden Motion Sensor Y value"); SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(sc->sc_sms_tree), OID_AUTO, "z", - CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, - dev, 0, model->smc_sms_z, "I", + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, + dev, 0, asmc_mb_sysctl_sms_z, "I", "Sudden Motion Sensor Z value"); /* @@ -757,33 +789,26 @@ asmc_attach(device_t dev) * Allocate an IRQ for the SMS. */ sc->sc_rid_irq = 0; - sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, - &sc->sc_rid_irq, RF_ACTIVE); + sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_rid_irq, + RF_ACTIVE); if (sc->sc_irq == NULL) { device_printf(dev, "unable to allocate IRQ resource\n"); ret = ENXIO; - goto err2; + goto err; } - ret = bus_setup_intr(dev, sc->sc_irq, - INTR_TYPE_MISC | INTR_MPSAFE, - asmc_sms_intrfast, NULL, - dev, &sc->sc_cookie); - + ret = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC | INTR_MPSAFE, + asmc_sms_intrfast, NULL, dev, &sc->sc_cookie); if (ret) { device_printf(dev, "unable to setup SMS IRQ\n"); - goto err1; + goto err; } + nosms: return (0); -err1: - bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq, sc->sc_irq); -err2: - bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port, - sc->sc_ioport); - mtx_destroy(&sc->sc_mtx); - if (sc->sc_sms_tq) - taskqueue_free(sc->sc_sms_tq); + +err: + asmc_detach(dev); return (ret); } @@ -793,19 +818,46 @@ asmc_detach(device_t dev) { struct asmc_softc *sc = device_get_softc(dev); + if (sc->sc_kbd_bkl != NULL) + backlight_destroy(sc->sc_kbd_bkl); + + /* Free temperature sensor key arrays */ + for (int i = 0; i < sc->sc_temp_count; i++) + free(sc->sc_temp_sensors[i], M_DEVBUF); + + /* Free sensor key arrays */ + for (int i = 0; i < sc->sc_voltage_count; i++) + free(sc->sc_voltage_sensors[i], M_DEVBUF); + for (int i = 0; i < sc->sc_current_count; i++) + free(sc->sc_current_sensors[i], M_DEVBUF); + for (int i = 0; i < sc->sc_power_count; i++) + free(sc->sc_power_sensors[i], M_DEVBUF); + for (int i = 0; i < sc->sc_light_count; i++) + free(sc->sc_light_sensors[i], M_DEVBUF); + if (sc->sc_sms_tq) { taskqueue_drain(sc->sc_sms_tq, &sc->sc_sms_task); taskqueue_free(sc->sc_sms_tq); + sc->sc_sms_tq = NULL; } - if (sc->sc_cookie) + if (sc->sc_cookie) { bus_teardown_intr(dev, sc->sc_irq, sc->sc_cookie); - if (sc->sc_irq) + sc->sc_cookie = NULL; + } + if (sc->sc_irq) { bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq, sc->sc_irq); - if (sc->sc_ioport) + sc->sc_irq = NULL; + } + if (sc->sc_ioport) { bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port, sc->sc_ioport); - mtx_destroy(&sc->sc_mtx); + sc->sc_ioport = NULL; + } + asmc_mmio_detach(dev, sc); + if (mtx_initialized(&sc->sc_mtx)) { + mtx_destroy(&sc->sc_mtx); + } return (0); } @@ -813,33 +865,115 @@ asmc_detach(device_t dev) static int asmc_resume(device_t dev) { - uint8_t buf[2]; - buf[0] = light_control; - buf[1] = 0x00; - asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, sizeof buf); - return (0); + uint8_t buf[2]; + + buf[0] = light_control; + buf[1] = 0x00; + asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, sizeof(buf)); + + return (0); } -#ifdef DEBUG -void asmc_dumpall(device_t dev) +#ifdef ASMC_DEBUG +void +asmc_dumpall(device_t dev) { + struct asmc_softc *sc = device_get_softc(dev); int i; - /* XXX magic number */ - for (i=0; i < 0x100; i++) + if (sc->sc_nkeys == 0) { + device_printf(dev, "asmc_dumpall: key count not available\n"); + return; + } + + device_printf(dev, "asmc_dumpall: dumping %d keys\n", sc->sc_nkeys); + for (i = 0; i < sc->sc_nkeys; i++) asmc_key_dump(dev, i); } #endif +/* + * Initialize SMC: read revision, key count, fan count. + * SMS initialization is handled separately in asmc_sms_init(). + */ static int asmc_init(device_t dev) { struct asmc_softc *sc = device_get_softc(dev); - int i, error = 1; - uint8_t buf[4]; + struct sysctl_ctx_list *sysctlctx; + uint8_t buf[6]; + int error; - if (sc->sc_model->smc_sms_x == NULL) - goto nosms; + sysctlctx = device_get_sysctl_ctx(dev); + + error = asmc_key_read(dev, ASMC_KEY_REV, buf, 6); + if (error != 0) { + /* + * Could not read REV key; T2 Macs may not have it. + * Use #KEY as a liveness check instead. + */ + if (sc->sc_is_t2) { + error = asmc_key_read(dev, ASMC_NKEYS, buf, 4); + if (error != 0) + goto out; + device_printf(dev, "T2 SMC: %d keys\n", + be32dec(buf)); + } else { + goto out; + } + } else { + device_printf(dev, "SMC revision: %x.%x%x%x\n", + buf[0], buf[1], buf[2], + ntohs(*(uint16_t *)buf + 4)); + } + + /* Auto power-on after AC power loss (AUPO). */ + if (asmc_key_read(dev, ASMC_KEY_AUPO, buf, 1) == 0) { + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "auto_poweron", + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, + dev, 0, asmc_aupo_sysctl, "I", + "Auto power-on after AC power loss (0=off, 1=on)"); + } + + sc->sc_nfan = asmc_fan_count(dev); + if (sc->sc_nfan > ASMC_MAXFANS) { + device_printf(dev, + "more than %d fans were detected. Please report this.\n", + ASMC_MAXFANS); + sc->sc_nfan = ASMC_MAXFANS; + } + + /* + * Read and cache the number of SMC keys (32 bit buffer) + */ + if (asmc_key_read(dev, ASMC_NKEYS, buf, 4) == 0) { + sc->sc_nkeys = be32dec(buf); + if (bootverbose) + device_printf(dev, "number of keys: %d\n", + sc->sc_nkeys); + } else { + sc->sc_nkeys = 0; + } + +out: +#ifdef ASMC_DEBUG + asmc_dumpall(dev); +#endif + return (error); +} + +/* + * Initialize the Sudden Motion Sensor hardware. + * Called from asmc_attach() after capabilities are detected. + */ +static void +asmc_sms_init(device_t dev) +{ + struct asmc_softc *sc = device_get_softc(dev); + uint8_t buf[2]; + int i; /* * We are ready to receive interrupts from the SMS. @@ -892,9 +1026,8 @@ asmc_init(device_t dev) for (i = 0; i < 1000; i++) { if (asmc_key_read(dev, ASMC_KEY_SMS, buf, 2) == 0 && (buf[0] == ASMC_SMS_INIT1 && buf[1] == ASMC_SMS_INIT2)) { - error = 0; sc->sc_sms_intr_works = 1; - goto out; + goto done; } buf[0] = ASMC_SMS_INIT1; buf[1] = ASMC_SMS_INIT2; @@ -904,29 +1037,71 @@ asmc_init(device_t dev) } device_printf(dev, "WARNING: Sudden Motion Sensor not initialized!\n"); -out: +done: asmc_sms_calibrate(dev); -nosms: - sc->sc_nfan = asmc_fan_count(dev); - if (sc->sc_nfan > ASMC_MAXFANS) { - device_printf(dev, "more than %d fans were detected. Please " - "report this.\n", ASMC_MAXFANS); - sc->sc_nfan = ASMC_MAXFANS; - } +} - if (bootverbose) { - /* - * The number of keys is a 32 bit buffer - */ - asmc_key_read(dev, ASMC_NKEYS, buf, 4); - device_printf(dev, "number of keys: %d\n", ntohl(*(uint32_t*)buf)); +/* + * Probe SMC keys to detect hardware capabilities. + */ +static void +asmc_detect_capabilities(device_t dev) +{ + struct asmc_softc *sc = device_get_softc(dev); + uint8_t len; + char type[ASMC_TYPELEN + 1]; + + /* SMS: require all keys used by asmc_sms_init() */ + sc->sc_has_sms = + (asmc_key_getinfo(dev, ASMC_KEY_SMS, + &len, type) == 0 && + asmc_key_getinfo(dev, ASMC_KEY_SMS_X, + &len, type) == 0 && + asmc_key_getinfo(dev, ASMC_KEY_SMS_Y, + &len, type) == 0 && + asmc_key_getinfo(dev, ASMC_KEY_SMS_Z, + &len, type) == 0 && + asmc_key_getinfo(dev, ASMC_KEY_SMS_LOW, + &len, type) == 0 && + asmc_key_getinfo(dev, ASMC_KEY_SMS_HIGH, + &len, type) == 0 && + asmc_key_getinfo(dev, ASMC_KEY_SMS_LOW_INT, + &len, type) == 0 && + asmc_key_getinfo(dev, ASMC_KEY_SMS_HIGH_INT, + &len, type) == 0 && + asmc_key_getinfo(dev, ASMC_KEY_SMS_FLAG, + &len, type) == 0 && + asmc_key_getinfo(dev, ASMC_KEY_INTOK, + &len, type) == 0); + + /* Light sensor: require ALV0 (len 6 or 10) and LKSB */ + if (asmc_key_getinfo(dev, ASMC_KEY_LIGHTLEFT, + &len, type) == 0 && + (len == ASMC_LIGHT_SHORTLEN || len == ASMC_LIGHT_LONGLEN) && + asmc_key_getinfo(dev, ASMC_KEY_LIGHTVALUE, + NULL, NULL) == 0) { + sc->sc_has_light = 1; + sc->sc_light_len = len; + } else { + sc->sc_has_light = 0; + sc->sc_light_len = 0; } -#ifdef DEBUG - asmc_dumpall(dev); -#endif + /* Fan safe speed */ + sc->sc_has_safespeed = + (asmc_key_getinfo(dev, ASMC_KEY_FANSAFESPEED0, + &len, type) == 0); - return (error); + /* Ambient light interrupt source */ + sc->sc_has_alsl = + (asmc_key_getinfo(dev, ASMC_KEY_LIGHTSRC, + &len, type) == 0); + + if (bootverbose) + device_printf(dev, + "capabilities: sms=%d light=%d (len=%d) safespeed=%d alsl=%d\n", + sc->sc_has_sms, sc->sc_has_light, sc->sc_light_len, + sc->sc_has_safespeed, sc->sc_has_alsl); } /* @@ -957,21 +1132,18 @@ asmc_wait_ack(device_t dev, uint8_t val, int amount) static int asmc_wait(device_t dev, uint8_t val) { -#ifdef DEBUG +#ifdef ASMC_DEBUG struct asmc_softc *sc; #endif if (asmc_wait_ack(dev, val, 1000) == 0) return (0); -#ifdef DEBUG +#ifdef ASMC_DEBUG sc = device_get_softc(dev); -#endif - val = val & ASMC_STATUS_MASK; -#ifdef DEBUG - device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, val, - ASMC_CMDPORT_READ(sc)); + device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, + val & ASMC_STATUS_MASK, ASMC_CMDPORT_READ(sc)); #endif return (1); } @@ -981,18 +1153,19 @@ asmc_wait(device_t dev, uint8_t val) * the acknowledgement fails. */ static int -asmc_command(device_t dev, uint8_t command) { +asmc_command(device_t dev, uint8_t command) +{ int i; struct asmc_softc *sc = device_get_softc(dev); - for (i=0; i < 10; i++) { + for (i = 0; i < 10; i++) { ASMC_CMDPORT_WRITE(sc, command); if (asmc_wait_ack(dev, 0x0c, 100) == 0) { return (0); } } -#ifdef DEBUG +#ifdef ASMC_DEBUG device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, command, ASMC_CMDPORT_READ(sc)); #endif @@ -1002,8 +1175,11 @@ asmc_command(device_t dev, uint8_t command) { static int asmc_key_read(device_t dev, const char *key, uint8_t *buf, uint8_t len) { - int i, error = 1, try = 0; struct asmc_softc *sc = device_get_softc(dev); + int i, error = 1, try = 0; + + if (sc->sc_is_mmio) + return (asmc_mmio_key_read(dev, key, buf, len)); mtx_lock_spin(&sc->sc_mtx); @@ -1028,9 +1204,10 @@ begin: error = 0; out: if (error) { - if (++try < 10) goto begin; - device_printf(dev,"%s for key %s failed %d times, giving up\n", - __func__, key, try); + if (++try < 10) + goto begin; + device_printf(dev, "%s for key %s failed %d times, giving up\n", + __func__, key, try); } mtx_unlock_spin(&sc->sc_mtx); @@ -1038,57 +1215,78 @@ out: return (error); } -#ifdef DEBUG +#ifdef ASMC_DEBUG static int asmc_key_dump(device_t dev, int number) { struct asmc_softc *sc = device_get_softc(dev); - char key[5] = { 0 }; - char type[7] = { 0 }; + char key[ASMC_KEYLEN + 1] = { 0 }; + char type[ASMC_KEYINFO_RESPLEN + 1] = { 0 }; uint8_t index[4]; - uint8_t v[32]; + uint8_t v[ASMC_MAXVAL]; uint8_t maxlen; int i, error = 1, try = 0; + if (sc->sc_is_mmio) { + uint8_t len = 0; + char mmio_type[ASMC_TYPELEN + 1] = { 0 }; + if (asmc_key_dump_by_index(dev, number, key, mmio_type, &len)) + return (1); + memset(v, 0, sizeof(v)); + len = MIN(len, sizeof(v)); + asmc_key_read(dev, key, v, len); + struct sbuf sb; + char buf[128]; + sbuf_new(&sb, buf, sizeof(buf), SBUF_FIXEDLEN); + sbuf_printf(&sb, "key %d: %s, type %s (len %d), data", + number, key, mmio_type, len); + for (i = 0; i < len; i++) + sbuf_printf(&sb, " %02x", v[i]); + sbuf_finish(&sb); + device_printf(dev, "%s\n", sbuf_data(&sb)); + sbuf_delete(&sb); + return (0); + } + mtx_lock_spin(&sc->sc_mtx); index[0] = (number >> 24) & 0xff; index[1] = (number >> 16) & 0xff; index[2] = (number >> 8) & 0xff; - index[3] = (number) & 0xff; + index[3] = number & 0xff; begin: - if (asmc_command(dev, 0x12)) + if (asmc_command(dev, ASMC_CMDGETBYINDEX)) goto out; - for (i = 0; i < 4; i++) { + for (i = 0; i < ASMC_KEYLEN; i++) { ASMC_DATAPORT_WRITE(sc, index[i]); - if (asmc_wait(dev, 0x04)) + if (asmc_wait(dev, ASMC_STATUS_AWAIT_DATA)) goto out; } - ASMC_DATAPORT_WRITE(sc, 4); + ASMC_DATAPORT_WRITE(sc, ASMC_KEYLEN); - for (i = 0; i < 4; i++) { - if (asmc_wait(dev, 0x05)) + for (i = 0; i < ASMC_KEYLEN; i++) { + if (asmc_wait(dev, ASMC_STATUS_DATA_READY)) goto out; key[i] = ASMC_DATAPORT_READ(sc); } - /* get type */ - if (asmc_command(dev, 0x13)) + /* Get key info (length + type). */ + if (asmc_command(dev, ASMC_CMDGETINFO)) goto out; - for (i = 0; i < 4; i++) { + for (i = 0; i < ASMC_KEYLEN; i++) { ASMC_DATAPORT_WRITE(sc, key[i]); - if (asmc_wait(dev, 0x04)) + if (asmc_wait(dev, ASMC_STATUS_AWAIT_DATA)) goto out; } - ASMC_DATAPORT_WRITE(sc, 6); + ASMC_DATAPORT_WRITE(sc, ASMC_KEYINFO_RESPLEN); - for (i = 0; i < 6; i++) { - if (asmc_wait(dev, 0x05)) + for (i = 0; i < ASMC_KEYINFO_RESPLEN; i++) { + if (asmc_wait(dev, ASMC_STATUS_DATA_READY)) goto out; type[i] = ASMC_DATAPORT_READ(sc); } @@ -1096,47 +1294,641 @@ begin: error = 0; out: if (error) { - if (++try < 10) goto begin; - device_printf(dev,"%s for key %s failed %d times, giving up\n", - __func__, key, try); - mtx_unlock_spin(&sc->sc_mtx); + if (++try < ASMC_MAXRETRIES) + goto begin; + device_printf(dev, + "%s for key %d failed %d times, giving up\n", + __func__, number, try); } - else { - char buf[1024]; - char buf2[8]; - mtx_unlock_spin(&sc->sc_mtx); - maxlen = type[0]; - type[0] = ' '; - type[5] = 0; - if (maxlen > sizeof(v)) { + mtx_unlock_spin(&sc->sc_mtx); + + if (error) + return (error); + + maxlen = type[0]; + type[0] = ' '; + type[5] = '\0'; + maxlen = MIN(maxlen, sizeof(v)); + + memset(v, 0, sizeof(v)); + error = asmc_key_read(dev, key, v, maxlen); + if (error) + return (error); + + struct sbuf sb; + char buf[128]; + sbuf_new(&sb, buf, sizeof(buf), SBUF_FIXEDLEN); + sbuf_printf(&sb, "key %d: %s, type%s (len %d), data", + number, key, type, maxlen); + for (i = 0; i < maxlen; i++) + sbuf_printf(&sb, " %02x", v[i]); + sbuf_finish(&sb); + device_printf(dev, "%s\n", sbuf_data(&sb)); + sbuf_delete(&sb); + + return (0); +} +#endif /* ASMC_DEBUG */ + +/* + * Get key info (length and type) from SMC using command 0x13. + * If len is non-NULL, stores the key's value length. + * If type is non-NULL, stores the 4-char type string (must be at least 5 bytes). + */ +static int +asmc_key_getinfo(device_t dev, const char *key, uint8_t *len, char *type) +{ + struct asmc_softc *sc = device_get_softc(dev); + uint8_t info[ASMC_KEYINFO_RESPLEN]; + int i, error = -1, try = 0; + + if (sc->sc_is_mmio) + return (asmc_mmio_key_getinfo(dev, key, len, type)); + + mtx_lock_spin(&sc->sc_mtx); + +begin: + if (asmc_command(dev, ASMC_CMDGETINFO)) + goto out; + + for (i = 0; i < ASMC_KEYLEN; i++) { + ASMC_DATAPORT_WRITE(sc, key[i]); + if (asmc_wait(dev, ASMC_STATUS_AWAIT_DATA)) + goto out; + } + + ASMC_DATAPORT_WRITE(sc, ASMC_KEYINFO_RESPLEN); + + for (i = 0; i < ASMC_KEYINFO_RESPLEN; i++) { + if (asmc_wait(dev, ASMC_STATUS_DATA_READY)) + goto out; + info[i] = ASMC_DATAPORT_READ(sc); + } + + error = 0; +out: + if (error && ++try < ASMC_MAXRETRIES) + goto begin; + mtx_unlock_spin(&sc->sc_mtx); + + if (error == 0) { + if (len != NULL) + *len = info[0]; + if (type != NULL) { + for (i = 0; i < ASMC_TYPELEN; i++) + type[i] = info[i + 1]; + type[ASMC_TYPELEN] = '\0'; + } + } + return (error); +} + +#ifdef ASMC_DEBUG +/* + * Raw SMC key access sysctls - enables reading/writing any SMC key by name + * Usage: + * sysctl dev.asmc.0.raw.key=TC0P # Set key, auto-detects length + * sysctl dev.asmc.0.raw.value # Read current value (hex bytes) + * sysctl dev.asmc.0.raw.value=01 # Write new value + */ +static int +asmc_raw_key_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev = (device_t) arg1; + struct asmc_softc *sc = device_get_softc(dev); + char newkey[ASMC_KEYLEN + 1]; + uint8_t keylen; + int error; + + strlcpy(newkey, sc->sc_rawkey, sizeof(newkey)); + error = sysctl_handle_string(oidp, newkey, sizeof(newkey), req); + if (error || req->newptr == NULL) + return (error); + + if (strlen(newkey) != ASMC_KEYLEN) + return (EINVAL); + + /* Get key info to auto-detect length and type */ + if (asmc_key_getinfo(dev, newkey, &keylen, sc->sc_rawtype) != 0) + return (ENOENT); + + if (keylen > ASMC_MAXVAL) + keylen = ASMC_MAXVAL; + + strlcpy(sc->sc_rawkey, newkey, sizeof(sc->sc_rawkey)); + sc->sc_rawlen = keylen; + memset(sc->sc_rawval, 0, sizeof(sc->sc_rawval)); + + /* Read the key value */ + asmc_key_read(dev, sc->sc_rawkey, sc->sc_rawval, sc->sc_rawlen); + + return (0); +} + +static int +asmc_raw_value_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev = (device_t) arg1; + struct asmc_softc *sc = device_get_softc(dev); + char hexbuf[ASMC_MAXVAL * 2 + 1]; + int error, i; + + /* Refresh from SMC if a key has been selected. */ + if (sc->sc_rawkey[0] != '\0') { + asmc_key_read(dev, sc->sc_rawkey, sc->sc_rawval, + sc->sc_rawlen > 0 ? sc->sc_rawlen : ASMC_MAXVAL); + } + + /* Format as hex string */ + for (i = 0; i < sc->sc_rawlen && i < ASMC_MAXVAL; i++) + snprintf(hexbuf + i * 2, 3, "%02x", sc->sc_rawval[i]); + hexbuf[i * 2] = '\0'; + + error = sysctl_handle_string(oidp, hexbuf, sizeof(hexbuf), req); + if (error || req->newptr == NULL) + return (error); + + /* Reject writes until a key is selected via raw.key. */ + if (sc->sc_rawkey[0] == '\0') + return (EINVAL); + + memset(sc->sc_rawval, 0, sizeof(sc->sc_rawval)); + for (i = 0; i < sc->sc_rawlen && hexbuf[i*2] && hexbuf[i*2+1]; i++) { + unsigned int val; + char tmp[3] = { hexbuf[i*2], hexbuf[i*2+1], 0 }; + if (sscanf(tmp, "%02x", &val) == 1) + sc->sc_rawval[i] = (uint8_t)val; + } + + if (asmc_key_write(dev, sc->sc_rawkey, sc->sc_rawval, sc->sc_rawlen) != 0) + return (EIO); + + return (0); +} + +static int +asmc_raw_len_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev = (device_t) arg1; + struct asmc_softc *sc = device_get_softc(dev); + + return (sysctl_handle_8(oidp, &sc->sc_rawlen, 0, req)); +} + +static int +asmc_raw_type_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev = (device_t) arg1; + struct asmc_softc *sc = device_get_softc(dev); + + return (sysctl_handle_string(oidp, sc->sc_rawtype, + sizeof(sc->sc_rawtype), req)); +} +#endif + +/* SMC sensor type table: type string to fixed-point divisor. */ +static const struct { + const char type[5]; + int divisor; +} asmc_sensor_types[] = { + { "sp78", 256 }, + { "sp87", 128 }, + { "sp4b", 2048 }, + { "sp5a", 1024 }, + { "sp69", 512 }, + { "sp96", 64 }, + { "sp2d", 8192 }, + { "ui16", 1 }, + { "", 0 }, +}; + +/* Convert a 2-byte SMC value to milli-units. */ +static bool +asmc_sensor_convert(const char *type, const uint8_t *buf, int *millivalue) +{ + int i; + + for (i = 0; asmc_sensor_types[i].divisor != 0; i++) { + if (strncmp(type, asmc_sensor_types[i].type, 4) != 0) + continue; + if (asmc_sensor_types[i].divisor == 1) + *millivalue = be16dec(buf); + else + *millivalue = ((int)(int16_t)be16dec(buf) * 1000) / + asmc_sensor_types[i].divisor; + return (true); + } + return (false); +} + +static bool +asmc_sensor_type_supported(const char *type) +{ + int i; + + for (i = 0; asmc_sensor_types[i].divisor != 0; i++) + if (strncmp(type, asmc_sensor_types[i].type, 4) == 0) + return (true); + return (false); +} + +/* + * Generic sensor value reader with automatic type conversion. + * Reads an SMC key, detects its type, and converts to millivalue. + */ +static int +asmc_sensor_read(device_t dev, const char *key, int *millivalue) +{ + uint8_t buf[2]; + char type[ASMC_TYPELEN + 1]; + uint8_t len; + int error; + + error = asmc_key_getinfo(dev, key, &len, type); + if (error != 0) + return (error); + + if (len != 2) { + if (bootverbose) device_printf(dev, - "WARNING: cropping maxlen from %d to %zu\n", - maxlen, sizeof(v)); - maxlen = sizeof(v); + "%s: key %s unexpected length %d\n", + __func__, key, len); + return (ENXIO); + } + + error = asmc_key_read(dev, key, buf, sizeof(buf)); + if (error != 0) + return (error); + + if (!asmc_sensor_convert(type, buf, millivalue)) { + if (bootverbose) + device_printf(dev, + "%s: unknown type '%s' for key %s\n", + __func__, type, key); + return (ENXIO); + } + + return (0); +} + +/* + * Generic sensor sysctl handler for voltage/current/power/light sensors. + * arg2 encodes: sensor_type (high byte) | sensor_index (low byte) + * Sensor types: 'V'=voltage, 'I'=current, 'P'=power, 'L'=light + */ +static int +asmc_sensor_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev = (device_t) arg1; + struct asmc_softc *sc = device_get_softc(dev); + int error, val; + int sensor_type = (arg2 >> 8) & 0xFF; + int sensor_idx = arg2 & 0xFF; + const char *key = NULL; + + /* Select sensor based on type and index */ + switch (sensor_type) { + case 'V': /* Voltage */ + if (sensor_idx < sc->sc_voltage_count) + key = sc->sc_voltage_sensors[sensor_idx]; + break; + case 'I': /* Current */ + if (sensor_idx < sc->sc_current_count) + key = sc->sc_current_sensors[sensor_idx]; + break; + case 'P': /* Power */ + if (sensor_idx < sc->sc_power_count) + key = sc->sc_power_sensors[sensor_idx]; + break; + case 'L': /* Light */ + if (sensor_idx < sc->sc_light_count) + key = sc->sc_light_sensors[sensor_idx]; + break; + default: + return (EINVAL); + } + + if (key == NULL) + return (ENOENT); + + error = asmc_sensor_read(dev, key, &val); + if (error != 0) + return (error); + + return (sysctl_handle_int(oidp, &val, 0, req)); +} + +/* + * Scan a range of SMC key indices, adding matching sensors. + * Only considers 2-byte keys with a supported type. + */ +static void +asmc_scan_sensor_range(device_t dev, unsigned int start, + unsigned int end, char prefix, int *countp, char **sensors, + int maxcount) +{ + char key[ASMC_KEYLEN + 1]; + char type[ASMC_TYPELEN + 1]; + uint8_t len; + unsigned int i; + char *sensor_key; + + for (i = start; i < end; i++) { + if (asmc_key_dump_by_index(dev, i, key, type, &len)) + continue; + if (key[0] != prefix || len != 2) + continue; + if (!asmc_sensor_type_supported(type)) + continue; + if (*countp >= maxcount) + break; + sensor_key = malloc(ASMC_KEYLEN + 1, + M_DEVBUF, M_WAITOK); + memcpy(sensor_key, key, ASMC_KEYLEN + 1); + sensors[(*countp)++] = sensor_key; + } +} + +static int +asmc_detect_sensors(device_t dev) +{ + struct asmc_softc *sc = device_get_softc(dev); + struct sysctl_ctx_list *sysctlctx; + struct sysctl_oid *tree_node; + char key[ASMC_KEYLEN + 1]; + char type[ASMC_TYPELEN + 1]; + uint8_t len; + unsigned int start, end, i; + int error; + char *sensor_key; + + sc->sc_voltage_count = 0; + sc->sc_current_count = 0; + sc->sc_power_count = 0; + sc->sc_light_count = 0; + sc->sc_temp_count = 0; + + if (sc->sc_nkeys == 0) + return (0); + + /* + * Temperature sensors: binary search for T..U range, + * then filter by type sp78. + */ + error = asmc_key_search(dev, "T\0\0\0", &start); + if (error == 0) + error = asmc_key_search(dev, "U\0\0\0", &end); + if (error == 0) { + for (i = start; i < end; i++) { + if (asmc_key_dump_by_index(dev, i, + key, type, &len)) + continue; + if (len != 2 || + strncmp(type, "sp78", 4) != 0) + continue; + if (sc->sc_temp_count >= ASMC_TEMP_MAX) + break; + sensor_key = malloc(ASMC_KEYLEN + 1, + M_DEVBUF, M_WAITOK); + memcpy(sensor_key, key, ASMC_KEYLEN + 1); + sc->sc_temp_sensors[sc->sc_temp_count++] = + sensor_key; } - for (i = 0; i < sizeof(v); i++) { - v[i] = 0; + } + + /* Voltage/Current/Power sensors */ + static const struct { + const char *range_start; + const char *range_end; + char prefix; + } sensor_ranges[] = { + { "V\0\0\0", "W\0\0\0", 'V' }, /* Voltage */ + { "I\0\0\0", "J\0\0\0", 'I' }, /* Current */ + { "P\0\0\0", "Q\0\0\0", 'P' }, /* Power */ + }; + static const size_t nsensor_ranges = nitems(sensor_ranges); + + int *sensor_counts[] = { + &sc->sc_voltage_count, &sc->sc_current_count, + &sc->sc_power_count }; + char **sensor_arrays[] = { + sc->sc_voltage_sensors, sc->sc_current_sensors, + sc->sc_power_sensors }; + + for (unsigned int r = 0; r < nsensor_ranges; r++) { + error = asmc_key_search(dev, sensor_ranges[r].range_start, + &start); + if (error == 0) + error = asmc_key_search(dev, + sensor_ranges[r].range_end, &end); + if (error == 0) + asmc_scan_sensor_range(dev, start, end, + sensor_ranges[r].prefix, sensor_counts[r], + sensor_arrays[r], ASMC_MAX_SENSORS); + } + + /* Ambient light sensors: AL* in A..B range */ + error = asmc_key_search(dev, "A\0\0\0", &start); + if (error == 0) + error = asmc_key_search(dev, "B\0\0\0", &end); + if (error == 0) { + for (i = start; i < end; i++) { + if (asmc_key_dump_by_index(dev, i, + key, type, &len)) + continue; + if (key[0] != 'A' || key[1] != 'L' || + (key[2] != 'V' && key[2] != 'S') || + len != 2) + continue; + if (!asmc_sensor_type_supported(type)) + continue; + if (sc->sc_light_count >= ASMC_MAX_SENSORS) + break; + sensor_key = malloc(ASMC_KEYLEN + 1, + M_DEVBUF, M_WAITOK); + memcpy(sensor_key, key, ASMC_KEYLEN + 1); + sc->sc_light_sensors[sc->sc_light_count++] = + sensor_key; } - asmc_key_read(dev, key, v, maxlen); - snprintf(buf, sizeof(buf), "key %d is: %s, type %s " - "(len %d), data", number, key, type, maxlen); - for (i = 0; i < maxlen; i++) { - snprintf(buf2, sizeof(buf2), " %02x", v[i]); - strlcat(buf, buf2, sizeof(buf)); + } + + if (bootverbose) + device_printf(dev, + "detected %d temp, %d voltage, %d current, " + "%d power, %d light sensors\n", + sc->sc_temp_count, sc->sc_voltage_count, + sc->sc_current_count, + sc->sc_power_count, sc->sc_light_count); + + /* Register sysctls for detected sensors */ + sysctlctx = device_get_sysctl_ctx(dev); + + static const struct { + const char *node_name; + const char *node_desc; + char tag; + const char *leaf_desc; + } sensor_sysctl[] = { + { "voltage", "Voltage sensors (millivolts)", 'V', + "Voltage sensor (millivolts)" }, + { "current", "Current sensors (milliamps)", 'I', + "Current sensor (milliamps)" }, + { "power", "Power sensors (milliwatts)", 'P', + "Power sensor (milliwatts)" }, + { "ambient", "Ambient light sensors", 'L', + "Light sensor value" }, + }; + + int *sysctl_counts[] = { + &sc->sc_voltage_count, &sc->sc_current_count, + &sc->sc_power_count, &sc->sc_light_count }; + char **sysctl_arrays[] = { + sc->sc_voltage_sensors, sc->sc_current_sensors, + sc->sc_power_sensors, sc->sc_light_sensors }; + + for (unsigned int s = 0; s < nitems(sensor_sysctl); s++) { + int count = *sysctl_counts[s]; + if (count <= 0) + continue; + tree_node = SYSCTL_ADD_NODE(sysctlctx, + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + sensor_sysctl[s].node_name, + CTLFLAG_RD | CTLFLAG_MPSAFE, 0, + sensor_sysctl[s].node_desc); + for (i = 0; i < count; i++) { + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(tree_node), + OID_AUTO, sysctl_arrays[s][i], + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, + dev, (sensor_sysctl[s].tag << 8) | i, + asmc_sensor_sysctl, "I", + sensor_sysctl[s].leaf_desc); } - strlcat(buf, " \n", sizeof(buf)); - device_printf(dev, "%s", buf); } + return (0); +} + +/* + * Helper function to get key info by index (for sensor detection). + */ +static int +asmc_key_dump_by_index(device_t dev, int index, char *key_out, + char *type_out, uint8_t *len_out) +{ + struct asmc_softc *sc = device_get_softc(dev); + uint8_t index_buf[ASMC_KEYLEN]; + uint8_t key_buf[ASMC_KEYLEN]; + uint8_t info_buf[ASMC_KEYINFO_RESPLEN]; + int error = ENXIO, try = 0; + int i; + + if (sc->sc_is_mmio) { + error = asmc_mmio_key_getbyindex(dev, index, key_out); + if (error != 0) + return (error); + return (asmc_mmio_key_getinfo(dev, key_out, len_out, + type_out)); + } + + mtx_lock_spin(&sc->sc_mtx); + + index_buf[0] = (index >> 24) & 0xff; + index_buf[1] = (index >> 16) & 0xff; + index_buf[2] = (index >> 8) & 0xff; + index_buf[3] = index & 0xff; + +begin: + if (asmc_command(dev, ASMC_CMDGETBYINDEX)) + goto out; + + for (i = 0; i < ASMC_KEYLEN; i++) { + ASMC_DATAPORT_WRITE(sc, index_buf[i]); + if (asmc_wait(dev, ASMC_STATUS_AWAIT_DATA)) + goto out; + } + + ASMC_DATAPORT_WRITE(sc, ASMC_KEYLEN); + + for (i = 0; i < ASMC_KEYLEN; i++) { + if (asmc_wait(dev, ASMC_STATUS_DATA_READY)) + goto out; + key_buf[i] = ASMC_DATAPORT_READ(sc); + } + + if (asmc_command(dev, ASMC_CMDGETINFO)) + goto out; + + for (i = 0; i < ASMC_KEYLEN; i++) { + ASMC_DATAPORT_WRITE(sc, key_buf[i]); + if (asmc_wait(dev, ASMC_STATUS_AWAIT_DATA)) + goto out; + } + + ASMC_DATAPORT_WRITE(sc, ASMC_KEYINFO_RESPLEN); + + for (i = 0; i < ASMC_KEYINFO_RESPLEN; i++) { + if (asmc_wait(dev, ASMC_STATUS_DATA_READY)) + goto out; + info_buf[i] = ASMC_DATAPORT_READ(sc); + } + + memcpy(key_out, key_buf, ASMC_KEYLEN); + key_out[ASMC_KEYLEN] = '\0'; + *len_out = info_buf[0]; + memcpy(type_out, &info_buf[1], ASMC_TYPELEN); + type_out[ASMC_TYPELEN] = '\0'; + error = 0; + +out: + if (error) { + if (++try < ASMC_MAXRETRIES) + goto begin; + } + + mtx_unlock_spin(&sc->sc_mtx); return (error); } -#endif + +/* + * Binary search for the first key index >= prefix. + * SMC keys are sorted, so this finds the lower bound efficiently. + */ +static int +asmc_key_search(device_t dev, const char *prefix, unsigned int *idx) +{ + struct asmc_softc *sc = device_get_softc(dev); + unsigned int lo, hi, mid; + char key[ASMC_KEYLEN + 1]; + char type[ASMC_TYPELEN + 1]; + uint8_t len; + int error; + + lo = 0; + hi = sc->sc_nkeys; + while (lo < hi) { + mid = lo + (hi - lo) / 2; + error = asmc_key_dump_by_index(dev, mid, + key, type, &len); + if (error != 0) + return (error); + if (strncmp(key, prefix, ASMC_KEYLEN) < 0) + lo = mid + 1; + else + hi = mid; + } + *idx = lo; + return (0); +} static int asmc_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len) { - int i, error = -1, try = 0; struct asmc_softc *sc = device_get_softc(dev); + int i, error = -1, try = 0; + + if (sc->sc_is_mmio) + return (asmc_mmio_key_write(dev, key, buf, len)); mtx_lock_spin(&sc->sc_mtx); @@ -1164,15 +1956,15 @@ begin: error = 0; out: if (error) { - if (++try < 10) goto begin; - device_printf(dev,"%s for key %s failed %d times, giving up\n", - __func__, key, try); + if (++try < 10) + goto begin; + device_printf(dev, "%s for key %s failed %d times, giving up\n", + __func__, key, try); } mtx_unlock_spin(&sc->sc_mtx); return (error); - } /* @@ -1183,7 +1975,7 @@ asmc_fan_count(device_t dev) { uint8_t buf[1]; - if (asmc_key_read(dev, ASMC_KEY_FANCOUNT, buf, sizeof buf) != 0) + if (asmc_key_read(dev, ASMC_KEY_FANCOUNT, buf, sizeof(buf)) != 0) return (-1); return (buf[0]); @@ -1192,28 +1984,45 @@ asmc_fan_count(device_t dev) static int asmc_fan_getvalue(device_t dev, const char *key, int fan) { + struct asmc_softc *sc = device_get_softc(dev); int speed; - uint8_t buf[2]; + uint8_t buf[4]; char fankey[5]; + char type[ASMC_TYPELEN + 1]; snprintf(fankey, sizeof(fankey), key, fan); - if (asmc_key_read(dev, fankey, buf, sizeof buf) != 0) - return (-1); - speed = (buf[0] << 6) | (buf[1] >> 2); + + /* + * T2 Macs use IEEE 754 float ("flt ") for fan speeds, + * stored little-endian in the MMIO data register. + * Standard Macs use s14.2 fixed-point ("fpe2", 2 bytes). + */ + if (sc->sc_is_t2 && + asmc_key_getinfo(dev, fankey, NULL, type) == 0 && + strncmp(type, "flt ", 4) == 0) { + if (asmc_key_read(dev, fankey, buf, 4) != 0) + return (-1); + speed = (int)asmc_float_to_u32(le32dec(buf)); + } else { + if (asmc_key_read(dev, fankey, buf, 2) != 0) + return (-1); + speed = (buf[0] << 6) | (buf[1] >> 2); + } return (speed); } -static char* -asmc_fan_getstring(device_t dev, const char *key, int fan, uint8_t *buf, uint8_t buflen) +static char * +asmc_fan_getstring(device_t dev, const char *key, int fan, uint8_t *buf, + uint8_t buflen) { char fankey[5]; - char* desc; + char *desc; snprintf(fankey, sizeof(fankey), key, fan); if (asmc_key_read(dev, fankey, buf, buflen) != 0) return (NULL); - desc = buf+4; + desc = buf + 4; return (desc); } @@ -1221,17 +2030,30 @@ asmc_fan_getstring(device_t dev, const char *key, int fan, uint8_t *buf, uint8_t static int asmc_fan_setvalue(device_t dev, const char *key, int fan, int speed) { - uint8_t buf[2]; + struct asmc_softc *sc = device_get_softc(dev); + uint8_t buf[4]; char fankey[5]; - - speed *= 4; - - buf[0] = speed>>8; - buf[1] = speed; + char type[ASMC_TYPELEN + 1]; snprintf(fankey, sizeof(fankey), key, fan); - if (asmc_key_write(dev, fankey, buf, sizeof buf) < 0) - return (-1); + + if (sc->sc_is_t2 && + asmc_key_getinfo(dev, fankey, NULL, type) == 0 && + strncmp(type, "flt ", 4) == 0) { + uint32_t fval; + speed = MAX(speed, 0); + speed = MIN(speed, 65535); + fval = asmc_u32_to_float((uint32_t)speed); + le32enc(buf, fval); + if (asmc_key_write(dev, fankey, buf, 4) != 0) + return (-1); + } else { + speed *= 4; + buf[0] = speed >> 8; + buf[1] = speed; + if (asmc_key_write(dev, fankey, buf, 2) != 0) + return (-1); + } return (0); } @@ -1239,7 +2061,7 @@ asmc_fan_setvalue(device_t dev, const char *key, int fan, int speed) static int asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS) { - device_t dev = (device_t) arg1; + device_t dev = (device_t)arg1; int fan = arg2; int error; int32_t v; @@ -1254,10 +2076,10 @@ static int asmc_mb_sysctl_fanid(SYSCTL_HANDLER_ARGS) { uint8_t buf[16]; - device_t dev = (device_t) arg1; + device_t dev = (device_t)arg1; int fan = arg2; int error = true; - char* desc; + char *desc; desc = asmc_fan_getstring(dev, ASMC_KEY_FANID, fan, buf, sizeof(buf)); @@ -1270,7 +2092,7 @@ asmc_mb_sysctl_fanid(SYSCTL_HANDLER_ARGS) static int asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS) { - device_t dev = (device_t) arg1; + device_t dev = (device_t)arg1; int fan = arg2; int error; int32_t v; @@ -1284,7 +2106,7 @@ asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS) static int asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS) { - device_t dev = (device_t) arg1; + device_t dev = (device_t)arg1; int fan = arg2; int error; int32_t v; @@ -1303,7 +2125,7 @@ asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS) static int asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS) { - device_t dev = (device_t) arg1; + device_t dev = (device_t)arg1; int fan = arg2; int error; int32_t v; @@ -1322,7 +2144,7 @@ asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS) static int asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS) { - device_t dev = (device_t) arg1; + device_t dev = (device_t)arg1; int fan = arg2; int error; int32_t v; @@ -1338,6 +2160,79 @@ asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS) return (error); } +static int +asmc_mb_sysctl_fanmanual(SYSCTL_HANDLER_ARGS) +{ + device_t dev = (device_t)arg1; + struct asmc_softc *sc = device_get_softc(dev); + int fan = arg2; + int error; + int32_t v; + uint8_t buf[2]; + uint16_t val; + char fmkey[5]; + + /* + * T2 Macs use per-fan F%dMd keys (1 byte each). + * Standard Macs use FS! bitmask (2 bytes). + */ + snprintf(fmkey, sizeof(fmkey), ASMC_KEY_FANMANUAL_T2, fan); + if (sc->sc_is_t2 && + asmc_key_getinfo(dev, fmkey, NULL, NULL) == 0) { + error = asmc_key_read(dev, fmkey, buf, 1); + if (error != 0) + return (error); + v = buf[0] ? 1 : 0; + + error = sysctl_handle_int(oidp, &v, 0, req); + if (error == 0 && req->newptr != NULL) { + if (v != 0 && v != 1) + return (EINVAL); + buf[0] = (uint8_t)v; + error = asmc_key_write(dev, fmkey, buf, 1); + } + return (error); + } + + /* Read current FS! bitmask (asmc_key_read locks internally) */ + error = asmc_key_read(dev, ASMC_KEY_FANMANUAL, buf, sizeof(buf)); + if (error != 0) + return (error); + + /* Extract manual bit for this fan (big-endian) */ + val = (buf[0] << 8) | buf[1]; + v = (val >> fan) & 0x01; + + /* Let sysctl handle the value */ + error = sysctl_handle_int(oidp, &v, 0, req); + + if (error == 0 && req->newptr != NULL) { + /* Validate input (0 = auto, 1 = manual) */ + if (v != 0 && v != 1) + return (EINVAL); + /* Read-modify-write of FS! bitmask */ + error = asmc_key_read(dev, ASMC_KEY_FANMANUAL, buf, + sizeof(buf)); + if (error == 0) { + val = (buf[0] << 8) | buf[1]; + + /* Modify single bit */ + if (v) + val |= (1 << fan); /* Set to manual */ + else + val &= ~(1 << fan); /* Set to auto */ + + /* Write back */ + buf[0] = val >> 8; + buf[1] = val & 0xff; + error = asmc_key_write(dev, ASMC_KEY_FANMANUAL, buf, + sizeof(buf)); + } + } + + return (error); +} + /* * Temperature functions. */ @@ -1349,7 +2244,7 @@ asmc_temp_getvalue(device_t dev, const char *key) /* * Check for invalid temperatures. */ - if (asmc_key_read(dev, key, buf, sizeof buf) != 0) + if (asmc_key_read(dev, key, buf, sizeof(buf)) != 0) return (-1); return (buf[0]); @@ -1358,11 +2253,14 @@ asmc_temp_getvalue(device_t dev, const char *key) static int asmc_temp_sysctl(SYSCTL_HANDLER_ARGS) { - device_t dev = (device_t) arg1; + device_t dev = (device_t)arg1; struct asmc_softc *sc = device_get_softc(dev); int error, val; - val = asmc_temp_getvalue(dev, sc->sc_model->smc_temps[arg2]); + if (arg2 < 0 || arg2 >= sc->sc_temp_count) + return (EINVAL); + + val = asmc_temp_getvalue(dev, sc->sc_temp_sensors[arg2]); error = sysctl_handle_int(oidp, &val, 0, req); return (error); @@ -1382,12 +2280,12 @@ asmc_sms_read(device_t dev, const char *key, int16_t *val) case 'X': case 'Y': case 'Z': - error = asmc_key_read(dev, key, buf, sizeof buf); + error = asmc_key_read(dev, key, buf, sizeof(buf)); break; default: device_printf(dev, "%s called with invalid argument %s\n", - __func__, key); - error = 1; + __func__, key); + error = EINVAL; goto out; } *val = ((int16_t)buf[0] << 8) | buf[1]; @@ -1409,7 +2307,7 @@ static int asmc_sms_intrfast(void *arg) { uint8_t type; - device_t dev = (device_t) arg; + device_t dev = (device_t)arg; struct asmc_softc *sc = device_get_softc(dev); if (!sc->sc_sms_intr_works) return (FILTER_HANDLED); @@ -1421,6 +2319,10 @@ asmc_sms_intrfast(void *arg) sc->sc_sms_intrtype = type; asmc_sms_printintr(dev, type); + /* Don't queue SMS task for ambient light interrupts */ + if (type == ASMC_ALSL_INT2A && sc->sc_has_alsl) + return (FILTER_HANDLED); + taskqueue_enqueue(sc->sc_sms_tq, &sc->sc_sms_task); return (FILTER_HANDLED); } @@ -1443,10 +2345,9 @@ asmc_sms_printintr(device_t dev, uint8_t type) case ASMC_ALSL_INT2A: /* * This suppresses console and log messages for the ambient - * light sensor for models known to generate this interrupt. + * light sensor interrupt on models that have ALSL. */ - if (strcmp(sc->sc_model->smc_model, "MacBookPro5,5") == 0 || - strcmp(sc->sc_model->smc_model, "MacBookPro6,2") == 0) + if (sc->sc_has_alsl) break; /* FALLTHROUGH */ default: @@ -1482,13 +2383,13 @@ asmc_sms_task(void *arg, int pending) static int asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS) { - device_t dev = (device_t) arg1; + device_t dev = (device_t)arg1; int error; int16_t val; int32_t v; asmc_sms_read(dev, ASMC_KEY_SMS_X, &val); - v = (int32_t) val; + v = (int32_t)val; error = sysctl_handle_int(oidp, &v, 0, req); return (error); @@ -1497,13 +2398,13 @@ asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS) static int asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS) { - device_t dev = (device_t) arg1; + device_t dev = (device_t)arg1; int error; int16_t val; int32_t v; asmc_sms_read(dev, ASMC_KEY_SMS_Y, &val); - v = (int32_t) val; + v = (int32_t)val; error = sysctl_handle_int(oidp, &v, 0, req); return (error); @@ -1512,13 +2413,13 @@ asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS) static int asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS) { - device_t dev = (device_t) arg1; + device_t dev = (device_t)arg1; int error; int16_t val; int32_t v; asmc_sms_read(dev, ASMC_KEY_SMS_Z, &val); - v = (int32_t) val; + v = (int32_t)val; error = sysctl_handle_int(oidp, &v, 0, req); return (error); @@ -1527,12 +2428,12 @@ asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS) static int asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS) { - device_t dev = (device_t) arg1; + device_t dev = (device_t)arg1; uint8_t buf[6]; int error; int32_t v; - asmc_key_read(dev, ASMC_KEY_LIGHTLEFT, buf, sizeof buf); + asmc_key_read(dev, ASMC_KEY_LIGHTLEFT, buf, sizeof(buf)); v = buf[2]; error = sysctl_handle_int(oidp, &v, 0, req); @@ -1542,12 +2443,12 @@ asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS) static int asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS) { - device_t dev = (device_t) arg1; + device_t dev = (device_t)arg1; uint8_t buf[6]; int error; int32_t v; - asmc_key_read(dev, ASMC_KEY_LIGHTRIGHT, buf, sizeof buf); + asmc_key_read(dev, ASMC_KEY_LIGHTRIGHT, buf, sizeof(buf)); v = buf[2]; error = sysctl_handle_int(oidp, &v, 0, req); @@ -1557,7 +2458,8 @@ asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS) static int asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS) { - device_t dev = (device_t) arg1; + device_t dev = (device_t)arg1; + struct asmc_softc *sc = device_get_softc(dev); uint8_t buf[2]; int error; int v; @@ -1569,9 +2471,10 @@ asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS) if (v < 0 || v > 255) return (EINVAL); light_control = v; + sc->sc_kbd_bkl_level = v * 100 / 255; buf[0] = light_control; buf[1] = 0x00; - asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, sizeof buf); + asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, sizeof(buf)); } return (error); } @@ -1579,12 +2482,12 @@ asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS) static int asmc_mbp_sysctl_light_left_10byte(SYSCTL_HANDLER_ARGS) { - device_t dev = (device_t) arg1; + device_t dev = (device_t)arg1; uint8_t buf[10]; int error; uint32_t v; - asmc_key_read(dev, ASMC_KEY_LIGHTLEFT, buf, sizeof buf); + asmc_key_read(dev, ASMC_KEY_LIGHTLEFT, buf, sizeof(buf)); /* * This seems to be a 32 bit big endian value from buf[6] -> buf[9]. @@ -1605,3 +2508,192 @@ asmc_mbp_sysctl_light_left_10byte(SYSCTL_HANDLER_ARGS) return (error); } + +/* + * Auto power-on after AC power loss (AUPO key). + * When non-zero the machine boots automatically when AC is restored + * after an unclean power loss. Useful for always-on servers / home labs. + */ +static int +asmc_aupo_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev = (device_t)arg1; + uint8_t aupo; + int val, error; + + if (asmc_key_read(dev, ASMC_KEY_AUPO, &aupo, 1) != 0) + return (EIO); + + val = (aupo != 0) ? 1 : 0; + error = sysctl_handle_int(oidp, &val, 0, req); + if (error != 0 || req->newptr == NULL) + return (error); + + aupo = (val != 0) ? 1 : 0; + if (asmc_key_write(dev, ASMC_KEY_AUPO, &aupo, 1) != 0) + return (EIO); + + return (0); +} + +static int +asmc_backlight_update_status(device_t dev, struct backlight_props *props) +{ + struct asmc_softc *sc = device_get_softc(dev); + uint8_t buf[2]; + + sc->sc_kbd_bkl_level = props->brightness; + light_control = props->brightness * 255 / 100; + buf[0] = light_control; + buf[1] = 0x00; + asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, sizeof(buf)); + + return (0); +} + +static int +asmc_backlight_get_status(device_t dev, struct backlight_props *props) +{ + struct asmc_softc *sc = device_get_softc(dev); + + props->brightness = sc->sc_kbd_bkl_level; + props->nlevels = 0; + + return (0); +} + +static int +asmc_backlight_get_info(device_t dev, struct backlight_info *info) +{ + info->type = BACKLIGHT_TYPE_KEYBOARD; + strlcpy(info->name, "Apple MacBook Keyboard", BACKLIGHTMAXNAMELENGTH); + + return (0); +} + +static const char * +asmc_cause_str(int8_t cause, bool is_sleep) +{ + size_t i; + + for (i = 0; i < nitems(asmc_cause_table); i++) { + if (asmc_cause_table[i].code != cause) + continue; + if (is_sleep && asmc_cause_table[i].sleep_desc != NULL) + return (asmc_cause_table[i].sleep_desc); + return (asmc_cause_table[i].desc); + } + return (NULL); +} + +/* MSSD/MSSP: last shutdown/sleep cause. arg2: 0=shutdown, 1=sleep. */ +static int +asmc_cause_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev = (device_t)arg1; + bool is_sleep = (arg2 != 0); + const char *key = is_sleep ? ASMC_KEY_MSSP : ASMC_KEY_MSSD; + int8_t cause; + const char *desc; + char buf[ASMC_CAUSE_BUFLEN]; + + /* EIO: SMC I/O bus did not respond to key read. */ + if (asmc_key_read(dev, key, (uint8_t *)&cause, 1) != 0) + return (EIO); + + desc = asmc_cause_str(cause, is_sleep); + if (desc != NULL) + snprintf(buf, sizeof(buf), "%d (%s)", (int)cause, desc); + else + snprintf(buf, sizeof(buf), "%d", (int)cause); + + return (sysctl_handle_string(oidp, buf, sizeof(buf), req)); +} + +static int +asmc_msal_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev = (device_t)arg1; + uint8_t msal; + char buf[80]; + + /* EIO: SMC I/O bus did not respond to key read. */ + if (asmc_key_read(dev, ASMC_KEY_MSAL, &msal, 1) != 0) + return (EIO); + + snprintf(buf, sizeof(buf), + "0x%02x (tss=%d therm_valid=%d calib_valid=%d prochot=%d plimits=%d)", + msal, + (msal & ASMC_MSAL_TSS) != 0, + (msal & ASMC_MSAL_THERM_VALID) != 0, + (msal & ASMC_MSAL_CALIB_VALID) != 0, + (msal & ASMC_MSAL_PROCHOT) != 0, + (msal & ASMC_MSAL_PLIMITS) != 0); + + return (sysctl_handle_string(oidp, buf, sizeof(buf), req)); +} + +static int +asmc_clkt_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev = (device_t)arg1; + uint8_t buf[4]; + uint32_t secs; + + if (asmc_key_read(dev, ASMC_KEY_CLKT, buf, 4) != 0) + return (EIO); + + secs = be32dec(buf); + return (sysctl_handle_32(oidp, &secs, 0, req)); +} + +static int +asmc_msps_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev = (device_t)arg1; + uint8_t buf[2], len; + uint32_t state; + + if (asmc_key_getinfo(dev, ASMC_KEY_MSPS, &len, NULL) != 0) + return (EIO); + if (len != 1 && len != 2) + return (EIO); + + memset(buf, 0, sizeof(buf)); + if (asmc_key_read(dev, ASMC_KEY_MSPS, buf, len) != 0) + return (EIO); + + state = (len == 1) ? buf[0] : be16dec(buf); + return (sysctl_handle_32(oidp, &state, 0, req)); +} + +static int +asmc_rplt_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev = (device_t)arg1; + uint8_t buf[ASMC_RPLT_MAXLEN + 1]; + char name[ASMC_RPLT_MAXLEN + 1]; + + memset(buf, 0, sizeof(buf)); + if (asmc_key_read(dev, ASMC_KEY_RPLT, buf, ASMC_RPLT_MAXLEN) != 0) + return (EIO); + + memcpy(name, buf, ASMC_RPLT_MAXLEN); + name[ASMC_RPLT_MAXLEN] = '\0'; + + return (sysctl_handle_string(oidp, name, sizeof(name), req)); +} + +static int +asmc_rgen_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev = (device_t)arg1; + uint8_t gen; + uint32_t val; + + if (asmc_key_read(dev, ASMC_KEY_RGEN, &gen, 1) != 0) + return (EIO); + + val = gen; + return (sysctl_handle_32(oidp, &val, 0, req)); +} diff --git a/sys/dev/asmc/asmcmmio.c b/sys/dev/asmc/asmcmmio.c new file mode 100644 index 000000000000..237e8ec4ed52 --- /dev/null +++ b/sys/dev/asmc/asmcmmio.c @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +/* + * MMIO backend for Apple SMC (T2 and later Macs). + * + * T2 Macs expose the SMC via memory-mapped registers instead of I/O ports. + * Protocol: clear status, write key/cmd, poll for ready, read result. + */ + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/endian.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/module.h> +#include <sys/rman.h> +#include <sys/sysctl.h> +#include <sys/systm.h> +#include <sys/taskqueue.h> + +#include <machine/bus.h> + +#include <dev/asmc/asmcvar.h> +#include <dev/asmc/asmcmmio.h> + +/* + * Wait for MMIO status register bit 5 (ready) with exponential backoff. + * Caller must hold sc_mtx. + */ +static int +asmc_mmio_wait(device_t dev) +{ + struct asmc_softc *sc = device_get_softc(dev); + int i; + uint8_t status; + int delay_us = 10; + + for (i = 0; i < ASMC_MMIO_MAX_WAIT; i++) { + status = bus_read_1(sc->sc_iomem, ASMC_MMIO_STATUS); + if (status & ASMC_MMIO_STATUS_READY) + return (0); + DELAY(delay_us); + if (delay_us < 3200) + delay_us *= 2; + } + + return (ETIMEDOUT); +} + +int +asmc_mmio_key_read(device_t dev, const char *key, uint8_t *buf, uint8_t len) +{ + struct asmc_softc *sc = device_get_softc(dev); + uint32_t key_int; + int error, i; + uint8_t cmd_result, rlen; + + if (len > ASMC_MAXVAL) + return (EINVAL); + + mtx_lock_spin(&sc->sc_mtx); + + /* Clear status if non-zero */ + if (bus_read_1(sc->sc_iomem, ASMC_MMIO_STATUS)) + bus_write_1(sc->sc_iomem, ASMC_MMIO_STATUS, 0); + + /* Write key name as raw 4 bytes */ + memcpy(&key_int, key, 4); + bus_write_4(sc->sc_iomem, ASMC_MMIO_KEY_NAME, key_int); + + /* Write SMC ID (always 0) and command */ + bus_write_1(sc->sc_iomem, ASMC_MMIO_SMC_ID, 0); + bus_write_1(sc->sc_iomem, ASMC_MMIO_CMD, ASMC_CMDREAD); + + /* Wait for ready */ + error = asmc_mmio_wait(dev); + if (error != 0) { + uint8_t st = bus_read_1(sc->sc_iomem, ASMC_MMIO_STATUS); + uint8_t cm = bus_read_1(sc->sc_iomem, ASMC_MMIO_CMD); + mtx_unlock_spin(&sc->sc_mtx); + device_printf(dev, + "%s: timeout key %.4s status=0x%02x cmd=0x%02x\n", + __func__, key, st, cm); + return (error); + } + + /* Check command result (0 = success, 0x84 = key not found) */ + cmd_result = bus_read_1(sc->sc_iomem, ASMC_MMIO_CMD); + if (cmd_result != 0) { + mtx_unlock_spin(&sc->sc_mtx); + device_printf(dev, + "%s: key %.4s cmd error 0x%02x\n", + __func__, key, cmd_result); + return (EIO); + } + + /* Read data length and data bytes; zero-fill remainder */ + rlen = bus_read_1(sc->sc_iomem, ASMC_MMIO_DATA_LEN); + rlen = MIN(rlen, len); + for (i = rlen; i < len; i++) + buf[i] = 0; + for (i = 0; i < rlen; i++) + buf[i] = bus_read_1(sc->sc_iomem, ASMC_MMIO_DATA + i); + + mtx_unlock_spin(&sc->sc_mtx); + return (0); +} + +int +asmc_mmio_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len) +{ + struct asmc_softc *sc = device_get_softc(dev); + uint32_t key_int; + int error, i; + uint8_t cmd_result; + + if (len > ASMC_MAXVAL) + return (EINVAL); + + mtx_lock_spin(&sc->sc_mtx); + + /* Clear status */ + bus_write_1(sc->sc_iomem, ASMC_MMIO_STATUS, 0); + + /* Write data bytes first */ + for (i = 0; i < len; i++) + bus_write_1(sc->sc_iomem, ASMC_MMIO_DATA + i, buf[i]); + + /* Write key name as raw 4 bytes */ + memcpy(&key_int, key, 4); + bus_write_4(sc->sc_iomem, ASMC_MMIO_KEY_NAME, key_int); + + /* Write length, SMC ID, command */ + bus_write_1(sc->sc_iomem, ASMC_MMIO_DATA_LEN, len); + bus_write_1(sc->sc_iomem, ASMC_MMIO_SMC_ID, 0); + bus_write_1(sc->sc_iomem, ASMC_MMIO_CMD, ASMC_CMDWRITE); + + /* Wait for ready */ + error = asmc_mmio_wait(dev); + if (error != 0) { + mtx_unlock_spin(&sc->sc_mtx); + device_printf(dev, "%s: timeout writing key %.4s\n", + __func__, key); + return (error); + } + + cmd_result = bus_read_1(sc->sc_iomem, ASMC_MMIO_CMD); + mtx_unlock_spin(&sc->sc_mtx); + + return (cmd_result == 0 ? 0 : EIO); +} + +int +asmc_mmio_key_getinfo(device_t dev, const char *key, uint8_t *len, char *type) +{ + struct asmc_softc *sc = device_get_softc(dev); + uint32_t key_int; + int error, i; + uint8_t cmd_result; + + mtx_lock_spin(&sc->sc_mtx); + + /* Clear status */ + bus_write_1(sc->sc_iomem, ASMC_MMIO_STATUS, 0); + + /* Write key name as raw 4 bytes */ + memcpy(&key_int, key, 4); + bus_write_4(sc->sc_iomem, ASMC_MMIO_KEY_NAME, key_int); + + bus_write_1(sc->sc_iomem, ASMC_MMIO_SMC_ID, 0); + bus_write_1(sc->sc_iomem, ASMC_MMIO_CMD, ASMC_CMDGETINFO); + + error = asmc_mmio_wait(dev); + if (error != 0) { + mtx_unlock_spin(&sc->sc_mtx); + return (error); + } + + cmd_result = bus_read_1(sc->sc_iomem, ASMC_MMIO_CMD); + if (cmd_result != 0) { + mtx_unlock_spin(&sc->sc_mtx); + return (EIO); + } + + /* + * GETINFO response layout (MMIO): + * data[0..3] = type code (4 chars) + * data[4] = reserved + * data[5] = data length + * data[6] = flags/attributes + */ + if (type != NULL) { + for (i = 0; i < ASMC_TYPELEN; i++) + type[i] = bus_read_1(sc->sc_iomem, + ASMC_MMIO_DATA + i); + type[ASMC_TYPELEN] = '\0'; + } + if (len != NULL) + *len = bus_read_1(sc->sc_iomem, ASMC_MMIO_DATA + 5); + + mtx_unlock_spin(&sc->sc_mtx); + return (0); +} + +int +asmc_mmio_key_getbyindex(device_t dev, int index, char *key) +{ + struct asmc_softc *sc = device_get_softc(dev); + uint32_t idx_val; + int error, i; + uint8_t cmd_result; + + mtx_lock_spin(&sc->sc_mtx); + + bus_write_1(sc->sc_iomem, ASMC_MMIO_STATUS, 0); + + /* Write index as big-endian 4 bytes to key name register */ + idx_val = htobe32(index); + bus_write_4(sc->sc_iomem, ASMC_MMIO_KEY_NAME, idx_val); + + bus_write_1(sc->sc_iomem, ASMC_MMIO_SMC_ID, 0); + bus_write_1(sc->sc_iomem, ASMC_MMIO_CMD, ASMC_CMDGETBYINDEX); + + error = asmc_mmio_wait(dev); + if (error != 0) { + mtx_unlock_spin(&sc->sc_mtx); + return (error); + } + + cmd_result = bus_read_1(sc->sc_iomem, ASMC_MMIO_CMD); + if (cmd_result != 0) { + mtx_unlock_spin(&sc->sc_mtx); + return (EIO); + } + + /* Result: 4-byte key name in DATA */ + for (i = 0; i < ASMC_KEYLEN; i++) + key[i] = bus_read_1(sc->sc_iomem, ASMC_MMIO_DATA + i); + key[ASMC_KEYLEN] = '\0'; + + mtx_unlock_spin(&sc->sc_mtx); + return (0); +} + +/* + * Validate MMIO and detect T2. + * Check that status register is accessible and LDKN firmware version >= 2. + */ +int +asmc_mmio_probe(device_t dev) +{ + struct asmc_softc *sc = device_get_softc(dev); + rman_res_t size; + uint8_t status, ldkn; + int error; + + size = rman_get_size(sc->sc_iomem); + if (size < ASMC_MMIO_MIN_SIZE) { + device_printf(dev, "MMIO region too small (%jd < %d)\n", + (intmax_t)size, ASMC_MMIO_MIN_SIZE); + return (ENXIO); + } + + /* Check status register isn't stuck at 0xFF */ + status = bus_read_1(sc->sc_iomem, ASMC_MMIO_STATUS); + if (status == 0xFF) { + device_printf(dev, "MMIO status register reads 0xFF\n"); + return (ENXIO); + } + + /* + * We need the mutex initialized before calling mmio_key_read, + * but attach hasn't done it yet. Initialize early. + */ + if (!mtx_initialized(&sc->sc_mtx)) + mtx_init(&sc->sc_mtx, "asmc", NULL, MTX_SPIN); + + /* Read LDKN (firmware version) -- must be >= 2 for MMIO */ + error = asmc_mmio_key_read(dev, ASMC_KEY_LDKN, &ldkn, 1); + if (error != 0) { + device_printf(dev, "MMIO: failed to read LDKN key\n"); + return (ENXIO); + } + + if (ldkn < 2) { + device_printf(dev, "MMIO: LDKN=%d (need >= 2)\n", ldkn); + return (ENXIO); + } + + device_printf(dev, "MMIO: LDKN=%d, T2 SMC detected\n", ldkn); + sc->sc_is_t2 = 1; + + return (0); +} + +void +asmc_mmio_detach(device_t dev, struct asmc_softc *sc) +{ + + if (sc->sc_iomem != NULL) { + bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid_mem, + sc->sc_iomem); + sc->sc_iomem = NULL; + } + sc->sc_is_mmio = false; + sc->sc_is_t2 = 0; +} + +/* + * Convert IEEE 754 float (as u32) to unsigned integer. + * Kernel soft-float: extract integer part only. + * Used for T2 fan RPM values (always positive, reasonable range). + */ +uint32_t +asmc_float_to_u32(uint32_t d) +{ + int32_t exp; + uint32_t fr; + + /* Negative or zero */ + if (d == 0 || (d >> 31) != 0) + return (0); + + exp = (int32_t)((d >> 23) & 0xff) - 0x7f; + fr = d & 0x7fffff; /* 23-bit mantissa */ + + if (exp < 0) + return (0); + if (exp > 23) { + if (exp > 30) + return (0xffffffffu); + return ((1u << exp) | (fr << (exp - 23))); + } + /* Normal case: 0 <= exp <= 23 */ + return ((1u << exp) + (fr >> (23 - exp))); +} + +/* + * Convert unsigned integer to IEEE 754 float (as u32). + * Only handles values in fan RPM range (0-65535). + */ +uint32_t +asmc_u32_to_float(uint32_t d) +{ + uint32_t dc, bc, exp; + + if (d == 0) + return (0); + + /* Find highest set bit position */ + dc = d; + bc = 0; + while (dc >>= 1) + ++bc; + + bc = MIN(bc, 30); + + exp = 0x7f + bc; + + /* + * Mantissa: strip the implicit leading 1-bit and place + * remaining bits into the 23-bit mantissa field. + */ + if (bc >= 23) + return ((exp << 23) | ((d >> (bc - 23)) & 0x7fffff)); + else + return ((exp << 23) | ((d << (23 - bc)) & 0x7fffff)); +} + +/* + * Battery charge limit sysctl (T2 Macs). + * BCLM key: 1 byte, 0-100 (percentage). + */ +int +asmc_bclm_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev = (device_t)arg1; + uint8_t bclm; + int val, error; + + error = asmc_mmio_key_read(dev, ASMC_KEY_BCLM, &bclm, 1); + if (error != 0) + return (EIO); + + val = (int)bclm; + error = sysctl_handle_int(oidp, &val, 0, req); + if (error != 0 || req->newptr == NULL) + return (error); + + if (val < 0 || val > 100) + return (EINVAL); + + bclm = (uint8_t)val; + error = asmc_mmio_key_write(dev, ASMC_KEY_BCLM, &bclm, 1); + + return (error != 0 ? EIO : 0); +} diff --git a/sys/dev/asmc/asmcmmio.h b/sys/dev/asmc/asmcmmio.h new file mode 100644 index 000000000000..51e81707ece1 --- /dev/null +++ b/sys/dev/asmc/asmcmmio.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef _DEV_ASMC_ASMCMMIO_H_ +#define _DEV_ASMC_ASMCMMIO_H_ + +struct asmc_softc; + +/* + * MMIO register offsets. + */ +#define ASMC_MMIO_DATA 0x0000 +#define ASMC_MMIO_KEY_NAME 0x0078 +#define ASMC_MMIO_DATA_LEN 0x007D +#define ASMC_MMIO_SMC_ID 0x007E +#define ASMC_MMIO_CMD 0x007F +#define ASMC_MMIO_STATUS 0x4005 +#define ASMC_MMIO_MIN_SIZE 0x4006 +#define ASMC_MMIO_STATUS_READY 0x20 /* Bit 5 */ +#define ASMC_MMIO_MAX_WAIT 24 + +/* + * T2-specific keys. + */ +#define ASMC_KEY_LDKN "LDKN" /* RO; 1 byte, firmware version */ +#define ASMC_KEY_BCLM "BCLM" /* RW; 1 byte, battery charge limit 0-100 */ +#define ASMC_KEY_FANMANUAL_T2 "F%dMd" /* RW; 1 byte per fan (T2) */ + +/* + * MMIO backend functions. + */ +int asmc_mmio_probe(device_t dev); +void asmc_mmio_detach(device_t dev, struct asmc_softc *sc); +int asmc_mmio_key_read(device_t dev, const char *key, + uint8_t *buf, uint8_t len); +int asmc_mmio_key_write(device_t dev, const char *key, + uint8_t *buf, uint8_t len); +int asmc_mmio_key_getinfo(device_t dev, const char *key, + uint8_t *len, char *type); +int asmc_mmio_key_getbyindex(device_t dev, int index, char *key); + +/* + * IEEE 754 float <-> uint32 conversion for T2 fan RPM values. + */ +uint32_t asmc_float_to_u32(uint32_t d); +uint32_t asmc_u32_to_float(uint32_t d); + +/* + * T2-specific sysctls. + */ +int asmc_bclm_sysctl(SYSCTL_HANDLER_ARGS); + +#endif /* _DEV_ASMC_ASMCMMIO_H_ */ diff --git a/sys/dev/asmc/asmcvar.h b/sys/dev/asmc/asmcvar.h index d40dc1e7c8ff..940119e59774 100644 --- a/sys/dev/asmc/asmcvar.h +++ b/sys/dev/asmc/asmcvar.h @@ -28,11 +28,19 @@ */ #define ASMC_MAXFANS 6 +#define ASMC_MAXVAL 32 /* Maximum SMC value size */ +#define ASMC_KEYLEN 4 /* SMC key name length */ +#define ASMC_TYPELEN 4 /* SMC type string length */ +#define ASMC_MAX_SENSORS 64 /* Max sensors per type */ + +/* Maximum number of auto-detected temperature sensors */ +#define ASMC_TEMP_MAX 80 struct asmc_softc { device_t sc_dev; struct mtx sc_mtx; int sc_nfan; + int sc_nkeys; int16_t sms_rest_x; int16_t sms_rest_y; int16_t sms_rest_z; @@ -40,16 +48,48 @@ struct asmc_softc { struct sysctl_oid *sc_temp_tree; struct sysctl_oid *sc_sms_tree; struct sysctl_oid *sc_light_tree; - const struct asmc_model *sc_model; int sc_rid_port; int sc_rid_irq; struct resource *sc_ioport; struct resource *sc_irq; void *sc_cookie; + /* MMIO backend (T2 Macs) */ + int sc_rid_mem; + struct resource *sc_iomem; + bool sc_is_mmio; + int sc_is_t2; /* T2 fan float + per-fan manual */ int sc_sms_intrtype; struct taskqueue *sc_sms_tq; struct task sc_sms_task; uint8_t sc_sms_intr_works; + struct cdev *sc_kbd_bkl; + uint32_t sc_kbd_bkl_level; +#ifdef ASMC_DEBUG + /* Raw key access */ + struct sysctl_oid *sc_raw_tree; + char sc_rawkey[ASMC_KEYLEN + 1]; + uint8_t sc_rawval[ASMC_MAXVAL]; + uint8_t sc_rawlen; + char sc_rawtype[ASMC_TYPELEN + 1]; +#endif + /* Voltage/Current/Power/Light sensors */ + char *sc_voltage_sensors[ASMC_MAX_SENSORS]; + int sc_voltage_count; + char *sc_current_sensors[ASMC_MAX_SENSORS]; + int sc_current_count; + char *sc_power_sensors[ASMC_MAX_SENSORS]; + int sc_power_count; + char *sc_light_sensors[ASMC_MAX_SENSORS]; + int sc_light_count; + /* Auto-detected temperature sensors */ + char *sc_temp_sensors[ASMC_TEMP_MAX]; + int sc_temp_count; + /* Auto-detected capabilities */ + int sc_has_sms; + int sc_has_light; + int sc_light_len; /* ASMC_LIGHT_{SHORT,LONG}LEN */ + int sc_has_safespeed; + int sc_has_alsl; /* ALSL interrupt source */ }; /* @@ -68,6 +108,14 @@ struct asmc_softc { bus_write_1(sc->sc_ioport, 0x04, val) #define ASMC_CMDREAD 0x10 #define ASMC_CMDWRITE 0x11 +#define ASMC_CMDGETBYINDEX 0x12 +#define ASMC_CMDGETINFO 0x13 + +#define ASMC_STATUS_AWAIT_DATA 0x04 +#define ASMC_STATUS_DATA_READY 0x05 + +#define ASMC_KEYINFO_RESPLEN 6 /* getinfo: 1 len + 4 type + 1 attr */ +#define ASMC_MAXRETRIES 10 /* * Interrupt port. @@ -77,6 +125,9 @@ struct asmc_softc { /* Number of keys */ #define ASMC_NKEYS "#KEY" /* RO; 4 bytes */ +/* Query the ASMC revision */ +#define ASMC_KEY_REV "REV " /* RO: 6 bytes */ + /* * Fan control via SMC. */ @@ -84,7 +135,7 @@ struct asmc_softc { #define ASMC_KEY_FANMANUAL "FS! " /* RW; 2 bytes */ #define ASMC_KEY_FANID "F%dID" /* RO; 16 bytes */ #define ASMC_KEY_FANSPEED "F%dAc" /* RO; 2 bytes */ -#define ASMC_KEY_FANMINSPEED "F%dMn" /* RO; 2 bytes */ +#define ASMC_KEY_FANMINSPEED "F%dMn" /* RW; 2 bytes */ #define ASMC_KEY_FANMAXSPEED "F%dMx" /* RO; 2 bytes */ #define ASMC_KEY_FANSAFESPEED "F%dSf" /* RO; 2 bytes */ #define ASMC_KEY_FANTARGETSPEED "F%dTg" /* RW; 2 bytes */ @@ -115,9 +166,13 @@ struct asmc_softc { /* * Keyboard backlight. */ -#define ASMC_KEY_LIGHTLEFT "ALV0" /* RO; 6 bytes */ +#define ASMC_LIGHT_SHORTLEN 6 /* ALV0 short format */ +#define ASMC_LIGHT_LONGLEN 10 /* ALV0 long format (10-byte) */ +#define ASMC_KEY_LIGHTLEFT "ALV0" /* RO; 6 or 10 bytes */ #define ASMC_KEY_LIGHTRIGHT "ALV1" /* RO; 6 bytes */ #define ASMC_KEY_LIGHTVALUE "LKSB" /* WO; 2 bytes */ +#define ASMC_KEY_LIGHTSRC "ALSL" /* RO; ambient light source */ +#define ASMC_KEY_FANSAFESPEED0 "F0Sf" /* RO; 2 bytes */ /* * Clamshell. @@ -125,793 +180,99 @@ struct asmc_softc { #define ASMC_KEY_CLAMSHELL "MSLD" /* RO; 1 byte */ /* + * Auto power-on after AC power loss (AUPO). + * When set, the machine boots automatically when AC power is restored + * after an unclean power loss. This is NOT Wake-on-LAN. + */ +#define ASMC_KEY_AUPO "AUPO" /* RW; 1 byte */ + +/* * Interrupt keys. */ #define ASMC_KEY_INTOK "NTOK" /* WO; 1 byte */ /* - * Temperatures. - * - * First for MacBook, second for MacBook Pro, third for Intel Mac Mini, - * fourth the Mac Pro 8-core and finally the MacBook Air. + * System State Machine / diagnostics keys. * + * MSSD - Mac System Shutdown cause (last shutdown reason code) + * MSSP - Mac System SleeP cause (last sleep reason code) + * MSAL - Mac System ALarm (thermal subsystem status bitmap) + * MSPS - Mac System Power State (current power state index) + * CLKT - CLocK Time (seconds since midnight, SMC RTC) + * MSTS - Mac System sTate/Status (SSM state; 0 = S0 awake) */ -/* maximum array size for temperatures including the last NULL */ -#define ASMC_TEMP_MAX 80 -#define ASMC_MB_TEMPS { "TB0T", "TN0P", "TN1P", "Th0H", "Th1H", \ - "TM0P", NULL } -#define ASMC_MB_TEMPNAMES { "enclosure", "northbridge1", \ - "northbridge2", "heatsink1", \ - "heatsink2", "memory", } -#define ASMC_MB_TEMPDESCS { "Enclosure Bottomside", \ - "Northbridge Point 1", \ - "Northbridge Point 2", "Heatsink 1", \ - "Heatsink 2", "Memory Bank A", } - -#define ASMC_MB31_TEMPS { "TB0T", "TN0P", "Th0H", "Th1H", \ - "TM0P", NULL } - -#define ASMC_MB31_TEMPNAMES { "enclosure", "northbridge1", \ - "heatsink1", "heatsink2", \ - "memory", } - -#define ASMC_MB31_TEMPDESCS { "Enclosure Bottomside", \ - "Northbridge Point 1", \ - "Heatsink 1","Heatsink 2" \ - "Memory Bank A", } - -#define ASMC_MB71_TEMPS { "TB0T", "TB1T", "TB2T", "TC0D", "TC0P", \ - "TH0P", "TN0D", "TN0P", "TN0S", "TN1D", \ - "TN1E", "TN1F", "TN1G", "TN1S", "Th1H", \ - "Ts0P", "Ts0S", NULL } - -#define ASMC_MB71_TEMPNAMES { "enclosure_bottom0", "battery_1", "battery_2", "cpu_package", "cpu_proximity", \ - "hdd_bay", "northbridge0_diode", "northbridge0_proximity", "TN0S", "mpc_die2", \ - "TN1E", "TN1F", "TN1G", "TN1S", "heatsink1", \ - "palm_rest", "memory_proximity", } - -#define ASMC_MB71_TEMPDESCS { "Enclosure Bottom 0", "Battery 1", "Battery 2", "CPU Package", "CPU Proximity", \ - "HDD Bay", "Northbridge Diode", "Northbridge Proximity", "TN0S", "MPC Die 2", \ - "TN1E", "TN1F", "TN1G", "TN1S", "Heatsink 1", \ - "Palm Rest", "Memory Proximity", } - -#define ASMC_MBP_TEMPS { "TB0T", "Th0H", "Th1H", "Tm0P", \ - "TG0H", "TG0P", "TG0T", NULL } - -#define ASMC_MBP_TEMPNAMES { "enclosure", "heatsink1", \ - "heatsink2", "memory", "graphics", \ - "graphicssink", "unknown", } - -#define ASMC_MBP_TEMPDESCS { "Enclosure Bottomside", \ - "Heatsink 1", "Heatsink 2", \ - "Memory Controller", \ - "Graphics Chip", "Graphics Heatsink", \ - "Unknown", } - -#define ASMC_MBP4_TEMPS { "TB0T", "Th0H", "Th1H", "Th2H", "Tm0P", \ - "TG0H", "TG0D", "TC0D", "TC0P", "Ts0P", \ - "TTF0", "TW0P", NULL } - -#define ASMC_MBP4_TEMPNAMES { "enclosure", "heatsink1", "heatsink2", \ - "heatsink3", "memory", "graphicssink", \ - "graphics", "cpu", "cpu2", "unknown1", \ - "unknown2", "wireless", } - -#define ASMC_MBP4_TEMPDESCS { "Enclosure Bottomside", \ - "Main Heatsink 1", "Main Heatsink 2", \ - "Main Heatsink 3", \ - "Memory Controller", \ - "Graphics Chip Heatsink", \ - "Graphics Chip Diode", \ - "CPU Temperature Diode", "CPU Point 2", \ - "Unknown", "Unknown", \ - "Wireless Module", } - -#define ASMC_MBP51_TEMPS { "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", \ - "TC0F", "TC0P", "TG0D", "TG0F", "TG0H", \ - "TG0P", "TG0T", "TG1H", "TN0D", "TN0P", \ - "TTF0", "Th2H", "Tm0P", "Ts0P", "Ts0S", \ - NULL } - -#define ASMC_MBP51_TEMPNAMES { "enclosure_bottom_0", "enclosure_bottom_1", \ - "enclosure_bottom_2", "enclosure_bottom_3", \ - "cpu_diode", "cpu", \ - "cpu_pin", "gpu_diode", \ - "gpu", "gpu_heatsink", \ - "gpu_pin", "gpu_transistor", \ - "gpu_2_heatsink", "northbridge_diode", \ - "northbridge_pin", "unknown", \ - "heatsink_2", "memory_controller", \ - "pci_express_slot_pin", "pci_express_slot_unk" } - -#define ASMC_MBP51_TEMPDESCS { "Enclosure Bottom 0", "Enclosure Bottom 1", \ - "Enclosure Bottom 2", "Enclosure Bottom 3", \ - "CPU Diode", "CPU ???", \ - "CPU Pin", "GPU Diode", \ - "GPU ???", "GPU Heatsink", \ - "GPU Pin", "GPU Transistor", \ - "GPU 2 Heatsink", "Northbridge Diode", \ - "Northbridge Pin", "Unknown", \ - "Heatsink 2", "Memory Controller", \ - "PCI Express Slot Pin", "PCI Express Slot (unk)" } - -#define ASMC_MBP62_TEMPS { "TB0T", "TB1T", "TB2T", \ - "TC0C", "TC0D", "TC0P", \ - "TC1C", "TG0D", "TG0P", \ - "TG0T", "TMCD", "TP0P", \ - "TPCD", "Th1H", "Th2H", \ - "Tm0P", "Ts0P", "Ts0S" } - -#define ASMC_MBP62_TEMPNAMES { "enclosure_bottom_0", "enclosure_bottom_1", \ - "enclosure_bottom_2", "cpu0", \ - "cpu_diode", "cpu_proximity", \ - "cpu1", "gpu_diode", \ - "gpu_pin", "gpu_transistor", \ - "TMCD", "pch_controller_proximity", \ - "pch_die", "heatsink1", \ - "heatsink2", "memory-controller", \ - "palmrest", "memoryproximity" } - -#define ASMC_MBP62_TEMPDESCS { "Enclosure Bottom 0", "Enclosure Bottom 1", \ - "Enclosure Bottom 2", "CPU 0", \ - "CPU Diode", "CPU Proximity", \ - "CPU 1", "GPU Diode", \ - "GPU Pin", "GPU Transistor", \ - "TMCD", "PCH Controller Proximity", \ - "PCH Die", "Heat Sink 1", \ - "Heat Sink 2", "Memory Controller", \ - "Palm Rest", "Memory Proximity" } - -#define ASMC_MBP55_TEMPS { "TB0T", "TB1T", \ - "TB2T", "TB3T", \ - "TC0D", "TC0P", \ - "TN0D", "TN0P", \ - "TTF0", \ - "Th0H", "Th1H", "ThFH", \ - "Ts0P", "Ts0S", \ - NULL } - -#define ASMC_MBP55_TEMPNAMES { "enclosure_bottom_0", "enclosure_bottom_1", \ - "enclosure_bottom_2", "enclosure_bottom_3", \ - "cpu_diode", "cpu_pin", \ - "northbridge_diode", "northbridge_pin", \ - "unknown", \ - "heatsink_0", "heatsink_1", "heatsink_2", \ - "pci_express_slot_pin", "pci_express_slot_unk" } - -#define ASMC_MBP55_TEMPDESCS { "Enclosure Bottom 0", "Enclosure Bottom 1", \ - "Enclosure Bottom 2", "Enclosure Bottom 3", \ - "CPU Diode", "CPU Pin", \ - "Northbridge Diode", "Northbridge Pin", \ - "Unknown", \ - "Heatsink 0", "Heatsink 1", "Heatsink 2", \ - "PCI Express Slot Pin", "PCI Express Slot (unk)" } - -#define ASMC_MBP81_TEMPS { "TB0T", "TB1T", "TB2T", "TC0C", "TC0D", \ - "TC0E", "TC0F", "TC0P", "TC1C", "TC2C", \ - "TCFC", "TCGC", "TCSA", "TM0S", "TMBS", \ - "TP0P", "TPCD", "TW0P", "Th1H", "Ts0P", \ - "Ts0S", NULL } - -#define ASMC_MBP81_TEMPNAMES { "enclosure", "TB1T", "TB2T", "TC0C", "TC0D", \ - "TC0E", "TC0F", "TC0P", "TC1C", "TC2C", \ - "TCFC", "TCGC", "TCSA", "TM0S", "TMBS", \ - "TP0P", "TPCD", "wireless", "Th1H", "Ts0P", \ - "Ts0S" } - -#define ASMC_MBP81_TEMPDESCS { "Enclosure Bottomside", "TB1T", "TB2T", "TC0C", "TC0D", \ - "TC0E", "TC0F", "TC0P", "TC1C", "TC2C", \ - "TCFC", "TCGC", "TCSA", "TM0S", "TMBS", \ - "TP0P", "TPCD", "TW0P", "Th1H", "Ts0P", \ - "Ts0S" } - -#define ASMC_MBP82_TEMPS { "TB0T", "TB1T", "TB2T", "TC0C", "TC0D", \ - "TC0E", "TC0F", "TC0P", "TC1C", "TC2C", \ - "TC3C", "TC4C", "TCFC", "TCGC", "TCSA", \ - "TCTD", "TG0D", "TG0P", "THSP", "TM0S", \ - "TMBS", "TP0P", "TPCD", "TW0P", "Th1H", \ - "Th2H", "Tm0P", "Ts0P", "Ts0S", NULL } - -#define ASMC_MBP82_TEMPNAMES { "enclosure", "TB1T", "TB2T", "TC0C", "TC0D", \ - "TC0E", "TC0F", "TC0P", "TC1C", "TC2C", \ - "TC3C", "TC4C", "TCFC", "TCGC", "TCSA", \ - "TCTD", "graphics", "TG0P", "THSP", "TM0S", \ - "TMBS", "TP0P", "TPCD", "wireless", "Th1H", \ - "Th2H", "memory", "Ts0P", "Ts0S" } - -#define ASMC_MBP82_TEMPDESCS { "Enclosure Bottomside", "TB1T", "TB2T", "TC0C", "TC0D", \ - "TC0E", "TC0F", "TC0P", "TC1C", "TC2C", \ - "TC3C", "TC4C", "TCFC", "TCGC", "TCSA", \ - "TCTD", "TG0D", "TG0P", "THSP", "TM0S", \ - "TMBS", "TP0P", "TPCD", "TW0P", "Th1H", \ - "Th2H", "Tm0P", "Ts0P", "Ts0S" } - -#define ASMC_MBP91_TEMPS { "TA0P", "TB0T", "TB1T", "TB2T", "TC0E", \ - "TC0F", "TC0P", "TC1C", "TC2C", "TC3C", \ - "TC4C", "TCGC", "TCSA", "TCXC", "TG0D", \ - "TG0P", "TG1D", "TG1F", "TG1d", "TGTC", \ - "TGTD", "TM0P", "TM0S", "TP0P", "TPCD", \ - "Th1H", "Th2H", "Ts0P", "Ts0S", "Tsqf", NULL } - -#define ASMC_MBP91_TEMPNAMES { "ambient", "enclosure_bottom_1", "enclosure_bottom_2", \ - "enclosure_bottom_3", "cpu_die_peci_0", "cpu_die_peci_1", \ - "cpu_proximity", "cpu_core_1", "cpu_core_2", "cpu_core_3", \ - "cpu_core_4", "intel_gpu", "cpu_sys_agent", \ - "cpu_core_peci", "gpu_analog", \ - "gpu_proximity", "geforce_gpu_digital", "tg1f", \ - "gpu_2_die", "tgtc", "tgtd", "memory_proximity", \ - "mem_bank_a1", "platform_ctrl_hub", "pch_digital", \ - "main_heatsink_r", "main_heatsink_l", "palm_rest", \ - "bottom_skin", "tsqf" } - -#define ASMC_MBP91_TEMPDESCS { "Ambient", "Enclosure Bottom 1", "Enclosure Bottom 2", \ - "Enclosure Bottom 3", "CPU Die PECI 0", "CPU Die PECI 1", \ - "CPU Proximity", "CPU Core 1", "CPU Core 2", \ - "CPU Core 3", "CPU Core 4", "Intel GPU", \ - "CPU System Agent Core", "CPU Core - PECI", \ - "GPU Die - Analog", "GPU Proximity", \ - "GeForce GPU Die - Digital", "TG1F", "GPU 2 Die" \ - "TGTC", "TGTD", "Memory Proximity", \ - "Memory Bank A1", "Platform Controller Hub", "PCH Die - Digital", \ - "Main Heatsink Right", "Main Heatsink Left", "Palm Rest", \ - "Bottom Skin", "Tsqf" } - -#define ASMC_MBP92_TEMPS { "Ts0P", "Ts0S", "TA0P", "TB1T", "TB2T", \ - "TB0T", "TC1C", "TC2C", "TC0E", "TC0F", \ - "TC0J", "TC0P", "TCFC", "TCGC", "TCSA", \ - "TCTD", "TCXC", "TG1D", "TM0P", "TM0S", \ - "TPCD", NULL } - -#define ASMC_MBP92_TEMPNAMES { "Ts0P", "Ts0S", "TA0P", "TB1T", "TB2T", \ - "TB0T", "TC1C", "TC2C", "TC0E", "TC0F", \ - "TC0J", "TC0P", "TCFC", "TCGC", "TCSA", \ - "TCTD", "TCXC", "TG1D", "TM0P", "TM0S", \ - "TPCD" } - -#define ASMC_MBP92_TEMPDESCS { "Palm Rest", "Memory Proximity", "Airflow 1", \ - "Battery 1", "Battery 2", "Battery TS_MAX", \ - "CPU Core 1", "CPU Core 2", "CPU1", "CPU1", \ - "TC0J", "CPU 1 Proximity", "TCFC", \ - "PECI GPU", "PECI SA", "TCTD", "PECI CPU", \ - "GPU Die", "Memory Bank A1", "Memory Module A1", \ - "PCH Die" } - -#define ASMC_MBP112_TEMPS { "TB0T", "TB1T", "TB2T", "TBXT", "TC0E", \ - "TC0F", "TC0P", "TC1C", "TC2C", "TC3C", \ - "TC4C", "TCFC", "TCGC", "TCSA", "TCTD", \ - "TCXC", "TH0A", "TH0B", "TH0F", "TH0R", \ - "TH0V", "TH0a", "TH0b", "TH0c", "TM0P", \ - "TM0S", "TP0P", "TPCD", "TW0P", "Ta0P", \ - "TaSP", "Th1H", "Th2H", "Ts0P", "Ts0S", \ - "Ts1S", NULL } - -#define ASMC_MBP112_TEMPNAMES { "TB0T", "TB1T", "TB2T", "TBXT", "TC0E", \ - "TC0F", "TC0P", "TC1C", "TC2C", "TC3C", \ - "TC4C", "TCFC", "TCGC", "TCSA", "TCTD", \ - "TCXC", "TH0A", "TH0B", "TH0F", "TH0R", \ - "TH0V", "TH0a", "TH0b", "TH0c", "TM0P", \ - "TM0S", "TP0P", "TPCD", "TW0P", "Ta0P", \ - "TaSP", "Th1H", "Th2H", "Ts0P", "Ts0S", \ - "Ts1S" } - -#define ASMC_MBP112_TEMPDESCS { "TB0T", "TB1T", "TB2T", "TBXT", "TC0E", \ - "TC0F", "TC0P", "TC1C", "TC2C", "TC3C", \ - "TC4C", "TCFC", "TCGC", "TCSA", "TCTD", \ - "TCXC", "TH0A", "TH0B", "TH0F", "TH0R", \ - "TH0V", "TH0a", "TH0b", "TH0c", "TM0P", \ - "TM0S", "TP0P", "TPCD", "TW0P", "Ta0P", \ - "TaSP", "Th1H", "Th2H", "Ts0P", "Ts0S", \ - "Ts1S" } - -#define ASMC_MBP113_TEMPS { "TB0T", "TB1T", "TB2T", "TBXT", "TC0E", \ - "TC0F", "TC0P", "TC1C", "TC2C", "TC3C", \ - "TC4C", "TCFC", "TCGC", "TCSA", "TCTD", \ - "TCXC", "TG0D", "TG0P", "TG1D", "TG1F", \ - "TG1d", "TH0A", "TH0B", "TH0F", "TH0R", \ - "TH0V", "TH0a", "TH0b", "TH0c", "TM0P", \ - "TM0S", "TP0P", "TPCD", "TW0P", "Ta0P", \ - "TaSP", "Th1H", "Th2H", "Ts0P", "Ts0S", \ - "Ts1S", NULL } - -#define ASMC_MBP113_TEMPNAMES { "TB0T", "TB1T", "TB2T", "TBXT", "TC0E", \ - "TC0F", "TC0P", "TC1C", "TC2C", "TC3C", \ - "TC4C", "TCFC", "TCGC", "TCSA", "TCTD", \ - "TCXC", "TG0D", "TG0P", "TG1D", "TG1F", \ - "TG1d", "TH0A", "TH0B", "TH0F", "TH0R", \ - "TH0V", "TH0a", "TH0b", "TH0c", "TM0P", \ - "TM0S", "TP0P", "TPCD", "TW0P", "Ta0P", \ - "TaSP", "Th1H", "Th2H", "Ts0P", "Ts0S", \ - "Ts1S" } - -#define ASMC_MBP113_TEMPDESCS { "TB0T", "TB1T", "TB2T", "TBXT", "TC0E", \ - "TC0F", "TC0P", "TC1C", "TC2C", "TC3C", \ - "TC4C", "TCFC", "TCGC", "TCSA", "TCTD", \ - "TCXC", "TG0D", "TG0P", "TG1D", "TG1F", \ - "TG1d", "TH0A", "TH0B", "TH0F", "TH0R", \ - "TH0V", "TH0a", "TH0b", "TH0c", "TM0P", \ - "TM0S", "TP0P", "TPCD", "TW0P", "Ta0P", \ - "TaSP", "Th1H", "Th2H", "Ts0P", "Ts0S", \ - "Ts1S" } - -#define ASMC_MBP114_TEMPS { "IC0C", "ID0R", "IHDC", "IPBR", "IC0R", \ - "IO3R", "IO5R", "IM0C", "IC1C", "IC2C", \ - "IC3C", "ILDC", "IBLC", "IAPC", "IHSC", \ - "ICMC", "TC0P", "TP0P", "TM0P", \ - "Ta0P", "Th2H", "Th1H", "TW0P", "Ts0P", \ - "Ts1P", "TB0T", "TB1T", "TB2T", "TH0A", "TH0B", \ - "TC1C", "TC2C", "TC3C", "TC4C", "TCXC", \ - "TCGC", "TPCD", "TCSA", "VC0C", "VD0R", \ - "VP0R", "ALSL", "F0Ac", "F1Ac", "PCPC", \ - "PCPG", "PCPT", "PSTR", "PDTR", NULL } - -#define ASMC_MBP114_TEMPNAMES { "IC0C", "ID0R", "IHDC", "IPBR", "IC0R", \ - "IO3R", "IO5R", "IM0C", "IC1C", "IC2C", \ - "IC3C", "ILDC", "IBLC", "IAPC", "IHSC", \ - "ICMC", "TC0P", "TP0P", "TM0P", \ - "Ta0P", "Th2H", "Th1H", "TW0P", "Ts0P", \ - "Ts1P", "TB0T", "TB1T", "TB2T", "TH0A", "TH0B", \ - "TC1C", "TC2C", "TC3C", "TC4C", "TCXC", \ - "TCGC", "TPCD", "TCSA", "VC0C", "VD0R", \ - "VP0R", "ALSL", "F0Ac", "F1Ac", "PCPC", \ - "PCPG", "PCPT", "PSTR", "PDTR" } - -#define ASMC_MBP114_TEMPDESCS { "CPU High (CPU, I/O)", "DC In", "SSD", "Charger (BMON)", "CPU", \ - "Other 3.3V", "Other 5V", "Memory", "Platform Controller Hub Core", "CPU Load Current Monitor", \ - "CPU DDR", "LCD Panel", "LCD Backlight", "Airport", "Thunderbolt", \ - "S2", "CPU Proximity", "Platform Controller Hub", "Memory Proximity", "Air Flow Proximity", \ - "Left Fin Stack", "Right Fin Stack", "Airport Proximity", "Palm Rest", "Palm Rest Actuator", \ - "Battery Max", "Battery Sensor 1", "Battery Sensor 2", "SSD A", "SSD B", \ - "CPU Core 1", "CPU Core 2", "CPU Core 3", "CPU Core 4", "CPU PECI Die", \ - "Intel GPU", "Platform Controller Hub PECI", "CPU System Agent Core", "CPU VCore", "DC In", \ - "Pbus", "Ambient Light", "Leftside", "Rightside", "CPU Package Core", \ - "CPU Package GPU", "CPU Package Total", "System Total", "DC In" } +#define ASMC_KEY_CLKT "CLKT" /* RO; 4 bytes ui32 BE */ +#define ASMC_KEY_MSSD "MSSD" /* RO; 1 byte si8 */ +#define ASMC_KEY_MSSP "MSSP" /* RO; 1 byte si8 */ +#define ASMC_KEY_MSAL "MSAL" /* RO; 1 byte hex_ */ +#define ASMC_KEY_MSPS "MSPS" /* RO; 1 or 2 bytes hex_ */ +#define ASMC_KEY_MSTS "MSTS" /* RO; 1 byte ui8 */ -#define ASMC_MM_TEMPS { "TN0P", "TN1P", NULL } -#define ASMC_MM_TEMPNAMES { "northbridge1", "northbridge2" } -#define ASMC_MM_TEMPDESCS { "Northbridge Point 1", \ - "Northbridge Point 2" } - -#define ASMC_MM21_TEMPS { "TA0P", "TC0D", \ - "TC0H", "TC0P", \ - "TC1P", "TN0P", \ - "TN1P", NULL } - -#define ASMC_MM21_TEMPNAMES { "ambient_air", "cpu_die", \ - "cpu_heatsink", "cpu_proximity1", \ - "cpu_proximity2", "northbridge_proximity1", \ - "northbridge_proximity2", } - -#define ASMC_MM21_TEMPDESCS { "Ambient Air Temperature" \ - "CPU Die Core Temperature", \ - "CPU Heatsink Temperature", \ - "CPU Proximity 1 Temperature", \ - "CPU Proximity 2 Temperature", \ - "Northbridge Proximity 1 Temperature", \ - "Northbridge Proximity 2 Temperature", } - -#define ASMC_MM31_TEMPS { "TC0D", "TC0H", \ - "TC0P", "TH0P", \ - "TN0D", "TN0P", \ - "TW0P", NULL } - -#define ASMC_MM31_TEMPNAMES { "cpu0_die", "cpu0_heatsink", \ - "cpu0_proximity", "hdd_bay", \ - "northbridge_die", \ - "northbridge_proximity", \ - "wireless_proximity", } - -#define ASMC_MM31_TEMPDESCS { "CPU0 Die Core Temperature", \ - "CPU0 Heatsink Temperature", \ - "CPU0 Proximity Temperature", \ - "HDD Bay Temperature", \ - "Northbridge Die Core Temperature", \ - "Northbridge Proximity Temperature", \ - "Wireless Module Proximity Temperature", } - -#define ASMC_MM41_TEMPS { "TA0P", "TC0D", "TC0G", "TC0H", "TC0P", \ - "TC0p", "TCPG", "TH0G", "TH0P", "TH0p", \ - "TM0G", "TM0P", "TM0p", "TN0D", "TN0G", \ - "TN0P", "TN0p", "TN1D", "TN1E", "TN1F", \ - "TN1G", "TN1S", "TNPG", "TO0P", "TO0p", \ - "TW0P", "Tm0P", "Tp0C", NULL } - -#define ASMC_MM41_TEMPNAMES { "TA0P", "TC0D", "TC0G", "TC0H", "TC0P", \ - "TC0p", "TCPG", "TH0G", "TH0P", "TH0p", \ - "TM0G", "TM0P", "TM0p", "TN0D", "TN0G", \ - "TN0P", "TN0p", "TN1D", "TN1E", "TN1F", \ - "TN1G", "TN1S", "TNPG", "TO0P", "TO0p", \ - "TW0P", "Tm0P", "Tp0C", NULL } - -#define ASMC_MM41_TEMPDESCS { "TA0P", "TC0D", "TC0G", "TC0H", "TC0P", \ - "TC0p", "TCPG", "TH0G", "TH0P", "TH0p", \ - "TM0G", "TM0P", "TM0p", "TN0D", "TN0G", \ - "TN0P", "TN0p", "TN1D", "TN1E", "TN1F", \ - "TN1G", "TN1S", "TNPG", "TO0P", "TO0p", \ - "TW0P", "Tm0P", "Tp0C", NULL } - -#define ASMC_MM52_TEMPS { "TA0P", "TA1P", \ - "TC0D", "TC0P", \ - "TG0D", "TG1D", \ - "TG0P", "TG0M", \ - "TI0P", \ - "TM0S", "TMBS", \ - "TM0P", "TP0P", \ - "TPCD", "Tp0C", \ - "TW0P", NULL } - -#define ASMC_MM52_TEMPNAMES { "ambient_air_proximity", "ambient_cpu_pch_wireless_dimm", \ - "cpu_die", "cpu_proximity", \ - "gpu_diode1", "gpu_diode2", \ - "gpu_proximity", "gpu_integrated_switcher", \ - "thunderbolt_proximity", \ - "memory_slot1", "memory_slot2", \ - "memory_proximity", "pch_controller_proximity", \ - "pch_controller_die", "pwr_supply", \ - "wireless_proximity", } - -#define ASMC_MM52_TEMPDESCS { "Ambient Air Proximity Temperature", \ - "Combo Ambient CPU PCH Wireless DIMM Temperature", \ - "CPU Die Temperature", "CPU Proximity Temperature", \ - "GPU Diode 1 Temperature" , "GPU Diode 2 Temperature", \ - "GPU Proximity Temperature", \ - "Integrated Graphics/GPU Switcher Temperature", \ - "Thunderbolt Proximity Temperature", \ - "Memory Slot 1 Temperature", \ - "Memory Slot 2 Temperature", \ - "Memory Slots Proximity Temperature", \ - "Platform Controller Hub Proximity Temperature", \ - "Platform Controller Hub Die Temperature", \ - "Power Supply Temperature", \ - "Wireless Module Proximity Temperature", } - -#define ASMC_MM61_TEMPS { "TA0P", "TA1P", \ - "TC0D", "TC0G", "TC0P", "TCPG", \ - "TI0P", \ - "TM0S", "TMBS", "TM0P", \ - "TP0P", "TPCD", \ - "Tp0C", \ - "TW0P", NULL } - -#define ASMC_MM61_TEMPNAMES { "ambient_air_proximity", "ambient_cpu_pch_wireless_dimm", \ - "cpu_die", "TC0G", "cpu_proximity", "TCPG", \ - "thunderbolt_proximity", \ - "memory_slot1", "memory_slot2", "memory_proximity", \ - "pch_controller_proximity", "pch_controller_die", \ - "pwr_supply", \ - "wireless_proximity", NULL } - -#define ASMC_MM61_TEMPDESCS { "Ambient Air Proximity Temperature", \ - "Combo Ambient CPU PCH Wireless DIMM Temperature", \ - "CPU Die Temperature", \ - NULL, \ - "CPU Proximity Temperature", \ - NULL, \ - "Thunderbolt Proximity Temperature", \ - "Memory Slot 1 Temperature", \ - "Memory Slot 2 Temperature", \ - "Memory Slots Proximity Temperature", \ - "Platform Controller Hub Proximity Temperature", \ - "Platform Controller Hub Die Temperature", \ - "Power Supply Temperature", \ - "Wireless Module Proximity Temperature", NULL } - -#define ASMC_MM62_TEMPS { "TA0P", "TA1P", \ - "TC0D", "TC0G", "TC0P", "TCPG", \ - "TI0P", \ - "TM0S", "TMBS", "TM0P", \ - "TP0P", "TPCD", \ - "Tp0C", \ - "TW0P", NULL } - -#define ASMC_MM62_TEMPNAMES { "ambient_air_proximity", "ambient_cpu_pch_wireless_dimm", \ - "cpu_die", "TC0G", "cpu_proximity", "TCPG", \ - "thunderbolt_proximity", \ - "memory_slot1", "memory_slot2", "memory_proximity", \ - "pch_controller_proximity", "pch_controller_die", \ - "pwr_supply", \ - "wireless_proximity", NULL } - -#define ASMC_MM62_TEMPDESCS { "Ambient Air Proximity Temperature", \ - "Combo Ambient CPU PCH Wireless DIMM Temperature", \ - "CPU Die Temperature", \ - NULL, \ - "CPU Proximity Temperature", \ - NULL, \ - "Thunderbolt Proximity Temperature", \ - "Memory Slot 1 Temperature", \ - "Memory Slot 2 Temperature", \ - "Memory Slots Proximity Temperature", \ - "Platform Controller Hub Proximity Temperature", \ - "Platform Controller Hub Die Temperature", \ - "Power Supply Temperature", \ - "Wireless Module Proximity Temperature", NULL } - -#define ASMC_MM71_TEMPS { "TA0p", "TA1p", \ - "TA2p", "TC0c", \ - "TC0p", "TC1c", \ - "TCGc", "TCSc", \ - "TCXC", "TCXR", \ - "TM0p", "TPCd", \ - "TW0p", "Te0T", \ - "Tm0P", NULL } - -#define ASMC_MM71_TEMPNAMES { "ambient_air1", "ambient_air2", \ - "ambient_air3", "cpu_core1_peci", \ - "cpu_proximity", "cpu_core2_peci", \ - "intel_gpu", "cpu_sa_core_peci", \ - "cpu_core", "cpu_peci_dts", \ - "memory_proximity", "pch_controller_die", \ - "wireless_proximity", "thunderbolt_diode", \ - "logic_board", } - -#define ASMC_MM71_TEMPDESCS { "Ambient Air Temperature 1", \ - "Ambient Air Temperature 2", \ - "Ambient Air Temperature 3", \ - "CPU Core 1 PECI Temperature", "CPU Proximity Temperature", \ - "CPU Core 2 PECI Temperature", "Intel GPU Temperature", \ - "CPU System Agent Core PECI Temperature", \ - "CPU Core Temperature", "CPU PECI DTS Temperature", \ - "Memory Proximity Temperature", \ - "Platform Controller Hub Die Temperature", \ - "Wireless Module Proximity Temperature", \ - "Thunderbolt Diode Temperature", \ - "Logic Board temperature", } - -#define ASMC_MP1_TEMPS { "TA0P", \ - "TCAH", "TCBH", \ - "TC0P", "TC0C", "TC1C", \ - "TC2C", "TC3C", "THTG", \ - "TH0P", "TH1P", \ - "TH2P", "TH3P", \ - "TM0P", "TM1P", "TM2P", \ - "TM8P", "TM9P", "TMAP", \ - "TM0S", "TM1S", "TM2P", "TM3S", \ - "TM8S", "TM9S", "TMAS", "TMBS", \ - "TN0H", "TS0C", \ - "Tp0C", "Tp1C", "Tv0S", "Tv1S", NULL } - -#define ASMC_MP1_TEMPNAMES { "ambient", \ - "cpu_a_heatsink", "cpu_b_heatsink", \ - "cpu_a_proximity", "cpu_core0", "cpu_core1", \ - "cpu_core2", "cpu_core3", "THTG", \ - "hdd_bay0", "hdd_bay1", \ - "hdd_bay2", "hdd_bay3", \ - "memory_card_a_proximity0", \ - "memory_card_a_proximity1", \ - "memory_card_a_proximity2", \ - "memory_card_b_proximity0", \ - "memory_card_b_proximity1", \ - "memory_card_b_proximity2", \ - "memory_card_a_slot0", \ - "memory_card_a_slot1", \ - "memory_card_a_slot2", \ - "memory_card_a_slot3", \ - "memory_card_b_slot0", \ - "memory_card_b_slot1", \ - "memory_card_b_slot2", \ - "memory_card_b_slot3", \ - "mch_heatsink", "expansion_slots", \ - "power_supply_loc0", "power_supply_loc1", \ - "Tv0S", "Tv1S", } - -#define ASMC_MP1_TEMPDESCS { "Ambient Air", \ - "CPU A Heatsink", "CPU B Heatsink", \ - "CPU A Proximity", \ - "CPU Core 1", "CPU Core 2", \ - "CPU Core 3", "CPU Core 4", "THTG", \ - "Hard Drive Bay 1", "Hard Drive Bay 2", \ - "Hard Drive Bay 3", "Hard Drive Bay 4", \ - "Memory Riser A, Proximity 1", \ - "Memory Riser A, Proximity 2", \ - "Memory Riser A, Proximity 3", \ - "Memory Riser B, Proximity 1", \ - "Memory Riser B, Proximity 2", \ - "Memory Riser B, Proximity 3", \ - "Memory Riser A, Slot 1", \ - "Memory Riser A, Slot 2", \ - "Memory Riser A, Slot 3", \ - "Memory Riser A, Slot 4", \ - "Memory Riser B, Slot 1", \ - "Memory Riser B, Slot 2", \ - "Memory Riser B, Slot 3", \ - "Memory Riser B, Slot 4", \ - "MCH Heatsink", "Expansion Slots", \ - "Power Supply, Location 1", \ - "Power Supply, Location 2", \ - "Tv0S", "Tv1S", } - -#define ASMC_MP2_TEMPS { "TA0P", "TCAG", "TCAH", "TCBG", "TCBH", \ - "TC0C", "TC0D", "TC0P", "TC1C", "TC1D", \ - "TC2C", "TC2D", "TC3C", "TC3D", "THTG", \ - "TH0P", "TH1P", "TH2P", "TH3P", "TMAP", \ - "TMAS", "TMBS", "TM0P", "TM0S", "TM1P", \ - "TM1S", "TM2P", "TM2S", "TM3S", "TM8P", \ - "TM8S", "TM9P", "TM9S", "TN0H", "TS0C", \ - NULL } - -#define ASMC_MP2_TEMPNAMES { "TA0P", "TCAG", "TCAH", "TCBG", "TCBH", \ - "TC0C", "TC0D", "TC0P", "TC1C", "TC1D", \ - "TC2C", "TC2D", "TC3C", "TC3D", "THTG", \ - "TH0P", "TH1P", "TH2P", "TH3P", "TMAP", \ - "TMAS", "TMBS", "TM0P", "TM0S", "TM1P", \ - "TM1S", "TM2P", "TM2S", "TM3S", "TM8P", \ - "TM8S", "TM9P", "TM9S", "TN0H", "TS0C", } - -#define ASMC_MP2_TEMPDESCS { "TA0P", "TCAG", "TCAH", "TCBG", "TCBH", \ - "TC0C", "TC0D", "TC0P", "TC1C", "TC1D", \ - "TC2C", "TC2D", "TC3C", "TC3D", "THTG", \ - "TH0P", "TH1P", "TH2P", "TH3P", "TMAP", \ - "TMAS", "TMBS", "TM0P", "TM0S", "TM1P", \ - "TM1S", "TM2P", "TM2S", "TM3S", "TM8P", \ - "TM8S", "TM9P", "TM9S", "TN0H", "TS0C", } - -#define ASMC_MP5_TEMPS { "TA0P", "TCAC", "TCAD", "TCAG", "TCAH", \ - "TCAS", "TCBC", "TCBD", "TCBG", "TCBH", \ - "TCBS", "TH1F", "TH1P", "TH1V", "TH2F", \ - "TH2P", "TH2V", "TH3F", "TH3P", "TH3V", \ - "TH4F", "TH4P", "TH4V", "THPS", "THTG", \ - "TM1P", "TM2P", "TM2V", "TM3P", "TM3V", \ - "TM4P", "TM5P", "TM6P", "TM6V", "TM7P", \ - "TM7V", "TM8P", "TM8V", "TM9V", "TMA1", \ - "TMA2", "TMA3", "TMA4", "TMB1", "TMB2", \ - "TMB3", "TMB4", "TMHS", "TMLS", "TMPS", \ - "TMPV", "TMTG", "TN0D", "TN0H", "TNTG", \ - "Te1F", "Te1P", "Te1S", "Te2F", "Te2S", \ - "Te3F", "Te3S", "Te4F", "Te4S", "Te5F", \ - "Te5S", "TeGG", "TeGP", "TeRG", "TeRP", \ - "TeRV", "Tp0C", "Tp1C", "TpPS", "TpTG", \ - NULL } - -#define ASMC_MP5_TEMPNAMES { "ambient", "TCAC", "TCAD", "TCAG", "TCAH", \ - "TCAS", "TCBC", "TCBD", "TCBG", "TCBH", \ - "TCBS", "TH1F", "TH1P", "TH1V", "TH2F", \ - "TH2P", "TH2V", "TH3F", "TH3P", "TH3V", \ - "TH4F", "TH4P", "TH4V", "THPS", "THTG", \ - "TM1P", "TM2P", "TM2V", "TM3P", "TM3V", \ - "TM4P", "TM5P", "TM6P", "TM6V", "TM7P", \ - "TM7V", "TM8P", "TM8V", "TM9V", "ram_a1", \ - "ram_a2", "ram_a3", "ram_a4", "ram_b1", "ram_b2", \ - "ram_b3", "ram_b4", "TMHS", "TMLS", "TMPS", \ - "TMPV", "TMTG", "TN0D", "TN0H", "TNTG", \ - "Te1F", "Te1P", "Te1S", "Te2F", "Te2S", \ - "Te3F", "Te3S", "Te4F", "Te4S", "Te5F", \ - "Te5S", "TeGG", "TeGP", "TeRG", "TeRP", \ - "TeRV", "Tp0C", "Tp1C", "TpPS", "TpTG", } - -#define ASMC_MP5_TEMPDESCS { "TA0P", "TCAC", "TCAD", "TCAG", "TCAH", \ - "TCAS", "TCBC", "TCBD", "TCBG", "TCBH", \ - "TCBS", "TH1F", "TH1P", "TH1V", "TH2F", \ - "TH2P", "TH2V", "TH3F", "TH3P", "TH3V", \ - "TH4F", "TH4P", "TH4V", "THPS", "THTG", \ - "TM1P", "TM2P", "TM2V", "TM3P", "TM3V", \ - "TM4P", "TM5P", "TM6P", "TM6V", "TM7P", \ - "TM7V", "TM8P", "TM8V", "TM9V", "TMA1", \ - "TMA2", "TMA3", "TMA4", "TMB1", "TMB2", \ - "TMB3", "TMB4", "TMHS", "TMLS", "TMPS", \ - "TMPV", "TMTG", "TN0D", "TN0H", "TNTG", \ - "Te1F", "Te1P", "Te1S", "Te2F", "Te2S", \ - "Te3F", "Te3S", "Te4F", "Te4S", "Te5F", \ - "Te5S", "TeGG", "TeGP", "TeRG", "TeRP", \ - "TeRV", "Tp0C", "Tp1C", "TpPS", "TpTG", } - -#define ASMC_MP6_TEMPS { "TA0P", "TA1P", "TC0P", "TG0D", "TG0P", \ - "TG1D", "TG1P", "TM0P", "TM1P", NULL } - -#define ASMC_MP6_TEMPNAMES { "ambient_air_1", "ambient_air_2", \ - "cpu_proximity", "gpu_diode_1", \ - "gpu_proximity_1", "gpu_diode_2", \ - "gpu_proximity_2", "mem_proximity_1", \ - "mem_proximity_2" } - -#define ASMC_MP6_TEMPDESCS { "Ambient Air 1", "Ambient Air 2", \ - "CPU Proximity", "GPU Diode 1", \ - "GPU Proximity 1", "GPU Diode 2", \ - "GPU Proximity 2", "Memory Bank A", \ - "Memory Bank B" } - -#define ASMC_MBA_TEMPS { "TB0T", NULL } -#define ASMC_MBA_TEMPNAMES { "enclosure" } -#define ASMC_MBA_TEMPDESCS { "Enclosure Bottom" } - -#define ASMC_MBA3_TEMPS { "TB0T", "TB1T", "TB2T", \ - "TC0D", "TC0E", "TC0P", NULL } - -#define ASMC_MBA3_TEMPNAMES { "enclosure", "TB1T", "TB2T", \ - "TC0D", "TC0E", "TC0P" } - -#define ASMC_MBA3_TEMPDESCS { "Enclosure Bottom", "TB1T", "TB2T", \ - "TC0D", "TC0E", "TC0P" } - -#define ASMC_MBA4_TEMPS { "TB0T", "TB1T", "TB2T", "TC0C", \ - "TC0D", "TC0E", "TC0F", "TC0P", \ - "TC1C", "TC2C", "TCGC", "TCSA", \ - "TH0F", "TH0J", "TH0O", "TH0o", \ - "TM0P", "TPCD", "Ta0P", "Th1H", \ - "Tm0P", "Tm1P", "Ts0P", "Ts0S", \ - NULL } - -#define ASMC_MBA4_TEMPNAMES { "TB0T", "TB1T", "TB2T", "TC0C", \ - "TC0D", "TC0E", "TC0F", "TC0P", \ - "TC1C", "TC2C", "TCGC", "TCSA", \ - "TH0F", "TH0J", "TH0O", "TH0o", \ - "TM0P", "TPCD", "Ta0P", "Th1H", \ - "Tm0P", "Tm1P", "Ts0P", "Ts0S", \ - NULL } - -#define ASMC_MBA4_TEMPDESCS { "TB0T", "TB1T", "TB2T", "TC0C", \ - "TC0D", "TC0E", "TC0F", "TC0P", \ - "TC1C", "TC2C", "TCGC", "TCSA", \ - "TH0F", "TH0J", "TH0O", "TH0o", \ - "TM0P", "TPCD", "Ta0P", "Th1H", \ - "Tm0P", "Tm1P", "Ts0P", "Ts0S", \ - NULL } - -#define ASMC_MBA5_TEMPS { "TB0T", "TB1T", "TB2T", "TC0C", \ - "TC0D", "TC0E", "TC0F", "TC0P", \ - "TC1C", "TC2C", "TCGC", "TCSA", \ - "TCXC", "THSP", "TM0P", "TPCD", \ - "Ta0P", "Th1H", "Tm0P", "Tm1P", \ - "Ts0P", "Ts0S", NULL } - -#define ASMC_MBA5_TEMPNAMES { "enclosure1", "enclosure2", "enclosure3", "TC0C", \ - "cpudiode", "cputemp1", "cputemp2", "cpuproximity", \ - "cpucore1", "cpucore2", "cpupeci", "pecisa", \ - "TCXC", "THSP", "memorybank", "pchdie", \ - "Ta0P", "heatpipe", "mainboardproximity1", "mainboardproximity2", \ - "palmrest", "memoryproximity" } - -#define ASMC_MBA5_TEMPDESCS { "Enclosure Bottom 1", "Enclosure Bottom 2", "Enclosure Bottom 3", "TC0C",\ - "CPU Diode", "CPU Temp 1", "CPU Temp 2", "CPU Proximity", \ - "CPU Core 1", "CPU Core 2", "CPU Peci Core", "PECI SA", \ - "TCXC", "THSP", "Memory Bank A", "PCH Die", \ - "Ta0P", "Heatpipe", "Mainboard Proximity 1", "Mainboard Proximity 2", \ - "Palm Rest", "Memory Proximity" } +#define ASMC_MSAL_TSS 0x01 /* TSS running */ +#define ASMC_MSAL_THERM_VALID 0x02 /* thermal calibration valid */ +#define ASMC_MSAL_CALIB_VALID 0x08 /* I/P calibration valid */ +#define ASMC_MSAL_PROCHOT 0x40 /* Prochot enabled */ +#define ASMC_MSAL_PLIMITS 0x80 /* plimits enabled */ /* - * TODO: validate the temp zones for MBA 6.x ! + * Board identity keys. + * + * RPlt - board platform identifier (e.g. "Mac-XXXX") + * RGEN - security chip generation (3 = T2) */ -#define ASMC_MBA6_TEMPS { "TB0T", "TB1T", "TB2T", \ - "TC0E", "TC0F", "TC0P", \ - "TC1C", "TC2C", "TCGC", "TCSA", \ - "TCXC", "THSP", "TM0P", "TPCD", \ - "Ta0P", "Th1H", "Tm0P", \ - "Ts0P", "Ts0S", NULL } - -#define ASMC_MBA6_TEMPNAMES { "enclosure1", "enclosure2", "enclosure3", \ - "cputemp1", "cputemp2", "cpuproximity", \ - "cpucore1", "cpucore2", "cpupeci", "pecisa", \ - "TCXC", "THSP", "memorybank", "pchdie", \ - "Ta0P", "heatpipe", "mainboardproximity1", \ - "palmrest", "memoryproximity" } - -#define ASMC_MBA6_TEMPDESCS { "Enclosure Bottom 1", "Enclosure Bottom 2", "Enclosure Bottom 3", \ - "CPU Temp 1", "CPU Temp 2", "CPU Proximity", \ - "CPU Core 1", "CPU Core 2", "CPU Peci Core", "PECI SA", \ - "TCXC", "THSP", "Memory Bank A", "PCH Die", \ - "Ta0P", "Heatpipe", "Mainboard Proximity 1", \ - "Palm Rest", "Memory Proximity" } +#define ASMC_KEY_RPLT "RPlt" /* RO; 8 bytes ch8* */ +#define ASMC_KEY_RGEN "RGEN" /* RO; 1 byte ui8 */ +#define ASMC_RPLT_MAXLEN 8 /* RPlt key data length */ +/* + * Shutdown/sleep cause codes (MSSD/MSSP values). + * + * Positive values indicate normal operation; negative values indicate + * fault conditions. + * Sources: Apple SMC firmware, VirtualSMC docs, macOS + * pmset/powermetrics output. + */ +struct asmc_cause { + int8_t code; + const char *desc; /* shutdown description */ + const char *sleep_desc; /* sleep description, or NULL if same */ +}; -#define ASMC_MBA7_TEMPS { "TB0T", "TB1T", "TB2T", \ - "TC0E", "TC0F", "TC0P", \ - "TC1C", "TC2C", \ - "TCGC", "TCSA", "TCXC", \ - "THSP", "TM0P", "TPCD", \ - "TW0P" "Ta0P", "Th1H", \ - "Tm0P", "Ts0P", "Ts0S", NULL } - -#define ASMC_MBA7_TEMPNAMES { "enclosure1", "enclosure2", "enclosure3", \ - "cputemp1", "cputemp2", "cpuproximity", \ - "cpucore1", "cpucore2", \ - "pecigpu", "pecisa", "pecicpu", \ - "thunderboltproximity", "memorybank", "pchdie", \ - "wirelessproximity", "airflowproximity", "heatpipe", \ - "mainboardproximity", "palmrest", "memoryproximity" } +#define ASMC_CAUSE_BUFLEN 48 /* "-128 (temperature-overlimit-timeout)\0" */ -#define ASMC_MBA7_TEMPDESCS { "Enclosure Bottom 1", "Enclosure Bottom 2", "Enclosure Bottom 3", \ - "CPU Temp 1", "CPU Temp 2", "CPU Proximity", \ - "CPU Core 1", "CPU Core 2", \ - "PECI GPU", "PECI SA", "PECI CPU", \ - "Thunderbolt Proximity", "Memory Bank A", "PCH Die", \ - "Wireless Proximity", "Airflow Proxmity", "Heatpipe", \ - "Mainboard Proximity", "Palm Rest", "Memory Proximity" } +static const struct asmc_cause asmc_cause_table[] = { + { 5, "good-shutdown", "good-sleep" }, + { 3, "power-button", NULL }, + { 2, "low-battery-sleep", NULL }, + { 1, "overtemp-sleep", NULL }, + { 0, "initial", NULL }, + { -1, "health-check", NULL }, + { -2, "power-supply", NULL }, + { -3, "temperature-multisleep", NULL }, + { -4, "sensor-fan", NULL }, + { -30, "temperature-overlimit-timeout",NULL }, + { -40, "pswr-smrst", NULL }, /* power supply watchdog reset */ + { -50, "unmapped", NULL }, + { -60, "low-battery", NULL }, + { -61, "ninja-shutdown", NULL }, /* sudden unexpected power loss */ + { -62, "ninja-restart", NULL }, + { -70, "palm-rest-temperature", NULL }, + { -71, "sodimm-temperature", NULL }, + { -72, "heatpipe-temperature", NULL }, + { -74, "battery-temperature", NULL }, + { -75, "adapter-timeout", NULL }, + { -77, "manual-temperature", NULL }, + { -78, "adapter-current", NULL }, + { -79, "battery-current", NULL }, + { -82, "skin-temperature", NULL }, + { -83, "skin-temperature-sensors", NULL }, + { -84, "backup-temperature", NULL }, + { -86, "cpu-proximity-temperature", NULL }, + { -95, "cpu-temperature", NULL }, + { -100, "power-supply-temperature", NULL }, + { -101, "lcd-temperature", NULL }, + { -102, "rsm-power-fail", NULL }, + { -103, "battery-cuv", NULL }, /* cell under-voltage */ + { -127, "pmu-forced-shutdown", NULL }, + { -128, "unknown", NULL }, +}; |
