Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F154299787
D55807.id173555.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
15 KB
Referenced Files
None
Subscribers
None
D55807.id173555.diff
View Options
diff --git a/sys/dev/asmc/asmc.c b/sys/dev/asmc/asmc.c
--- a/sys/dev/asmc/asmc.c
+++ b/sys/dev/asmc/asmc.c
@@ -111,6 +111,14 @@
static int asmc_mbp_sysctl_light_left_10byte(SYSCTL_HANDLER_ARGS);
static int asmc_wol_sysctl(SYSCTL_HANDLER_ARGS);
+/* Key info and sensor support */
+static int asmc_key_getinfo(device_t, const char *, uint8_t *, char *);
+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 */
@@ -881,6 +889,16 @@
{
struct asmc_softc *sc = device_get_softc(dev);
+ /* 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);
@@ -1052,6 +1070,12 @@
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);
@@ -1262,6 +1286,480 @@
}
#endif
+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[6];
+ int i, error = -1, try = 0;
+
+ mtx_lock_spin(&sc->sc_mtx);
+
+begin:
+ if (asmc_command(dev, 0x13))
+ goto out;
+
+ for (i = 0; i < 4; i++) {
+ ASMC_DATAPORT_WRITE(sc, key[i]);
+ if (asmc_wait(dev, 0x04))
+ goto out;
+ }
+
+ ASMC_DATAPORT_WRITE(sc, 6);
+
+ for (i = 0; i < 6; i++) {
+ if (asmc_wait(dev, 0x05))
+ goto out;
+ info[i] = ASMC_DATAPORT_READ(sc);
+ }
+
+ error = 0;
+out:
+ if (error && ++try < 10)
+ goto begin;
+ mtx_unlock_spin(&sc->sc_mtx);
+
+ if (error == 0) {
+ if (len != NULL)
+ *len = info[0];
+ if (type != NULL) {
+ for (i = 0; i < 4; i++)
+ type[i] = info[i + 1];
+ type[4] = '\0';
+ }
+ }
+ return (error);
+}
+
+/*
+ * Convert sp78 (signed 8.8 fixed-point) to millivalue.
+ * Used for: temperatures (TC0P, etc.), some current sensors (IC0C), power sensors (PC*)
+ * Returns value in milli-units (e.g., millidegrees C, milliamps, milliwatts).
+ */
+static int
+asmc_sp78_to_milli(uint8_t *buf)
+{
+ int16_t val = (buf[0] << 8) | buf[1];
+ /* Convert 8.8 fixed-point to milli-units: multiply by 1000, divide by 256 */
+ return ((int)val * 1000) / 256;
+}
+
+/*
+ * Convert sp87 (signed 8.7 fixed-point) to millivalue.
+ * Used for: IG0C (GPU current)
+ */
+static int
+asmc_sp87_to_milli(uint8_t *buf)
+{
+ int16_t val = (buf[0] << 8) | buf[1];
+ return ((int)val * 1000) / 128;
+}
+
+/*
+ * Convert sp4b (signed 4.11 fixed-point) to millivalue.
+ * Used for: IC0R, VD2R, VG0F, VG1C, IG1C (voltage/current sensors)
+ */
+static int
+asmc_sp4b_to_milli(uint8_t *buf)
+{
+ int16_t val = (buf[0] << 8) | buf[1];
+ return ((int)val * 1000) / 2048;
+}
+
+/*
+ * Convert sp5a (signed 5.10 fixed-point) to millivalue.
+ * Used for: IC0M, IC1C, IC2C, ID0R, ID1R, IM0C, IM0R, IN1R (current sensors)
+ */
+static int
+asmc_sp5a_to_milli(uint8_t *buf)
+{
+ int16_t val = (buf[0] << 8) | buf[1];
+ return ((int)val * 1000) / 1024;
+}
+
+/*
+ * Convert sp69 (signed 6.9 fixed-point) to millivalue.
+ * Used for: IN0R (current sensor)
+ */
+static int
+asmc_sp69_to_milli(uint8_t *buf)
+{
+ int16_t val = (buf[0] << 8) | buf[1];
+ return ((int)val * 1000) / 512;
+}
+
+/*
+ * Convert sp96 (signed 9.6 fixed-point) to millivalue.
+ * Used for: PC* (power sensors on some models)
+ */
+static int
+asmc_sp96_to_milli(uint8_t *buf)
+{
+ int16_t val = (buf[0] << 8) | buf[1];
+ return ((int)val * 1000) / 64;
+}
+
+/*
+ * Convert sp2d (signed 2.13 fixed-point) to millivalue.
+ * Used for: VC0C, VC0M (voltage sensors)
+ */
+static int
+asmc_sp2d_to_milli(uint8_t *buf)
+{
+ int16_t val = (buf[0] << 8) | buf[1];
+ return ((int)val * 1000) / 8192;
+}
+
+static int
+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.
+ * Returns 0 on success, -1 on error.
+ */
+static int
+asmc_sensor_read(device_t dev, const char *key, int *millivalue)
+{
+ uint8_t buf[2];
+ char type[5];
+ uint8_t len;
+ int error;
+
+ /* Get key type and length */
+ error = asmc_key_getinfo(dev, key, &len, type);
+ if (error != 0)
+ return (-1);
+
+ /* Verify 2-byte value */
+ if (len != 2) {
+ if (bootverbose)
+ device_printf(dev, "asmc_sensor_read: key %s has unexpected length %d\n",
+ key, len);
+ return (-1);
+ }
+
+ /* Read raw value */
+ error = asmc_key_read(dev, key, buf, sizeof(buf));
+ if (error != 0)
+ return (-1);
+
+ /* Convert based on type */
+ 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) {
+ /* Raw 16-bit unsigned value (no conversion) */
+ *millivalue = (buf[0] << 8) | buf[1];
+ } else {
+ if (bootverbose)
+ device_printf(dev, "asmc_sensor_read: unknown type '%s' for key %s\n",
+ type, key);
+ return (-1);
+ }
+
+ 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);
+
+ /* Read and convert sensor value */
+ if (asmc_sensor_read(dev, key, &val) != 0)
+ return (EIO);
+
+ error = sysctl_handle_int(oidp, &val, 0, req);
+ return (error);
+}
+
+/*
+ * 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[5];
+ char type[5];
+ uint8_t len;
+ u_int nkeys;
+ u_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;
+
+ /* Check for 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(5, M_DEVBUF, M_WAITOK);
+ if (sensor_key == NULL)
+ continue;
+ memcpy(sensor_key, key, 5);
+ sc->sc_voltage_sensors[sc->sc_voltage_count++] = sensor_key;
+ }
+ /* Check for current sensors (I{C,D,G,M,N,O,H,P,B,A,L}*) */
+ 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) {
+ if (sc->sc_current_count >= ASMC_MAX_SENSORS)
+ continue;
+ sensor_key = malloc(5, M_DEVBUF, M_WAITOK);
+ if (sensor_key == NULL)
+ continue;
+ memcpy(sensor_key, key, 5);
+ sc->sc_current_sensors[sc->sc_current_count++] = sensor_key;
+ }
+ /* Check for power sensors (P{C,D,N,S,T,H,F,Z,z}*) */
+ 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) {
+ if (sc->sc_power_count >= ASMC_MAX_SENSORS)
+ continue;
+ sensor_key = malloc(5, M_DEVBUF, M_WAITOK);
+ if (sensor_key == NULL)
+ continue;
+ memcpy(sensor_key, key, 5);
+ sc->sc_power_sensors[sc->sc_power_count++] = sensor_key;
+ }
+ /* Check for ambient light sensors (ALV*, ALS*) */
+ else if (key[0] == 'A' && key[1] == 'L' && (key[2] == 'V' || key[2] == 'S') && len == 2) {
+ if (sc->sc_light_count >= ASMC_MAX_SENSORS)
+ continue;
+ sensor_key = malloc(5, M_DEVBUF, M_WAITOK);
+ if (sensor_key == NULL)
+ continue;
+ memcpy(sensor_key, key, 5);
+ sc->sc_light_sensors[sc->sc_light_count++] = sensor_key;
+ }
+ }
+
+ if (bootverbose)
+ device_printf(dev, "detected %d voltage, %d current, %d power, %d ambient 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)");
+ }
+ }
+
+ /* 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)");
+ }
+ }
+
+ /* 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).
+ * Returns 0 on success, -1 on error.
+ */
+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[4];
+ uint8_t key_buf[4];
+ uint8_t type_buf[6];
+ int error = -1, 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:
+ /* Read key name by index (command 0x12) */
+ if (asmc_command(dev, 0x12))
+ goto out;
+
+ for (i = 0; i < 4; i++) {
+ ASMC_DATAPORT_WRITE(sc, index_buf[i]);
+ if (asmc_wait(dev, 0x04))
+ goto out;
+ }
+
+ ASMC_DATAPORT_WRITE(sc, 4);
+
+ for (i = 0; i < 4; i++) {
+ if (asmc_wait(dev, 0x05))
+ goto out;
+ key_buf[i] = ASMC_DATAPORT_READ(sc);
+ }
+
+ /* Get key info (type and length) using command 0x13 */
+ if (asmc_command(dev, 0x13))
+ goto out;
+
+ for (i = 0; i < 4; i++) {
+ ASMC_DATAPORT_WRITE(sc, key_buf[i]);
+ if (asmc_wait(dev, 0x04))
+ goto out;
+ }
+
+ ASMC_DATAPORT_WRITE(sc, 6);
+
+ for (i = 0; i < 6; i++) {
+ if (asmc_wait(dev, 0x05))
+ goto out;
+ type_buf[i] = ASMC_DATAPORT_READ(sc);
+ }
+
+ /* Success - copy results */
+ memcpy(key_out, key_buf, 4);
+ key_out[4] = '\0';
+ *len_out = type_buf[0]; /* Length is first byte */
+ memcpy(type_out, &type_buf[1], 4);
+ type_out[4] = '\0';
+ error = 0;
+
+out:
+ if (error) {
+ if (++try < 10) goto begin;
+ }
+
+ mtx_unlock_spin(&sc->sc_mtx);
+ return (error);
+}
+
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
--- a/sys/dev/asmc/asmcvar.h
+++ b/sys/dev/asmc/asmcvar.h
@@ -28,6 +28,8 @@
*/
#define ASMC_MAXFANS 6
+#define ASMC_MAXVAL 32 /* Maximum SMC value size */
+#define ASMC_MAX_SENSORS 64 /* Max sensors per type */
struct asmc_softc {
device_t sc_dev;
@@ -51,6 +53,15 @@
struct taskqueue *sc_sms_tq;
struct task sc_sms_task;
uint8_t sc_sms_intr_works;
+ /* 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;
};
/*
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Apr 28, 4:56 PM (31 m, 23 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
32281849
Default Alt Text
D55807.id173555.diff (15 KB)
Attached To
Mode
D55807: asmc: add automatic voltage/current/power/ambient sensor detection
Attached
Detach File
Event Timeline
Log In to Comment