diff options
Diffstat (limited to 'sys/dev/asmc')
| -rw-r--r-- | sys/dev/asmc/asmc.c | 718 | ||||
| -rw-r--r-- | sys/dev/asmc/asmcvar.h | 49 |
2 files changed, 723 insertions, 44 deletions
diff --git a/sys/dev/asmc/asmc.c b/sys/dev/asmc/asmc.c index 4a6734e22786..0a701e6fd663 100644 --- a/sys/dev/asmc/asmc.c +++ b/sys/dev/asmc/asmc.c @@ -123,6 +123,22 @@ static int asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS); static int asmc_mbp_sysctl_light_left_10byte(SYSCTL_HANDLER_ARGS); static int asmc_wol_sysctl(SYSCTL_HANDLER_ARGS); +static int asmc_key_getinfo(device_t, const char *, uint8_t *, char *); + +#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 + +/* 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 *); + struct asmc_model { const char *smc_model; /* smbios.system.product env var. */ const char *smc_desc; /* driver description */ @@ -334,6 +350,12 @@ static const struct asmc_model asmc_models[] = { ASMC_MBP115_TEMPS, ASMC_MBP115_TEMPNAMES, ASMC_MBP115_TEMPDESCS }, + { + "MacBookPro13,1", "Apple SMC MacBook Pro Retina Core i5 (late 2016, 13-inch)", + ASMC_SMS_FUNCS_DISABLED, ASMC_FAN_FUNCS2, ASMC_LIGHT_FUNCS, + ASMC_MBP131_TEMPS, ASMC_MBP131_TEMPNAMES, ASMC_MBP131_TEMPDESCS + }, + /* The Mac Mini has no SMS */ { "Macmini1,1", "Apple SMC Mac Mini", @@ -828,6 +850,43 @@ asmc_attach(device_t dev) } } +#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 + if (model->smc_sms_x == NULL) goto nosms; @@ -911,6 +970,16 @@ asmc_detach(device_t dev) if (sc->sc_kbd_bkl != NULL) backlight_destroy(sc->sc_kbd_bkl); + /* 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); @@ -1082,6 +1151,12 @@ nosms: sc->sc_nkeys = 0; } + /* + * Auto-detect and register voltage/current/power/ambient sensors. + * Scans SMC keys and creates sysctls for detected sensors. + */ + asmc_detect_sensors(dev); + out_err: #ifdef ASMC_DEBUG asmc_dumpall(dev); @@ -1202,10 +1277,10 @@ 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; @@ -1214,40 +1289,40 @@ asmc_key_dump(device_t dev, int number) 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); } @@ -1255,42 +1330,599 @@ begin: error = 0; out: if (error) { - if (++try < 10) + if (++try < ASMC_MAXRETRIES) goto begin; - device_printf(dev, "%s for key %s failed %d times, giving up\n", - __func__, key, try); - mtx_unlock_spin(&sc->sc_mtx); + device_printf(dev, + "%s for key %d failed %d times, giving up\n", + __func__, number, try); + } + mtx_unlock_spin(&sc->sc_mtx); + + if (error) + return (error); + + maxlen = type[0]; + type[0] = ' '; + type[5] = '\0'; + if (maxlen > sizeof(v)) + maxlen = sizeof(v); + + memset(v, 0, sizeof(v)); + error = asmc_key_read(dev, key, v, maxlen); + if (error) + return (error); + + device_printf(dev, "key %d: %s, type%s (len %d), data", + number, key, type, maxlen); + for (i = 0; i < maxlen; i++) + printf(" %02x", v[i]); + printf("\n"); + + 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; + + 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=AUPO # 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 + +/* + * Convert signed fixed-point SMC values to milli-units. + * Format "spXY" means signed with X integer bits and Y fraction bits. + */ +static int +asmc_sp78_to_milli(const uint8_t *buf) +{ + int16_t val = (int16_t)be16dec(buf); + + return ((int)val * 1000) / 256; +} + +static int +asmc_sp87_to_milli(const uint8_t *buf) +{ + int16_t val = (int16_t)be16dec(buf); + + return ((int)val * 1000) / 128; +} + +static int +asmc_sp4b_to_milli(const uint8_t *buf) +{ + int16_t val = (int16_t)be16dec(buf); + + return ((int)val * 1000) / 2048; +} + +static int +asmc_sp5a_to_milli(const uint8_t *buf) +{ + int16_t val = (int16_t)be16dec(buf); + + return ((int)val * 1000) / 1024; +} + +static int +asmc_sp69_to_milli(const uint8_t *buf) +{ + int16_t val = (int16_t)be16dec(buf); + + return ((int)val * 1000) / 512; +} + +static int +asmc_sp96_to_milli(const uint8_t *buf) +{ + int16_t val = (int16_t)be16dec(buf); + + return ((int)val * 1000) / 64; +} + +static int +asmc_sp2d_to_milli(const uint8_t *buf) +{ + int16_t val = (int16_t)be16dec(buf); + + return ((int)val * 1000) / 8192; +} + +static bool +asmc_sensor_type_supported(const char *type) +{ + + return (strncmp(type, "sp78", 4) == 0 || + strncmp(type, "sp87", 4) == 0 || + strncmp(type, "sp4b", 4) == 0 || + strncmp(type, "sp5a", 4) == 0 || + strncmp(type, "sp69", 4) == 0 || + strncmp(type, "sp96", 4) == 0 || + strncmp(type, "sp2d", 4) == 0 || + strncmp(type, "ui16", 4) == 0); +} + +/* + * 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, + "%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 (strncmp(type, "sp78", 4) == 0) { + *millivalue = asmc_sp78_to_milli(buf); + } else if (strncmp(type, "sp87", 4) == 0) { + *millivalue = asmc_sp87_to_milli(buf); + } else if (strncmp(type, "sp4b", 4) == 0) { + *millivalue = asmc_sp4b_to_milli(buf); + } else if (strncmp(type, "sp5a", 4) == 0) { + *millivalue = asmc_sp5a_to_milli(buf); + } else if (strncmp(type, "sp69", 4) == 0) { + *millivalue = asmc_sp69_to_milli(buf); + } else if (strncmp(type, "sp96", 4) == 0) { + *millivalue = asmc_sp96_to_milli(buf); + } else if (strncmp(type, "sp2d", 4) == 0) { + *millivalue = asmc_sp2d_to_milli(buf); + } else if (strncmp(type, "ui16", 4) == 0) { + *millivalue = be16dec(buf); } 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)) { + if (bootverbose) device_printf(dev, - "WARNING: cropping maxlen from %d to %zu\n", maxlen, - sizeof(v)); - maxlen = sizeof(v); + "%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)); +} + +/* + * Detect and register voltage/current/power/ambient sensors. + * Scans all SMC keys and identifies sensor keys by prefix. + * Returns 0 on success, -1 on error. + */ +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 nkeys; + unsigned int i; + int error; + char *sensor_key; + + /* Initialize counts */ + sc->sc_voltage_count = 0; + sc->sc_current_count = 0; + sc->sc_power_count = 0; + sc->sc_light_count = 0; + + if (sc->sc_nkeys == 0) + return (0); + nkeys = sc->sc_nkeys; + + /* Scan all keys for voltage/current/power/ambient light sensors */ + for (i = 0; i < nkeys; i++) { + /* Get key name by index */ + error = asmc_key_dump_by_index(dev, i, key, type, &len); + if (error != 0) + continue; + if (!asmc_sensor_type_supported(type)) + continue; + + /* Voltage sensors (VC*, VD*, VG*, VP*, VI*) */ + if (key[0] == 'V' && (key[1] == 'C' || key[1] == 'D' || + key[1] == 'G' || key[1] == 'P' || key[1] == 'I') && + len == 2) { + if (sc->sc_voltage_count >= ASMC_MAX_SENSORS) + continue; + sensor_key = malloc(ASMC_KEYLEN + 1, + M_DEVBUF, M_WAITOK); + memcpy(sensor_key, key, ASMC_KEYLEN + 1); + sc->sc_voltage_sensors[sc->sc_voltage_count++] = + sensor_key; + } else if (key[0] == 'I' && (key[1] == 'C' || + key[1] == 'D' || key[1] == 'G' || key[1] == 'M' || + key[1] == 'N' || key[1] == 'O' || key[1] == 'H' || + key[1] == 'P' || key[1] == 'B' || key[1] == 'A' || + key[1] == 'L') && len == 2) { + /* Current sensors */ + if (sc->sc_current_count >= ASMC_MAX_SENSORS) + continue; + sensor_key = malloc(ASMC_KEYLEN + 1, + M_DEVBUF, M_WAITOK); + memcpy(sensor_key, key, ASMC_KEYLEN + 1); + sc->sc_current_sensors[sc->sc_current_count++] = + sensor_key; + } else if (key[0] == 'P' && (key[1] == 'C' || + key[1] == 'D' || key[1] == 'N' || key[1] == 'S' || + key[1] == 'T' || key[1] == 'H' || key[1] == 'F' || + key[1] == 'Z' || key[1] == 'z') && len == 2) { + /* Power sensors */ + if (sc->sc_power_count >= ASMC_MAX_SENSORS) + continue; + sensor_key = malloc(ASMC_KEYLEN + 1, + M_DEVBUF, M_WAITOK); + memcpy(sensor_key, key, ASMC_KEYLEN + 1); + sc->sc_power_sensors[sc->sc_power_count++] = + sensor_key; + } else if (key[0] == 'A' && key[1] == 'L' && + (key[2] == 'V' || key[2] == 'S') && len == 2) { + /* Ambient light sensors */ + if (sc->sc_light_count >= ASMC_MAX_SENSORS) + continue; + 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; } - for (i = 0; i < sizeof(v); i++) { - v[i] = 0; + } + + if (bootverbose) + device_printf(dev, + "detected %d voltage, %d current, " + "%d power, %d light sensors\n", + 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); + + /* Voltage sensors */ + if (sc->sc_voltage_count > 0) { + tree_node = SYSCTL_ADD_NODE(sysctlctx, + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "voltage", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Voltage sensors (millivolts)"); + + for (i = 0; i < sc->sc_voltage_count; i++) { + SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(tree_node), + OID_AUTO, sc->sc_voltage_sensors[i], + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, + dev, ('V' << 8) | i, asmc_sensor_sysctl, "I", + "Voltage sensor (millivolts)"); } - 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)); + } + + /* Current sensors */ + if (sc->sc_current_count > 0) { + tree_node = SYSCTL_ADD_NODE(sysctlctx, + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "current", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Current sensors (milliamps)"); + + for (i = 0; i < sc->sc_current_count; i++) { + SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(tree_node), + OID_AUTO, sc->sc_current_sensors[i], + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, + dev, ('I' << 8) | i, asmc_sensor_sysctl, "I", + "Current sensor (milliamps)"); } - strlcat(buf, " \n", sizeof(buf)); - device_printf(dev, "%s", buf); } + /* Power sensors */ + if (sc->sc_power_count > 0) { + tree_node = SYSCTL_ADD_NODE(sysctlctx, + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "power", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Power sensors (milliwatts)"); + + for (i = 0; i < sc->sc_power_count; i++) { + SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(tree_node), + OID_AUTO, sc->sc_power_sensors[i], + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, + dev, ('P' << 8) | i, asmc_sensor_sysctl, "I", + "Power sensor (milliwatts)"); + } + } + + /* Ambient light sensors */ + if (sc->sc_light_count > 0) { + tree_node = SYSCTL_ADD_NODE(sysctlctx, + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "ambient", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Ambient light sensors"); + + for (i = 0; i < sc->sc_light_count; i++) { + SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(tree_node), + OID_AUTO, sc->sc_light_sensors[i], + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, + dev, ('L' << 8) | i, asmc_sensor_sysctl, "I", + "Light sensor value"); + } + } + + 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; + + 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 static int asmc_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len) diff --git a/sys/dev/asmc/asmcvar.h b/sys/dev/asmc/asmcvar.h index cfc176559ed9..43f679f3fef0 100644 --- a/sys/dev/asmc/asmcvar.h +++ b/sys/dev/asmc/asmcvar.h @@ -28,6 +28,10 @@ */ #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 */ struct asmc_softc { device_t sc_dev; @@ -53,6 +57,23 @@ struct asmc_softc { 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; }; /* @@ -71,6 +92,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. @@ -90,7 +119,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 */ @@ -545,6 +574,24 @@ struct asmc_softc { "Pbus", "Ambient Light", "Leftside", "Rightside", "CPU Package Core", \ "CPU Package GPU", "CPU Package Total", "System Total", "DC In" } +#define ASMC_MBP131_TEMPS { "TB0T", "TB1T", "TB2T", "TC0F", \ + "TC0P", "TC1C", "TC2C", "TCGC", \ + "TCSA", "TCXC", "Th1H", "TM0P", \ + "TPCD", "Ts0P", "Ts0S", "TaLC", \ + "Ts1P", NULL } + +#define ASMC_MBP131_TEMPNAMES { "battery", "battery_1", "battery_2", "cpu_die_peci", \ + "cpu_proximity", "cpu_core_1", "cpu_core_2", "intel_gpu", \ + "cpu_sys_agent", "cpu_core_peci", "right_fin_stack", "memory_proximity", \ + "platform_ctrl_hub", "trackpad", "bottom_skin", "air_flow", \ + "trackpad_act" } + +#define ASMC_MBP131_TEMPDESCS { "Battery", "Battery Sensor 1", "Battery Sensor 2", "CPU Die (PECI)", \ + "CPU Proximity", "CPU Core 1", "CPU Core 2", "Intel GPU", \ + "CPU System Agent Core (PECI)", "CPU Core (PECI)", "Right Fin Stack", "DDR3 Proximity", \ + "Platform Controller Hub Die", "Trackpad", "Bottom Skin", "Air Flow", \ + "Trackpad Actuator" } + #define ASMC_MM_TEMPS { "TN0P", "TN1P", NULL } #define ASMC_MM_TEMPNAMES { "northbridge1", "northbridge2" } #define ASMC_MM_TEMPDESCS { "Northbridge Point 1", \ |
