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;