Module Name: src
Committed By: riastradh
Date: Tue Apr 1 17:49:05 UTC 2014
Modified Files:
src/sys/dev/ic: apple_smc.c apple_smc_fan.c apple_smc_temp.c
apple_smcvar.h
Log Message:
Polish the Apple SMC code with commentary and little fixes.
To generate a diff of this commit:
cvs rdiff -u -r1.3 -r1.4 src/sys/dev/ic/apple_smc.c \
src/sys/dev/ic/apple_smc_fan.c src/sys/dev/ic/apple_smc_temp.c \
src/sys/dev/ic/apple_smcvar.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/dev/ic/apple_smc.c
diff -u src/sys/dev/ic/apple_smc.c:1.3 src/sys/dev/ic/apple_smc.c:1.4
--- src/sys/dev/ic/apple_smc.c:1.3 Tue Apr 1 17:48:52 2014
+++ src/sys/dev/ic/apple_smc.c Tue Apr 1 17:49:05 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: apple_smc.c,v 1.3 2014/04/01 17:48:52 riastradh Exp $ */
+/* $NetBSD: apple_smc.c,v 1.4 2014/04/01 17:49:05 riastradh Exp $ */
/*
* Apple System Management Controller
@@ -34,7 +34,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: apple_smc.c,v 1.3 2014/04/01 17:48:52 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: apple_smc.c,v 1.4 2014/04/01 17:49:05 riastradh Exp $");
#include <sys/types.h>
#include <sys/param.h>
@@ -75,12 +75,12 @@ void
apple_smc_attach(struct apple_smc_tag *smc)
{
- mutex_init(&smc->smc_lock, MUTEX_DEFAULT, IPL_NONE);
-
+ mutex_init(&smc->smc_io_lock, MUTEX_DEFAULT, IPL_NONE);
#if 0 /* XXX sysctl */
apple_smc_sysctl_setup(smc);
#endif
+ /* Attach any children. */
(void)apple_smc_rescan(smc, APPLE_SMC_BUS, NULL);
}
@@ -89,6 +89,7 @@ apple_smc_detach(struct apple_smc_tag *s
{
int error;
+ /* Fail if we can't detach all our children. */
error = config_detach_children(smc->smc_dev, flags);
if (error)
return error;
@@ -96,6 +97,7 @@ apple_smc_detach(struct apple_smc_tag *s
#if 0 /* XXX sysctl */
sysctl_teardown(&smc->smc_log);
#endif
+ mutex_destroy(&smc->smc_io_lock);
return 0;
}
@@ -105,6 +107,7 @@ apple_smc_rescan(struct apple_smc_tag *s
const int *locators)
{
+ /* Let autoconf(9) do the work of finding new children. */
(void)config_search_loc(&apple_smc_search, smc->smc_dev, APPLE_SMC_BUS,
locators, smc);
return 0;
@@ -154,6 +157,7 @@ void
apple_smc_child_detached(struct apple_smc_tag *smc __unused,
device_t child __unused)
{
+ /* We keep no books about our children. */
}
static uint8_t
@@ -181,8 +185,12 @@ apple_smc_read_data(struct apple_smc_tag
uint8_t status;
unsigned int i;
- KASSERT(mutex_owned(&smc->smc_lock));
+ KASSERT(mutex_owned(&smc->smc_io_lock));
+ /*
+ * Wait until the status register says there's data to read and
+ * read it.
+ */
for (i = 0; i < 100; i++) {
status = apple_smc_bus_read_1(smc, APPLE_SMC_CSR);
if (status & APPLE_SMC_STATUS_READ_READY) {
@@ -201,14 +209,20 @@ apple_smc_write(struct apple_smc_tag *sm
uint8_t status;
unsigned int i;
- KASSERT(mutex_owned(&smc->smc_lock));
+ KASSERT(mutex_owned(&smc->smc_io_lock));
+ /*
+ * Write the byte and then wait until the status register says
+ * it has been accepted.
+ */
apple_smc_bus_write_1(smc, reg, byte);
for (i = 0; i < 100; i++) {
status = apple_smc_bus_read_1(smc, APPLE_SMC_CSR);
if (status & APPLE_SMC_STATUS_WRITE_ACCEPTED)
return 0;
DELAY(100);
+
+ /* Write again if it hasn't been acknowledged at all. */
if (!(status & APPLE_SMC_STATUS_WRITE_PENDING))
apple_smc_bus_write_1(smc, reg, byte);
}
@@ -237,18 +251,21 @@ apple_smc_begin(struct apple_smc_tag *sm
unsigned int i;
int error;
- KASSERT(mutex_owned(&smc->smc_lock));
+ KASSERT(mutex_owned(&smc->smc_io_lock));
+ /* Write the command first. */
error = apple_smc_write_cmd(smc, cmd);
if (error)
return error;
+ /* Write the key next. */
for (i = 0; i < 4; i++) {
error = apple_smc_write_data(smc, key[i]);
if (error)
return error;
}
+ /* Finally, report how many bytes of data we want to send/receive. */
error = apple_smc_write_data(smc, size);
if (error)
return error;
@@ -264,11 +281,15 @@ apple_smc_input(struct apple_smc_tag *sm
uint8_t i;
int error;
- mutex_enter(&smc->smc_lock);
+ /* Grab the SMC I/O lock. */
+ mutex_enter(&smc->smc_io_lock);
+
+ /* Initiate the command with this key. */
error = apple_smc_begin(smc, cmd, key, size);
if (error)
goto out;
+ /* Read each byte of data in sequence. */
for (i = 0; i < size; i++) {
error = apple_smc_read_data(smc, &bytes[i]);
if (error)
@@ -278,8 +299,7 @@ apple_smc_input(struct apple_smc_tag *sm
/* Success! */
error = 0;
-out:
- mutex_exit(&smc->smc_lock);
+out: mutex_exit(&smc->smc_io_lock);
return error;
}
@@ -291,19 +311,25 @@ apple_smc_output(struct apple_smc_tag *s
uint8_t i;
int error;
- mutex_enter(&smc->smc_lock);
+ /* Grab the SMC I/O lock. */
+ mutex_enter(&smc->smc_io_lock);
+
+ /* Initiate the command with this key. */
error = apple_smc_begin(smc, cmd, key, size);
if (error)
goto out;
+ /* Write each byte of data in sequence. */
for (i = 0; i < size; i++) {
error = apple_smc_write_data(smc, bytes[i]);
if (error)
goto out;
}
-out:
- mutex_exit(&smc->smc_lock);
+ /* Success! */
+ error = 0;
+
+out: mutex_exit(&smc->smc_io_lock);
return error;
}
@@ -344,27 +370,34 @@ apple_smc_nth_key(struct apple_smc_tag *
struct apple_smc_key *key;
int error;
+ /* Paranoia: type must be NULL or 4 non-null characters long. */
if ((type != NULL) && (strlen(type) != 4))
return EINVAL;
+ /* Create a new key. XXX Consider caching these. */
key = kmem_alloc(sizeof(*key), KM_SLEEP);
#ifdef DIAGNOSTIC
key->ask_smc = smc;
#endif
+ /* Ask the SMC what the name of the key by this number is. */
index_be.u32 = htobe32(index);
error = apple_smc_input(smc, APPLE_SMC_CMD_NTH_KEY, index_be.name,
key->ask_name, 4);
if (error)
goto fail;
+
+ /* Null-terminate the name. */
key->ask_name[4] = '\0';
+ /* Ask the SMC for a description of this key by name. */
CTASSERT(sizeof(key->ask_desc) == 6);
error = apple_smc_input(smc, APPLE_SMC_CMD_KEY_DESC, key->ask_name,
&key->ask_desc, 6);
if (error)
goto fail;
+ /* Fail with EINVAL if the types don't match. */
if ((type != NULL) && (0 != memcmp(key->ask_desc.asd_type, type, 4))) {
error = EINVAL;
goto fail;
@@ -374,8 +407,7 @@ apple_smc_nth_key(struct apple_smc_tag *
*keyp = key;
return 0;
-fail:
- kmem_free(key, sizeof(*key));
+fail: kmem_free(key, sizeof(*key));
return error;
}
@@ -386,36 +418,43 @@ apple_smc_named_key(struct apple_smc_tag
struct apple_smc_key *key;
int error;
+ /* Paranoia: name must be 4 non-null characters long. */
KASSERT(name != NULL);
if (strlen(name) != 4)
return EINVAL;
+ /* Paranoia: type must be NULL or 4 non-null characters long. */
if ((type != NULL) && (strlen(type) != 4))
return EINVAL;
+ /* Create a new key. XXX Consider caching these. */
key = kmem_alloc(sizeof(*key), KM_SLEEP);
#ifdef DIAGNOSTIC
key->ask_smc = smc;
#endif
+
+ /* Use the specified name, and make sure it's null-terminated. */
(void)memcpy(key->ask_name, name, 4);
key->ask_name[4] = '\0';
+ /* Ask the SMC for a description of this key by name. */
CTASSERT(sizeof(key->ask_desc) == 6);
error = apple_smc_input(smc, APPLE_SMC_CMD_KEY_DESC, key->ask_name,
&key->ask_desc, 6);
if (error)
goto fail;
+ /* Fail with EINVAL if the types don't match. */
if ((type != NULL) && (0 != memcmp(key->ask_desc.asd_type, type, 4))) {
error = EINVAL;
goto fail;
}
+ /* Success! */
*keyp = key;
return 0;
-fail:
- kmem_free(key, sizeof(*key));
+fail: kmem_free(key, sizeof(*key));
return error;
}
@@ -424,11 +463,14 @@ apple_smc_release_key(struct apple_smc_t
{
#ifdef DIAGNOSTIC
+ /* Make sure the caller didn't mix up SMC tags. */
if (key->ask_smc != smc)
aprint_error_dev(smc->smc_dev,
"releasing key with wrong tag: %p != %p",
smc, key->ask_smc);
#endif
+
+ /* Nothing to do but free the key's memory. */
kmem_free(key, sizeof(*key));
}
@@ -440,6 +482,7 @@ apple_smc_key_search(struct apple_smc_ta
uint32_t start = 0, end = apple_smc_nkeys(smc), median;
int error;
+ /* Do a binary search on the SMC's key space. */
while (start < end) {
median = (start + ((end - start) / 2));
error = apple_smc_nth_key(smc, median, NULL, &key);
@@ -453,6 +496,7 @@ apple_smc_key_search(struct apple_smc_ta
apple_smc_release_key(smc, key);
}
+ /* Success! */
*result = start;
return 0;
}
@@ -462,11 +506,15 @@ apple_smc_read_key(struct apple_smc_tag
void *buffer, uint8_t size)
{
+ /* Refuse if software and hardware disagree on the key's size. */
if (key->ask_desc.asd_size != size)
return EINVAL;
+
+ /* Refuse if the hardware doesn't want us to read it. */
if (!(key->ask_desc.asd_flags & APPLE_SMC_FLAG_READ))
return EACCES;
+ /* Looks good. Try reading it from the hardware. */
return apple_smc_input(smc, APPLE_SMC_CMD_READ_KEY, key->ask_name,
buffer, size);
}
@@ -486,11 +534,15 @@ apple_smc_read_key_2(struct apple_smc_ta
uint16_t be;
int error;
+ /* Read a big-endian quantity from the hardware. */
error = apple_smc_read_key(smc, key, &be, 2);
if (error)
return error;
+ /* Convert it to host order. */
*p = be16toh(be);
+
+ /* Success! */
return 0;
}
@@ -501,11 +553,15 @@ apple_smc_read_key_4(struct apple_smc_ta
uint32_t be;
int error;
+ /* Read a big-endian quantity from the hardware. */
error = apple_smc_read_key(smc, key, &be, 4);
if (error)
return error;
+ /* Convert it to host order. */
*p = be32toh(be);
+
+ /* Success! */
return 0;
}
@@ -514,11 +570,15 @@ apple_smc_write_key(struct apple_smc_tag
const void *buffer, uint8_t size)
{
+ /* Refuse if software and hardware disagree on the key's size. */
if (key->ask_desc.asd_size != size)
return EINVAL;
+
+ /* Refuse if the hardware doesn't want us to write it. */
if (!(key->ask_desc.asd_flags & APPLE_SMC_FLAG_WRITE))
return EACCES;
+ /* Looks good. Try writing it to the hardware. */
return apple_smc_output(smc, APPLE_SMC_CMD_WRITE_KEY, key->ask_name,
buffer, size);
}
@@ -535,8 +595,10 @@ int
apple_smc_write_key_2(struct apple_smc_tag *smc,
const struct apple_smc_key *key, uint16_t v)
{
+ /* Convert the quantity from host to big-endian byte order. */
const uint16_t v_be = htobe16(v);
+ /* Write the big-endian quantity to the hardware. */
return apple_smc_write_key(smc, key, &v_be, 2);
}
@@ -544,8 +606,10 @@ int
apple_smc_write_key_4(struct apple_smc_tag *smc,
const struct apple_smc_key *key, uint32_t v)
{
+ /* Convert the quantity from host to big-endian byte order. */
const uint16_t v_be = htobe32(v);
+ /* Write the big-endian quantity to the hardware. */
return apple_smc_write_key(smc, key, &v_be, 4);
}
@@ -555,6 +619,7 @@ static int
apple_smc_modcmd(modcmd_t cmd, void *data __unused)
{
+ /* Nothing to do for now to set up or tear down the module. */
switch (cmd) {
case MODULE_CMD_INIT:
return 0;
Index: src/sys/dev/ic/apple_smc_fan.c
diff -u src/sys/dev/ic/apple_smc_fan.c:1.3 src/sys/dev/ic/apple_smc_fan.c:1.4
--- src/sys/dev/ic/apple_smc_fan.c:1.3 Tue Apr 1 17:48:52 2014
+++ src/sys/dev/ic/apple_smc_fan.c Tue Apr 1 17:49:05 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: apple_smc_fan.c,v 1.3 2014/04/01 17:48:52 riastradh Exp $ */
+/* $NetBSD: apple_smc_fan.c,v 1.4 2014/04/01 17:49:05 riastradh Exp $ */
/*
* Apple System Management Controller: Fans
@@ -34,7 +34,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: apple_smc_fan.c,v 1.3 2014/04/01 17:48:52 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: apple_smc_fan.c,v 1.4 2014/04/01 17:49:05 riastradh Exp $");
#include <sys/types.h>
#include <sys/param.h>
@@ -116,15 +116,18 @@ apple_smc_fan_match(device_t parent, cfd
int rv = 0;
int error;
+ /* Find how to find how many fans there are. */
error = apple_smc_named_key(asa->asa_smc, APPLE_SMC_NFANS_KEY,
APPLE_SMC_TYPE_UINT8, &nfans_key);
if (error)
goto out0;
+ /* Find how many fans there are. */
error = apple_smc_read_key_1(asa->asa_smc, nfans_key, &nfans);
if (error)
goto out1;
+ /* Attach only if there's at least one fan. */
if (nfans > 0)
rv = 1;
@@ -140,26 +143,37 @@ apple_smc_fan_attach(device_t parent, de
struct apple_smc_key *nfans_key;
int error;
+ /* Identify ourselves. */
aprint_normal(": Apple SMC fan sensors\n");
+ /* Initialize the softc. */
sc->sc_dev = self;
sc->sc_smc = asa->asa_smc;
+ /* Find how to find how many fans there are. */
error = apple_smc_named_key(sc->sc_smc, APPLE_SMC_NFANS_KEY,
APPLE_SMC_TYPE_UINT8, &nfans_key);
if (error)
goto out0;
+ /* Find how many fans there are. */
error = apple_smc_read_key_1(sc->sc_smc, nfans_key, &sc->sc_nfans);
if (error)
goto out1;
- if (sc->sc_nfans == 0) { /* XXX */
+ /*
+ * There should be at least one, but just in case the hardware
+ * changed its mind in the interim...
+ */
+ if (sc->sc_nfans == 0) {
aprint_error_dev(self, "no fans\n");
goto out1;
}
- /* Must fit in a single decimal digit. */
+ /*
+ * The number of fans must fit in a single decimal digit for
+ * the names of the fan keys; see the fan_sensor table above.
+ */
if (sc->sc_nfans >= 10) {
aprint_error_dev(self, "too many fans: %"PRIu8"\n",
sc->sc_nfans);
@@ -167,15 +181,18 @@ apple_smc_fan_attach(device_t parent, de
}
#if 0 /* XXX sysctl */
+ /* Set up the sysctl tree for controlling the fans. */
error = apple_smc_fan_sysctl_setup(sc);
if (error)
goto fail0;
#endif
+ /* Attach the sensors to sysmon_envsys. */
error = apple_smc_fan_attach_sensors(sc);
if (error)
goto fail1;
+ /* Success! */
goto out1;
#if 0
@@ -198,6 +215,7 @@ apple_smc_fan_detach(device_t self, int
{
struct apple_smc_fan_softc *sc = device_private(self);
+ /* If we registered with sysmon_envsys, unregister. */
if (sc->sc_sme != NULL) {
sysmon_envsys_unregister(sc->sc_sme);
sc->sc_sme = NULL;
@@ -206,6 +224,7 @@ apple_smc_fan_detach(device_t self, int
KASSERT(sc->sc_nfans > 0);
KASSERT(sc->sc_nfans < 10);
+ /* Release the keys and free the memory for fan records. */
apple_smc_fan_release_keys(sc);
kmem_free(sc->sc_fans,
(sizeof(sc->sc_fans[0]) * sc->sc_nfans));
@@ -214,6 +233,7 @@ apple_smc_fan_detach(device_t self, int
}
#if 0 /* XXX sysctl */
+ /* Tear down all the sysctl knobs we set up. */
sysctl_teardown(&sc->sc_sysctl_log);
#endif
@@ -230,20 +250,26 @@ apple_smc_fan_attach_sensors(struct appl
char name[sizeof(fan_desc.fd_name) + 1];
int error;
+ /* Create a sysmon_envsys record, but don't register it yet. */
sc->sc_sme = sysmon_envsys_create();
sc->sc_sme->sme_name = device_xname(sc->sc_dev);
sc->sc_sme->sme_cookie = sc;
sc->sc_sme->sme_refresh = apple_smc_fan_refresh;
+ /* Create an array of fan sensor records. */
CTASSERT(10 <= (SIZE_MAX / sizeof(sc->sc_fans[0])));
sc->sc_fans = kmem_zalloc((sizeof(sc->sc_fans[0]) * sc->sc_nfans),
KM_SLEEP);
+ /* Find all the fans. */
for (fan = 0; fan < sc->sc_nfans; fan++) {
- (void)snprintf(fan_desc_key_name, sizeof fan_desc_key_name,
+
+ /* Format the name of the key for the fan's description. */
+ (void)snprintf(fan_desc_key_name, sizeof(fan_desc_key_name),
"F%"PRIu8"ID", fan);
KASSERT(4 == strlen(fan_desc_key_name));
+ /* Look up the key for this fan's description. */
error = apple_smc_named_key(sc->sc_smc, fan_desc_key_name,
APPLE_SMC_TYPE_FANDESC, &fan_desc_key);
if (error) {
@@ -253,8 +279,9 @@ apple_smc_fan_attach_sensors(struct appl
continue;
}
+ /* Read the description of this fan. */
error = apple_smc_read_key(sc->sc_smc, fan_desc_key, &fan_desc,
- sizeof fan_desc);
+ sizeof(fan_desc));
if (error) {
aprint_error_dev(sc->sc_dev,
"error identifying fan %"PRIu8": %d\n",
@@ -266,21 +293,26 @@ apple_smc_fan_attach_sensors(struct appl
* XXX Do more with the fan description...
*/
+ /* Make a null-terminated copy of this fan's description. */
(void)memcpy(name, fan_desc.fd_name, sizeof(fan_desc.fd_name));
- name[sizeof(fan_desc.fd_name)] = 0;
+ name[sizeof(fan_desc.fd_name)] = '\0';
+ /* Attach all the sensors for this fan. */
for (sensor = 0; sensor < __arraycount(fan_sensors); sensor++)
apple_smc_fan_attach_sensor(sc, fan, name, sensor);
#if 0 /* XXX sysctl */
+ /* Attach sysctl knobs to control this fan. */
apple_smc_fan_sysctl_setup_1(sc, fan);
#endif
}
+ /* Fan sensors are all attached. Register with sysmon_envsys now. */
error = sysmon_envsys_register(sc->sc_sme);
if (error)
goto fail;
+ /* Success! */
error = 0;
goto out;
@@ -301,15 +333,19 @@ apple_smc_fan_attach_sensor(struct apple
KASSERT(fan < sc->sc_nfans);
KASSERT(sensor < __arraycount(fan_sensors));
- keyp = &sc->sc_fans[fan].sensors[sensor].sensor_key;
- (void)snprintf(key_name, sizeof key_name, "F%d%s",
+ /* Format the name of the key for this fan sensor. */
+ (void)snprintf(key_name, sizeof(key_name), "F%d%s",
(int)sensor, fan_sensors[sensor].fs_key_suffix);
KASSERT(strlen(key_name) == 4);
+
+ /* Look up the key for this fan sensor. */
+ keyp = &sc->sc_fans[fan].sensors[sensor].sensor_key;
error = apple_smc_named_key(sc->sc_smc, key_name, APPLE_SMC_TYPE_FPE2,
keyp);
if (error)
goto fail0;
+ /* Initialize the envsys_data record for this fan sensor. */
edata = &sc->sc_fans[fan].sensors[sensor].sensor_data;
edata->units = ENVSYS_SFANRPM;
edata->state = ENVSYS_SINVALID;
@@ -317,10 +353,12 @@ apple_smc_fan_attach_sensor(struct apple
(void)snprintf(edata->desc, sizeof(edata->desc), "fan %s %s speed",
name, fan_sensors[sensor].fs_name);
+ /* Attach this fan sensor to sysmon_envsys. */
error = sysmon_envsys_sensor_attach(sc->sc_sme, edata);
if (error)
goto fail1;
+ /* Success! */
return;
fail1: apple_smc_release_key(sc->sc_smc, *keyp);
@@ -339,6 +377,7 @@ apple_smc_fan_refresh(struct sysmon_envs
uint16_t rpm;
int error;
+ /* Sanity-check the sensor number out of paranoia. */
CTASSERT(10 <= (SIZE_MAX / __arraycount(fan_sensors)));
KASSERT(sc->sc_nfans < 10);
if (edata->sensor >= (sc->sc_nfans * __arraycount(fan_sensors))) {
@@ -347,6 +386,7 @@ apple_smc_fan_refresh(struct sysmon_envs
return;
}
+ /* Pick apart the fan number and its sensor number. */
fan = (edata->sensor / __arraycount(fan_sensors));
sensor = (edata->sensor % __arraycount(fan_sensors));
@@ -356,11 +396,12 @@ apple_smc_fan_refresh(struct sysmon_envs
/*
* If we're refreshing, this sensor got attached, so we ought
- * to have a sensor key.
+ * to have a sensor key. Grab it.
*/
key = sc->sc_fans[fan].sensors[sensor].sensor_key;
KASSERT(key != NULL);
+ /* Read the fan sensor value, in rpm. */
error = apple_smc_read_key_2(sc->sc_smc, key, &rpm);
if (error) {
aprint_error_dev(sc->sc_dev,
@@ -370,6 +411,7 @@ apple_smc_fan_refresh(struct sysmon_envs
return;
}
+ /* Success! */
edata->value_cur = rpm;
edata->state = ENVSYS_SVALID;
}
@@ -415,7 +457,9 @@ MODULE(MODULE_CLASS_DRIVER, apple_smc_fa
static int
apple_smc_fan_modcmd(modcmd_t cmd, void *arg __unused)
{
+#ifdef _MODULE
int error;
+#endif
switch (cmd) {
case MODULE_CMD_INIT:
Index: src/sys/dev/ic/apple_smc_temp.c
diff -u src/sys/dev/ic/apple_smc_temp.c:1.3 src/sys/dev/ic/apple_smc_temp.c:1.4
--- src/sys/dev/ic/apple_smc_temp.c:1.3 Tue Apr 1 17:48:52 2014
+++ src/sys/dev/ic/apple_smc_temp.c Tue Apr 1 17:49:05 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: apple_smc_temp.c,v 1.3 2014/04/01 17:48:52 riastradh Exp $ */
+/* $NetBSD: apple_smc_temp.c,v 1.4 2014/04/01 17:49:05 riastradh Exp $ */
/*
* Apple System Management Controller: Temperature Sensors
@@ -34,7 +34,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: apple_smc_temp.c,v 1.3 2014/04/01 17:48:52 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: apple_smc_temp.c,v 1.4 2014/04/01 17:49:05 riastradh Exp $");
#include <sys/types.h>
#include <sys/param.h>
@@ -87,14 +87,16 @@ CFATTACH_DECL_NEW(apple_smc_temp, sizeof
static int
apple_smc_temp_match(device_t parent, cfdata_t match, void *aux)
{
- const struct apple_smc_attach_args *asa = aux;
+ const struct apple_smc_attach_args *const asa = aux;
uint32_t nsensors;
int error;
+ /* Find how many temperature sensors we have. */
error = apple_smc_temp_count_sensors(asa->asa_smc, &nsensors);
if (error)
return 0;
+ /* If there aren't any, don't bother attaching. */
if (nsensors == 0)
return 0;
@@ -104,30 +106,59 @@ apple_smc_temp_match(device_t parent, cf
static void
apple_smc_temp_attach(device_t parent, device_t self, void *aux)
{
- struct apple_smc_temp_softc *sc = device_private(self);
- const struct apple_smc_attach_args *asa = aux;
+ struct apple_smc_temp_softc *const sc = device_private(self);
+ const struct apple_smc_attach_args *const asa = aux;
+ int error;
+
+ /* Identify ourselves. */
+ aprint_normal(": Apple SMC temperature sensors\n");
+ /* Initialize the softc. */
sc->sc_dev = self;
sc->sc_smc = asa->asa_smc;
+
+ /* Create a sysmon_envsys record, but don't register it yet. */
sc->sc_sme = sysmon_envsys_create();
sc->sc_sme->sme_name = device_xname(self);
sc->sc_sme->sme_cookie = sc;
sc->sc_sme->sme_refresh = apple_smc_temp_refresh;
- (void)apple_smc_temp_find_sensors(sc);
+ /* Find and attach all the sensors. */
+ error = apple_smc_temp_find_sensors(sc);
+ if (error) {
+ aprint_error_dev(self, "failed to find sensors: %d\n", error);
+ goto fail;
+ }
+
+ /* Sensors are all attached. Register with sysmon_envsys now. */
+ error = sysmon_envsys_register(sc->sc_sme);
+ if (error) {
+ aprint_error_dev(self, "failed to register with sysmon_envsys:"
+ " %d\n", error);
+ goto fail;
+ }
+
+ /* Success! */
+ return;
+
+fail: sysmon_envsys_destroy(sc->sc_sme);
+ sc->sc_sme = NULL;
}
static int
apple_smc_temp_detach(device_t self, int flags)
{
- struct apple_smc_temp_softc *sc = device_private(self);
+ struct apple_smc_temp_softc *const sc = device_private(self);
- KASSERT(sc->sc_sme != NULL);
- sysmon_envsys_unregister(sc->sc_sme);
- sc->sc_sme = NULL;
+ /* If we registered with sysmon_envsys, unregister. */
+ if (sc->sc_sme != NULL) {
+ sysmon_envsys_unregister(sc->sc_sme);
+ sc->sc_sme = NULL;
- if (sc->sc_sensors != NULL) {
+ KASSERT(sc->sc_sensors != NULL);
KASSERT(sc->sc_nsensors > 0);
+
+ /* Release the keys and free the memory for sensor records. */
apple_smc_temp_release_keys(sc);
kmem_free(sc->sc_sensors,
(sizeof(sc->sc_sensors[0]) * sc->sc_nsensors));
@@ -135,25 +166,29 @@ apple_smc_temp_detach(device_t self, int
sc->sc_nsensors = 0;
}
+ /* Success! */
return 0;
}
static void
apple_smc_temp_refresh(struct sysmon_envsys *sme, struct envsys_data *edata)
{
- struct apple_smc_temp_softc *sc = sme->sme_cookie;
+ struct apple_smc_temp_softc *const sc = sme->sme_cookie;
const struct apple_smc_key *key;
uint16_t utemp16;
int32_t temp;
int error;
+ /* Sanity-check the sensor number out of paranoia. */
if (edata->sensor >= sc->sc_nsensors) {
aprint_error_dev(sc->sc_dev, "unknown sensor %"PRIu32"\n",
edata->sensor);
return;
}
+ /* Read the raw temperature sensor value. */
key = sc->sc_sensors[edata->sensor].sensor_key;
+ KASSERT(key != NULL);
error = apple_smc_read_key_2(sc->sc_smc, key, &utemp16);
if (error) {
aprint_error_dev(sc->sc_dev,
@@ -176,6 +211,7 @@ apple_smc_temp_refresh(struct sysmon_env
/* Finally, convert to microkelvins as sysmon_envsys wants. */
temp *= 1000;
+ /* Success! */
edata->value_cur = temp;
edata->state = ENVSYS_SVALID;
}
@@ -184,8 +220,10 @@ static int
apple_smc_temp_count_sensors(struct apple_smc_tag *smc, uint32_t *nsensors)
{
+ /* Start with zero sensors. */
*nsensors = 0;
+ /* Count 'em. */
return apple_smc_scan_temp_sensors(smc, nsensors,
NULL,
&apple_smc_temp_count_sensors_scanner);
@@ -195,15 +233,15 @@ static void
apple_smc_temp_count_sensors_scanner(struct apple_smc_tag *smc, void *arg,
struct apple_smc_key *key)
{
- uint32_t *nsensors = arg;
+ uint32_t *const nsensors = arg;
(*nsensors)++;
apple_smc_release_key(smc, key);
}
struct fss { /* Find Sensors State */
- struct apple_smc_temp_softc *fss_sc;
- size_t fss_sensor;
+ struct apple_smc_temp_softc *fss_sc;
+ unsigned int fss_sensor;
};
static int
@@ -212,26 +250,45 @@ apple_smc_temp_find_sensors(struct apple
struct fss fss;
int error;
+ /* Start with zero sensors. */
fss.fss_sc = sc;
fss.fss_sensor = 0;
+ /* Find 'em. */
error = apple_smc_scan_temp_sensors(sc->sc_smc, &fss,
&apple_smc_temp_find_sensors_init,
&apple_smc_temp_find_sensors_scanner);
if (error)
return error;
+ /*
+ * Success guarantees that sc->sc_nsensors will be nonzero and
+ * sc->sc_sensors will be allocated.
+ */
+ KASSERT(sc->sc_sensors != NULL);
+ KASSERT(sc->sc_nsensors > 0);
+
+ /* If we didn't find any sensors, bail. */
+ if (fss.fss_sensor == 0) {
+ kmem_free(sc->sc_sensors, sc->sc_nsensors);
+ sc->sc_sensors = NULL;
+ sc->sc_nsensors = 0;
+ return EIO;
+ }
+
/* Shrink the array if we overshot. */
if (fss.fss_sensor < sc->sc_nsensors) {
- void *sensors = kmem_alloc((fss.fss_sensor *
+ void *const sensors = kmem_alloc((fss.fss_sensor *
sizeof(sc->sc_sensors[0])), KM_SLEEP);
+
(void)memcpy(sensors, sc->sc_sensors,
(fss.fss_sensor * sizeof(sc->sc_sensors[0])));
kmem_free(sc->sc_sensors, sc->sc_nsensors);
- sc->sc_nsensors = fss.fss_sensor;
sc->sc_sensors = sensors;
+ sc->sc_nsensors = fss.fss_sensor;
}
+ /* Success! */
return 0;
}
@@ -239,15 +296,25 @@ static int
apple_smc_temp_find_sensors_init(struct apple_smc_tag *smc, void *arg,
uint32_t nsensors)
{
- struct fss *fss = arg;
+ struct fss *const fss = arg;
+ /* Record the maximum number of sensors we may have. */
fss->fss_sc->sc_nsensors = nsensors;
- if (nsensors == 0)
+
+ /* If we found a maximum of zero sensors, bail. */
+ if (nsensors == 0) {
fss->fss_sc->sc_sensors = NULL;
- else
- fss->fss_sc->sc_sensors = kmem_alloc((nsensors *
- sizeof(fss->fss_sc->sc_sensors[0])), KM_SLEEP);
+ return EIO;
+ }
+
+ /*
+ * If there may be any sensors, optimistically allocate as many
+ * records for them as we may possibly need.
+ */
+ fss->fss_sc->sc_sensors = kmem_alloc((nsensors *
+ sizeof(fss->fss_sc->sc_sensors[0])), KM_SLEEP);
+ /* Success! */
return 0;
}
@@ -255,23 +322,27 @@ static void
apple_smc_temp_find_sensors_scanner(struct apple_smc_tag *smc, void *arg,
struct apple_smc_key *key)
{
- struct fss *fss = arg;
+ struct fss *const fss = arg;
const uint32_t sensor = fss->fss_sensor;
- struct envsys_data *edata =
+ struct envsys_data *const edata =
&fss->fss_sc->sc_sensors[sensor].sensor_data;
int error;
+ /* Initialize the envsys_data record for this temperature sensor. */
edata->units = ENVSYS_STEMP;
edata->state = ENVSYS_SINVALID;
edata->flags = ENVSYS_FHAS_ENTROPY;
/*
- * XXX Use a more meaningful name based on a table of known
- * temperature sensors.
+ * Use the SMC key name as the temperature sensor's name.
+ *
+ * XXX We ought to use a more meaningful name based on a table
+ * of known temperature sensors.
*/
CTASSERT(sizeof(edata->desc) >= 4);
(void)strlcpy(edata->desc, apple_smc_key_name(key), 4);
+ /* Attach this temperature sensor to sysmon_envsys. */
error = sysmon_envsys_sensor_attach(fss->fss_sc->sc_sme, edata);
if (error) {
aprint_error_dev(fss->fss_sc->sc_dev,
@@ -280,8 +351,8 @@ apple_smc_temp_find_sensors_scanner(stru
return;
}
+ /* Success! */
fss->fss_sc->sc_sensors[sensor].sensor_key = key;
-
fss->fss_sensor++;
}
@@ -306,30 +377,36 @@ apple_smc_scan_temp_sensors(struct apple
struct apple_smc_key *key;
int error;
+ /* Find [start, end) bounds on the temperature sensor key indices. */
error = apple_smc_bound_temp_sensors(smc, &tstart, &ustart);
if (error)
return error;
KASSERT(tstart <= ustart);
+ /* Inform the caller of the number of candidates. */
if (init != NULL) {
error = (*init)(smc, arg, (ustart - tstart));
if (error)
return error;
}
+ /* Take a closer look at all the candidates. */
for (i = tstart; i < ustart; i++) {
error = apple_smc_nth_key(smc, i, NULL, &key);
if (error)
continue;
+ /* Skip it if it's not a temperature sensor. */
if (!apple_smc_temp_sensor_p(key)) {
apple_smc_release_key(smc, key);
continue;
}
+ /* Scan it if it is one. */
(*scanner)(smc, arg, key);
}
+ /* Success! */
return 0;
}
@@ -337,6 +414,7 @@ static bool
apple_smc_temp_sensor_p(const struct apple_smc_key *key)
{
+ /* It's a temperature sensor iff its type is sp78. */
return (0 == memcmp(apple_smc_key_desc(key)->asd_type,
APPLE_SMC_TYPE_SP78, 4));
}
@@ -347,17 +425,21 @@ apple_smc_bound_temp_sensors(struct appl
{
int error;
+ /* Find the first `T...' key. */
error = apple_smc_key_search(smc, "T", tstart);
if (error)
return error;
+ /* Find the first `U...' key. */
error = apple_smc_key_search(smc, "U", ustart);
if (error)
return error;
+ /* Sanity check: `T...' keys had better precede `U...' keys. */
if (!(*tstart <= *ustart))
return EIO;
+ /* Success! */
return 0;
}
@@ -370,7 +452,9 @@ MODULE(MODULE_CLASS_DRIVER, apple_smc_te
static int
apple_smc_temp_modcmd(modcmd_t cmd, void *arg __unused)
{
+#ifdef _MODULE
int error;
+#endif
switch (cmd) {
case MODULE_CMD_INIT:
Index: src/sys/dev/ic/apple_smcvar.h
diff -u src/sys/dev/ic/apple_smcvar.h:1.3 src/sys/dev/ic/apple_smcvar.h:1.4
--- src/sys/dev/ic/apple_smcvar.h:1.3 Tue Apr 1 17:48:52 2014
+++ src/sys/dev/ic/apple_smcvar.h Tue Apr 1 17:49:05 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: apple_smcvar.h,v 1.3 2014/04/01 17:48:52 riastradh Exp $ */
+/* $NetBSD: apple_smcvar.h,v 1.4 2014/04/01 17:49:05 riastradh Exp $ */
/*
* Apple System Management Controller State
@@ -47,8 +47,8 @@ struct apple_smc_tag {
bus_space_tag_t smc_bst;
bus_space_handle_t smc_bsh;
bus_size_t smc_size;
- uint32_t smc_nkeys;
- kmutex_t smc_lock;
+ uint32_t smc_nkeys; /* number of keys in the SMC */
+ kmutex_t smc_io_lock; /* excludes I/O with the SMC */
#if 0 /* XXX sysctl */
struct sysctllog *smc_sysctllog;