Module Name: src Committed By: riastradh Date: Tue Apr 1 17:47:36 UTC 2014
Modified Files: src/sys/conf: files src/sys/dev/acpi: files.acpi Added Files: src/sys/dev/acpi: apple_smc_acpi.c src/sys/dev/ic: apple_smc.c apple_smc.h apple_smc_fan.c apple_smc_temp.c apple_smcreg.h apple_smcvar.h src/sys/modules/apple_smc: Makefile apple_smc.ioconf Log Message: First draft of drivers for the Apple System Management Controller. Device interface derived by reading the Linux driver source code and <http:///www.parhelia.ch/blog/statics/k3_keys.html> as of 2012-12-05. Includes support for attaching fan and temperature sensors to sysmon. No accelerometer yet. Compile-tested only, based on some run-testing of experiments from userland. Module attachment is not quite finished, so it won't work yet. To generate a diff of this commit: cvs rdiff -u -r1.1087 -r1.1088 src/sys/conf/files cvs rdiff -u -r0 -r1.1 src/sys/dev/acpi/apple_smc_acpi.c cvs rdiff -u -r1.93 -r1.94 src/sys/dev/acpi/files.acpi cvs rdiff -u -r0 -r1.1 src/sys/dev/ic/apple_smc.c src/sys/dev/ic/apple_smc.h \ src/sys/dev/ic/apple_smc_fan.c src/sys/dev/ic/apple_smc_temp.c \ src/sys/dev/ic/apple_smcreg.h src/sys/dev/ic/apple_smcvar.h cvs rdiff -u -r0 -r1.1 src/sys/modules/apple_smc/Makefile \ src/sys/modules/apple_smc/apple_smc.ioconf Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/conf/files diff -u src/sys/conf/files:1.1087 src/sys/conf/files:1.1088 --- src/sys/conf/files:1.1087 Wed Mar 19 15:26:42 2014 +++ src/sys/conf/files Tue Apr 1 17:47:36 2014 @@ -1,4 +1,4 @@ -# $NetBSD: files,v 1.1087 2014/03/19 15:26:42 nonaka Exp $ +# $NetBSD: files,v 1.1088 2014/04/01 17:47:36 riastradh Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 version 20100430 @@ -1095,6 +1095,26 @@ file dev/ic/hpet.c hpet needs-flag device smsh: arp, ether, ifnet, mii file dev/ic/lan9118.c smsh +# Apple System Management Controller +# +device applesmc {} +file dev/ic/apple_smc.c applesmc + +# Apple SMC fan sensors and control +device applesmcfan: applesmc, sysmon_envsys +attach applesmcfan at applesmc with apple_smc_fan +file dev/ic/apple_smc_fan.c applesmcfan + +# Apple SMC temperature sensors +device applesmctemp: applesmc, sysmon_envsys +attach applesmctemp at applesmc with apple_smc_temp +file dev/ic/apple_smc_temp.c applesmctemp + +# Apple SMC accelerometer +#device applesmcaccel: applesmc, sysmon_envsys +#attach applesmcaccel at applesmc with apple_smc_accel +#file dev/ic/apple_smc_accel.c applesmcaccel + # DRM - Direct Rendering Infrastructure: dev/drm define drm {} include "external/bsd/drm/conf/files.drm" Index: src/sys/dev/acpi/files.acpi diff -u src/sys/dev/acpi/files.acpi:1.93 src/sys/dev/acpi/files.acpi:1.94 --- src/sys/dev/acpi/files.acpi:1.93 Sun Jan 22 06:44:28 2012 +++ src/sys/dev/acpi/files.acpi Tue Apr 1 17:47:36 2014 @@ -1,4 +1,4 @@ -# $NetBSD: files.acpi,v 1.93 2012/01/22 06:44:28 christos Exp $ +# $NetBSD: files.acpi,v 1.94 2014/04/01 17:47:36 riastradh Exp $ include "dev/acpi/acpica/files.acpica" @@ -206,4 +206,8 @@ device fujhk: sysmon_power attach fujhk at acpinodebus file dev/acpi/fujhk_acpi.c fujhk +# Apple SMC +attach applesmc at acpinodebus with apple_smc_acpi +file dev/acpi/apple_smc_acpi.c apple_smc_acpi + include "dev/acpi/wmi/files.wmi" Added files: Index: src/sys/dev/acpi/apple_smc_acpi.c diff -u /dev/null src/sys/dev/acpi/apple_smc_acpi.c:1.1 --- /dev/null Tue Apr 1 17:47:36 2014 +++ src/sys/dev/acpi/apple_smc_acpi.c Tue Apr 1 17:47:36 2014 @@ -0,0 +1,145 @@ +/* $NetBSD: apple_smc_acpi.c,v 1.1 2014/04/01 17:47:36 riastradh Exp $ */ + +/* + * Apple System Management Controller: ACPI Attachment + */ + +/*- + * Copyright (c) 2013 Taylor R. Campbell + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: apple_smc_acpi.c,v 1.1 2014/04/01 17:47:36 riastradh Exp $"); + +#include <sys/types.h> +#include <sys/param.h> + +#include <dev/acpi/acpireg.h> +#include <dev/acpi/acpivar.h> + +#include <dev/ic/apple_smcreg.h> +#include <dev/ic/apple_smcvar.h> + +#define _COMPONENT ACPI_RESOURCE_COMPONENT +ACPI_MODULE_NAME ("apple_smc_acpi") + +struct apple_smc_acpi_softc { + struct apple_smc_tag sc_smc; +}; + +static int apple_smc_acpi_match(device_t, cfdata_t, void *); +static void apple_smc_acpi_attach(device_t, device_t, void *); +static int apple_smc_acpi_detach(device_t, int); + +CFATTACH_DECL_NEW(apple_smc_acpi, sizeof(struct apple_smc_acpi_softc), + apple_smc_acpi_match, apple_smc_acpi_attach, apple_smc_acpi_detach, NULL); + +static const char *const apple_smc_ids[] = { + "APP0001", + NULL +}; + +static int +apple_smc_acpi_match(device_t parent, cfdata_t match, void *aux) +{ + struct acpi_attach_args *aa = aux; + + if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) + return 0; + + if (!acpi_match_hid(aa->aa_node->ad_devinfo, apple_smc_ids)) + return 0; + + return 1; +} + +static void +apple_smc_acpi_attach(device_t parent, device_t self, void *aux) +{ + struct apple_smc_acpi_softc *sc = device_private(self); + struct apple_smc_tag *smc = &sc->sc_smc; + struct acpi_attach_args *aa = aux; + struct acpi_resources res; + struct acpi_io *io; + int rv; + + smc->smc_dev = self; + + aprint_normal("\n"); + aprint_naive("\n"); + + rv = acpi_resource_parse(self, aa->aa_node->ad_handle, "_CRS", + &res, &acpi_resource_parse_ops_default); + if (ACPI_FAILURE(rv)) { + aprint_error_dev(self, "couldn't parse SMC resources: %s\n", + AcpiFormatException(rv)); + goto out0; + } + + io = acpi_res_io(&res, 0); + if (io == NULL) { + aprint_error_dev(self, "no I/O resource\n"); + goto out1; + } + + if (io->ar_length < APPLE_SMC_REGSIZE) { + aprint_error_dev(self, "I/O resources too small: %"PRId32"\n", + io->ar_length); + goto out1; + } + + if (bus_space_map(aa->aa_iot, io->ar_base, io->ar_length, 0, + &smc->smc_bsh) != 0) { + aprint_error_dev(self, "unable to map I/O registers\n"); + goto out1; + } + + smc->smc_bst = aa->aa_iot; + smc->smc_size = io->ar_length; + + apple_smc_attach(smc); + +out1: acpi_resource_cleanup(&res); +out0: return; +} + +static int +apple_smc_acpi_detach(device_t self, int flags) +{ + struct apple_smc_acpi_softc *sc = device_private(self); + struct apple_smc_tag *smc = &sc->sc_smc; + int error; + + if (smc->smc_size != 0) { + error = apple_smc_detach(smc, flags); + if (error) + return error; + + bus_space_unmap(smc->smc_bst, smc->smc_bsh, smc->smc_size); + smc->smc_size = 0; + } + + return 0; +} Index: src/sys/dev/ic/apple_smc.c diff -u /dev/null src/sys/dev/ic/apple_smc.c:1.1 --- /dev/null Tue Apr 1 17:47:36 2014 +++ src/sys/dev/ic/apple_smc.c Tue Apr 1 17:47:36 2014 @@ -0,0 +1,524 @@ +/* $NetBSD: apple_smc.c,v 1.1 2014/04/01 17:47:36 riastradh Exp $ */ + +/* + * Apple System Management Controller + */ + +/*- + * Copyright (c) 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Taylor R. Campbell. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: apple_smc.c,v 1.1 2014/04/01 17:47:36 riastradh Exp $"); + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/device.h> +#include <sys/errno.h> +#include <sys/kmem.h> +#include <sys/mutex.h> +#if 0 /* XXX sysctl */ +#include <sys/sysctl.h> +#endif +#include <sys/systm.h> + +#include <dev/ic/apple_smc.h> +#include <dev/ic/apple_smcreg.h> +#include <dev/ic/apple_smcvar.h> + +static void apple_smc_rescan1(struct apple_smc_tag *, + struct apple_smc_attach_args *, + const char *, const char *, device_t *); +static uint8_t apple_smc_bus_read_1(struct apple_smc_tag *, bus_size_t); +static void apple_smc_bus_write_1(struct apple_smc_tag *, bus_size_t, + uint8_t); +static int apple_smc_read_data(struct apple_smc_tag *, uint8_t *); +static int apple_smc_write(struct apple_smc_tag *, bus_size_t, uint8_t); +static int apple_smc_write_cmd(struct apple_smc_tag *, uint8_t); +static int apple_smc_write_data(struct apple_smc_tag *, uint8_t); +static int apple_smc_begin(struct apple_smc_tag *, uint8_t, + const char *, uint8_t); +static int apple_smc_input(struct apple_smc_tag *, uint8_t, + const char *, void *, uint8_t); +static int apple_smc_output(struct apple_smc_tag *, uint8_t, + const char *, const void *, uint8_t); + +void +apple_smc_attach(struct apple_smc_tag *smc) +{ + + mutex_init(&smc->smc_lock, MUTEX_DEFAULT, IPL_NONE); + +#if 0 /* XXX sysctl */ + apple_smc_sysctl_setup(smc); +#endif + + (void)apple_smc_rescan(smc, NULL, NULL); +} + +int +apple_smc_rescan(struct apple_smc_tag *smc, const char *ifattr, + const int *locators) +{ + struct apple_smc_attach_args asa; + + (void)locators; /* ignore */ + + (void)memset(&asa, 0, sizeof asa); + asa.asa_smc = smc; +#if 0 /* XXX sysctl */ + asa.asa_sysctlnode = smc->smc_sysctlnode; +#endif + + apple_smc_rescan1(smc, &asa, ifattr, "applesmcfan", &smc->smc_fan); + apple_smc_rescan1(smc, &asa, ifattr, "applesmctemp", &smc->smc_temp); +#if 0 /* XXX accelerometer */ + apple_smc_rescan1(smc, &asa, ifattr, "applesmcaccel", + &smc->smc_accelfan); +#endif + + return 0; +} + +static void +apple_smc_rescan1(struct apple_smc_tag *smc, struct apple_smc_attach_args *asa, + const char *ifattr, const char *ifa_conf, device_t *devp) +{ + + if (ifattr_match(ifattr, ifa_conf) && *devp == NULL) + *devp = config_found_ia(smc->smc_dev, ifa_conf, asa, NULL); +} + +int +apple_smc_detach(struct apple_smc_tag *smc, int flags) +{ + int error; + + error = config_detach_children(smc->smc_dev, flags); + if (error) + return error; + +#if 0 /* XXX sysctl */ + sysctl_teardown(&smc->smc_log); +#endif + + return 0; +} + +static uint8_t +apple_smc_bus_read_1(struct apple_smc_tag *smc, bus_size_t reg) +{ + + return bus_space_read_1(smc->smc_bst, smc->smc_bsh, reg); +} + +static void +apple_smc_bus_write_1(struct apple_smc_tag *smc, bus_size_t reg, uint8_t v) +{ + + bus_space_write_1(smc->smc_bst, smc->smc_bsh, reg, v); +} + +/* + * XXX These delays are pretty randomly chosen. Wait in 100 us + * increments, up to a total of 1 ms. + */ + +static int +apple_smc_read_data(struct apple_smc_tag *smc, uint8_t *byte) +{ + uint8_t status; + unsigned int i; + + KASSERT(mutex_owned(&smc->smc_lock)); + + for (i = 0; i < 100; i++) { + status = apple_smc_bus_read_1(smc, APPLE_SMC_CSR); + if (status & APPLE_SMC_STATUS_READ_READY) { + *byte = apple_smc_bus_read_1(smc, APPLE_SMC_DATA); + return 0; + } + DELAY(100); + } + + return ETIMEDOUT; +} + +static int +apple_smc_write(struct apple_smc_tag *smc, bus_size_t reg, uint8_t byte) +{ + uint8_t status; + unsigned int i; + + KASSERT(mutex_owned(&smc->smc_lock)); + + 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); + if (!(status & APPLE_SMC_STATUS_WRITE_PENDING)) + apple_smc_bus_write_1(smc, reg, byte); + } + + return ETIMEDOUT; +} + +static int +apple_smc_write_cmd(struct apple_smc_tag *smc, uint8_t cmd) +{ + + return apple_smc_write(smc, APPLE_SMC_CSR, cmd); +} + +static int +apple_smc_write_data(struct apple_smc_tag *smc, uint8_t data) +{ + + return apple_smc_write(smc, APPLE_SMC_DATA, data); +} + +struct apple_smc_key { + char ask_name[4 + 1]; + struct apple_smc_desc ask_desc; +#ifdef DIAGNOSTIC + struct apple_smc_tag *ask_smc; +#endif +}; + +static int +apple_smc_begin(struct apple_smc_tag *smc, uint8_t cmd, const char *key, + uint8_t size) +{ + unsigned int i; + int error; + + KASSERT(mutex_owned(&smc->smc_lock)); + + error = apple_smc_write_cmd(smc, cmd); + if (error) + return error; + + for (i = 0; i < 4; i++) { + error = apple_smc_write_data(smc, key[i]); + if (error) + return error; + } + + error = apple_smc_write_data(smc, size); + if (error) + return error; + + return 0; +} + +static int +apple_smc_input(struct apple_smc_tag *smc, uint8_t cmd, const char *key, + void *buffer, uint8_t size) +{ + uint8_t *bytes = buffer; + uint8_t i; + int error; + + mutex_enter(&smc->smc_lock); + error = apple_smc_begin(smc, cmd, key, size); + if (error) + goto out; + + for (i = 0; i < size; i++) { + error = apple_smc_read_data(smc, &bytes[i]); + if (error) + goto out; + } + + /* Success! */ + error = 0; + +out: + mutex_exit(&smc->smc_lock); + return error; +} + +static int +apple_smc_output(struct apple_smc_tag *smc, uint8_t cmd, const char *key, + const void *buffer, uint8_t size) +{ + const uint8_t *bytes = buffer; + uint8_t i; + int error; + + mutex_enter(&smc->smc_lock); + error = apple_smc_begin(smc, cmd, key, size); + if (error) + goto out; + + for (i = 0; i < size; i++) { + error = apple_smc_write_data(smc, bytes[i]); + if (error) + goto out; + } + +out: + mutex_exit(&smc->smc_lock); + return error; +} + +const char * +apple_smc_key_name(const struct apple_smc_key *key) +{ + + return key->ask_name; +} + +const struct apple_smc_desc * +apple_smc_key_desc(const struct apple_smc_key *key) +{ + + return &key->ask_desc; +} + +uint32_t +apple_smc_nkeys(struct apple_smc_tag *smc) +{ + + return smc->smc_nkeys; +} + +int +apple_smc_nth_key(struct apple_smc_tag *smc, uint32_t index, + const char type[4 + 1], struct apple_smc_key **keyp) +{ + union { uint32_t u32; char name[4]; } index_be; + struct apple_smc_key *key; + int error; + + if ((type != NULL) && (strlen(type) != 4)) + return EINVAL; + + key = kmem_alloc(sizeof(*key), KM_SLEEP); +#ifdef DIAGNOSTIC + key->ask_smc = smc; +#endif + + 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; + key->ask_name[4] = '\0'; + + 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; + + 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)); + return error; +} + +int +apple_smc_named_key(struct apple_smc_tag *smc, const char name[4 + 1], + const char type[4 + 1], struct apple_smc_key **keyp) +{ + struct apple_smc_key *key; + int error; + + KASSERT(name != NULL); + if (strlen(name) != 4) + return EINVAL; + + if ((type != NULL) && (strlen(type) != 4)) + return EINVAL; + + key = kmem_alloc(sizeof(*key), KM_SLEEP); +#ifdef DIAGNOSTIC + key->ask_smc = smc; +#endif + (void)memcpy(key->ask_name, name, 4); + key->ask_name[4] = '\0'; + + 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; + + if ((type != NULL) && (0 != memcmp(key->ask_desc.asd_type, type, 4))) { + error = EINVAL; + goto fail; + } + + *keyp = key; + return 0; + +fail: + kmem_free(key, sizeof(*key)); + return error; +} + +void +apple_smc_release_key(struct apple_smc_tag *smc, struct apple_smc_key *key) +{ + +#ifdef DIAGNOSTIC + if (key->ask_smc != smc) + aprint_error_dev(smc->smc_dev, + "releasing key with wrong tag: %p != %p", + smc, key->ask_smc); +#endif + kmem_free(key, sizeof(*key)); +} + +int +apple_smc_key_search(struct apple_smc_tag *smc, const char *name, + uint32_t *result) +{ + struct apple_smc_key *key; + uint32_t start = 0, end = apple_smc_nkeys(smc), median; + int error; + + while (start < end) { + median = (start + ((end - start) / 2)); + error = apple_smc_nth_key(smc, median, NULL, &key); + if (error) + return error; + + if (memcmp(name, apple_smc_key_name(key), 4) < 0) + end = median; + else + start = (median + 1); + apple_smc_release_key(smc, key); + } + + *result = start; + return 0; +} + +int +apple_smc_read_key(struct apple_smc_tag *smc, const struct apple_smc_key *key, + void *buffer, uint8_t size) +{ + + if (key->ask_desc.asd_size != size) + return EINVAL; + if (!(key->ask_desc.asd_flags & APPLE_SMC_FLAG_READ)) + return EACCES; + + return apple_smc_input(smc, APPLE_SMC_CMD_READ_KEY, key->ask_name, + buffer, size); +} + +int +apple_smc_read_key_1(struct apple_smc_tag *smc, + const struct apple_smc_key *key, uint8_t *p) +{ + + return apple_smc_read_key(smc, key, p, 1); +} + +int +apple_smc_read_key_2(struct apple_smc_tag *smc, + const struct apple_smc_key *key, uint16_t *p) +{ + uint16_t be; + int error; + + error = apple_smc_read_key(smc, key, &be, 2); + if (error) + return error; + + *p = be16toh(be); + return 0; +} + +int +apple_smc_read_key_4(struct apple_smc_tag *smc, + const struct apple_smc_key *key, uint32_t *p) +{ + uint32_t be; + int error; + + error = apple_smc_read_key(smc, key, &be, 4); + if (error) + return error; + + *p = be32toh(be); + return 0; +} + +int +apple_smc_write_key(struct apple_smc_tag *smc, const struct apple_smc_key *key, + const void *buffer, uint8_t size) +{ + + if (key->ask_desc.asd_size != size) + return EINVAL; + if (!(key->ask_desc.asd_flags & APPLE_SMC_FLAG_WRITE)) + return EACCES; + + return apple_smc_output(smc, APPLE_SMC_CMD_WRITE_KEY, key->ask_name, + buffer, size); +} + +int +apple_smc_write_key_1(struct apple_smc_tag *smc, + const struct apple_smc_key *key, uint8_t v) +{ + + return apple_smc_write_key(smc, key, &v, 1); +} + +int +apple_smc_write_key_2(struct apple_smc_tag *smc, + const struct apple_smc_key *key, uint16_t v) +{ + const uint16_t v_be = htobe16(v); + + return apple_smc_write_key(smc, key, &v_be, 2); +} + +int +apple_smc_write_key_4(struct apple_smc_tag *smc, + const struct apple_smc_key *key, uint32_t v) +{ + const uint16_t v_be = htobe32(v); + + return apple_smc_write_key(smc, key, &v_be, 4); +} Index: src/sys/dev/ic/apple_smc.h diff -u /dev/null src/sys/dev/ic/apple_smc.h:1.1 --- /dev/null Tue Apr 1 17:47:36 2014 +++ src/sys/dev/ic/apple_smc.h Tue Apr 1 17:47:36 2014 @@ -0,0 +1,109 @@ +/* $NetBSD: apple_smc.h,v 1.1 2014/04/01 17:47:36 riastradh Exp $ */ + +/* + * Apple System Management Controller Interface + */ + +/*- + * Copyright (c) 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Taylor R. Campbell. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DEV_IC_APPLE_SMC_H_ +#define _DEV_IC_APPLE_SMC_H_ + +#include <sys/types.h> + +struct apple_smc_tag; + +struct apple_smc_attach_args { + struct apple_smc_tag *asa_smc; +}; + +struct apple_smc_key; + +struct apple_smc_desc { + uint8_t asd_size; + + char asd_type[4]; +#define APPLE_SMC_TYPE_UINT8 "ui8 " +#define APPLE_SMC_TYPE_UINT16 "ui16" +#define APPLE_SMC_TYPE_UINT32 "ui32" +#define APPLE_SMC_TYPE_SINT8 "si8 " +#define APPLE_SMC_TYPE_SINT16 "si16" +#define APPLE_SMC_TYPE_SINT32 "si32" +#define APPLE_SMC_TYPE_STRING "ch8*" +#define APPLE_SMC_TYPE_FANDESC "{fds" /* fan description */ +#define APPLE_SMC_TYPE_FPE2 "fpe2" /* fan RPM */ +#define APPLE_SMC_TYPE_SP78 "sp78" /* temperature in a weird scale */ + + uint8_t asd_flags; +#define APPLE_SMC_FLAG_UNKNOWN0 0x01 +#define APPLE_SMC_FLAG_UNKNOWN1 0x02 +#define APPLE_SMC_FLAG_UNKNOWN2 0x04 +#define APPLE_SMC_FLAG_UNKNOWN3 0x08 +#define APPLE_SMC_FLAG_UNKNOWN4 0x10 +#define APPLE_SMC_FLAG_UNKNOWN5 0x20 +#define APPLE_SMC_FLAG_WRITE 0x40 +#define APPLE_SMC_FLAG_READ 0x80 +} __packed; + +uint32_t apple_smc_nkeys(struct apple_smc_tag *); +int apple_smc_nth_key(struct apple_smc_tag *, + uint32_t, const char[4 + 1], + struct apple_smc_key **); +int apple_smc_named_key(struct apple_smc_tag *, + const char[4 + 1], const char[4 + 1], + struct apple_smc_key **); +void apple_smc_release_key(struct apple_smc_tag *, + struct apple_smc_key *); +int apple_smc_key_search(struct apple_smc_tag *, const char[4 + 1], + uint32_t *); +const char * apple_smc_key_name(const struct apple_smc_key *); +uint32_t apple_smc_key_index(const struct apple_smc_key *); +const struct apple_smc_desc * + apple_smc_key_desc(const struct apple_smc_key *); + +int apple_smc_read_key(struct apple_smc_tag *, + const struct apple_smc_key *, void *, uint8_t); +int apple_smc_read_key_1(struct apple_smc_tag *, + const struct apple_smc_key *, uint8_t *); +int apple_smc_read_key_2(struct apple_smc_tag *, + const struct apple_smc_key *, uint16_t *); +int apple_smc_read_key_4(struct apple_smc_tag *, + const struct apple_smc_key *, uint32_t *); + +int apple_smc_write_key(struct apple_smc_tag *, + const struct apple_smc_key *, const void *, uint8_t); +int apple_smc_write_key_1(struct apple_smc_tag *, + const struct apple_smc_key *, uint8_t); +int apple_smc_write_key_2(struct apple_smc_tag *, + const struct apple_smc_key *, uint16_t); +int apple_smc_write_key_4(struct apple_smc_tag *, + const struct apple_smc_key *, uint32_t); + +#endif /* _DEV_IC_APPLE_SMC_H_ */ Index: src/sys/dev/ic/apple_smc_fan.c diff -u /dev/null src/sys/dev/ic/apple_smc_fan.c:1.1 --- /dev/null Tue Apr 1 17:47:36 2014 +++ src/sys/dev/ic/apple_smc_fan.c Tue Apr 1 17:47:36 2014 @@ -0,0 +1,406 @@ +/* $NetBSD: apple_smc_fan.c,v 1.1 2014/04/01 17:47:36 riastradh Exp $ */ + +/* + * Apple System Management Controller: Fans + */ + +/*- + * Copyright (c) 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Taylor R. Campbell. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: apple_smc_fan.c,v 1.1 2014/04/01 17:47:36 riastradh Exp $"); + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/device.h> +#include <sys/kmem.h> +#if 0 /* XXX sysctl */ +#include <sys/sysctl.h> +#endif +#include <sys/systm.h> + +#include <dev/ic/apple_smc.h> + +#include <dev/sysmon/sysmonvar.h> + +#define APPLE_SMC_NFANS_KEY "FNum" + +static const struct fan_sensor { + const char *fs_name; + const char *fs_key_suffix; +} fan_sensors[] = { + { "actual", "Ac" }, + { "minimum", "Mn" }, + { "maximum", "Mx" }, + { "safe", "Sf" }, + { "target", "Tg" }, +}; + +struct apple_smc_fan_softc { + device_t sc_dev; + struct apple_smc_tag *sc_smc; + struct sysmon_envsys *sc_sme; + uint8_t sc_nfans; + struct { + struct { + struct apple_smc_key *sensor_key; + struct envsys_data sensor_data; + } sensors[__arraycount(fan_sensors)]; + } *sc_fans; + +#if 0 /* XXX sysctl */ + struct sysctllog *sc_sysctl_log; + const struct sysctlnode *sc_sysctl_node; +#endif +}; + +struct fan_desc { + uint8_t fd_type; + uint8_t fd_zone; + uint8_t fd_location; + uint8_t fd_reserved0; + char fd_name[12]; +} __packed; + +static int apple_smc_fan_match(device_t, cfdata_t, void *); +static void apple_smc_fan_attach(device_t, device_t, void *); +static int apple_smc_fan_detach(device_t, int); +static int apple_smc_fan_attach_sensors(struct apple_smc_fan_softc *); +static void apple_smc_fan_attach_sensor(struct apple_smc_fan_softc *, + uint8_t, const char *, uint8_t); +static void apple_smc_fan_refresh(struct sysmon_envsys *, + struct envsys_data *); +static void apple_smc_fan_release_keys(struct apple_smc_fan_softc *); +#if 0 /* XXX sysctl */ +static int apple_smc_fan_sysctl_setup(struct apple_smc_fan_softc *); +static void apple_smc_fan_sysctl_setup_1(struct apple_smc_tag *, + uint8_t); +#endif + +CFATTACH_DECL_NEW(apple_smc_fan, sizeof(struct apple_smc_fan_softc), + apple_smc_fan_match, apple_smc_fan_attach, apple_smc_fan_detach, NULL); + +static int +apple_smc_fan_match(device_t parent, cfdata_t match, void *aux) +{ + const struct apple_smc_attach_args *asa = aux; + struct apple_smc_key *nfans_key; + uint8_t nfans; + int rv = 0; + int error; + + error = apple_smc_named_key(asa->asa_smc, APPLE_SMC_NFANS_KEY, + APPLE_SMC_TYPE_UINT8, &nfans_key); + if (error) + goto out0; + + error = apple_smc_read_key_1(asa->asa_smc, nfans_key, &nfans); + if (error) + goto out1; + + if (nfans > 0) + rv = 1; + +out1: apple_smc_release_key(asa->asa_smc, nfans_key); +out0: return rv; +} + +static void +apple_smc_fan_attach(device_t parent, device_t self, void *aux) +{ + struct apple_smc_fan_softc *sc = device_private(self); + const struct apple_smc_attach_args *asa = aux; + struct apple_smc_key *nfans_key; + int error; + + aprint_normal(": Apple SMC fan sensors\n"); + + sc->sc_dev = self; + sc->sc_smc = asa->asa_smc; + + error = apple_smc_named_key(sc->sc_smc, APPLE_SMC_NFANS_KEY, + APPLE_SMC_TYPE_UINT8, &nfans_key); + if (error) + goto out0; + + error = apple_smc_read_key_1(sc->sc_smc, nfans_key, &sc->sc_nfans); + if (error) + goto out1; + + if (sc->sc_nfans == 0) { /* XXX */ + aprint_error_dev(self, "no fans\n"); + goto out1; + } + + /* Must fit in a single decimal digit. */ + if (sc->sc_nfans >= 10) { + aprint_error_dev(self, "too many fans: %"PRIu8"\n", + sc->sc_nfans); + sc->sc_nfans = 9; + } + +#if 0 /* XXX sysctl */ + error = apple_smc_fan_sysctl_setup(sc); + if (error) + goto fail0; +#endif + + error = apple_smc_fan_attach_sensors(sc); + if (error) + goto fail1; + + goto out1; + +#if 0 +fail2: + apple_smc_fan_detach_sensors(sc); +#endif + +fail1: +#if 0 /* XXX sysctl */ + sysctl_teardown(&sc->sc_sysctl_log); +fail0: +#endif + +out1: apple_smc_release_key(sc->sc_smc, nfans_key); +out0: return; +} + +static int +apple_smc_fan_detach(device_t self, int flags) +{ + struct apple_smc_fan_softc *sc = device_private(self); + + if (sc->sc_sme != NULL) { + sysmon_envsys_unregister(sc->sc_sme); + sc->sc_sme = NULL; + + KASSERT(sc->sc_fans != NULL); + KASSERT(sc->sc_nfans > 0); + KASSERT(sc->sc_nfans < 10); + + apple_smc_fan_release_keys(sc); + kmem_free(sc->sc_fans, + (sizeof(sc->sc_fans[0]) * sc->sc_nfans)); + sc->sc_fans = NULL; + sc->sc_nfans = 0; + } + +#if 0 /* XXX sysctl */ + sysctl_teardown(&sc->sc_sysctl_log); +#endif + + return 0; +} + +static int +apple_smc_fan_attach_sensors(struct apple_smc_fan_softc *sc) +{ + uint8_t fan, sensor; + char fan_desc_key_name[4 + 1]; + struct apple_smc_key *fan_desc_key; + struct fan_desc fan_desc; + char name[sizeof(fan_desc.fd_name) + 1]; + int error; + + 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; + + CTASSERT(10 <= (SIZE_MAX / sizeof(sc->sc_fans[0]))); + sc->sc_fans = kmem_zalloc((sizeof(sc->sc_fans[0]) * sc->sc_nfans), + KM_SLEEP); + + for (fan = 0; fan < sc->sc_nfans; fan++) { + (void)snprintf(fan_desc_key_name, sizeof fan_desc_key_name, + "F%"PRIu8"ID", fan); + KASSERT(4 == strlen(fan_desc_key_name)); + + error = apple_smc_named_key(sc->sc_smc, fan_desc_key_name, + APPLE_SMC_TYPE_FANDESC, &fan_desc_key); + if (error) { + aprint_error_dev(sc->sc_dev, + "error identifying fan %"PRIu8": %d\n", + fan, error); + continue; + } + + error = apple_smc_read_key(sc->sc_smc, fan_desc_key, &fan_desc, + sizeof fan_desc); + if (error) { + aprint_error_dev(sc->sc_dev, + "error identifying fan %"PRIu8": %d\n", + fan, error); + continue; + } + + /* + * XXX Do more with the fan description... + */ + + (void)memcpy(name, fan_desc.fd_name, sizeof(fan_desc.fd_name)); + name[sizeof(fan_desc.fd_name)] = 0; + + for (sensor = 0; sensor < __arraycount(fan_sensors); sensor++) + apple_smc_fan_attach_sensor(sc, fan, name, sensor); + +#if 0 /* XXX sysctl */ + apple_smc_fan_sysctl_setup_1(sc, fan); +#endif + } + + error = sysmon_envsys_register(sc->sc_sme); + if (error) + goto fail; + + error = 0; + goto out; + +fail: sysmon_envsys_destroy(sc->sc_sme); + sc->sc_sme = NULL; +out: return error; +} + +static void +apple_smc_fan_attach_sensor(struct apple_smc_fan_softc *sc, uint8_t fan, + const char *name, uint8_t sensor) +{ + char key_name[4 + 1]; + struct apple_smc_key **keyp; + struct envsys_data *edata; + int error; + + 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", + (int)sensor, fan_sensors[sensor].fs_key_suffix); + KASSERT(strlen(key_name) == 4); + error = apple_smc_named_key(sc->sc_smc, key_name, APPLE_SMC_TYPE_FPE2, + keyp); + if (error) + goto fail0; + + edata = &sc->sc_fans[fan].sensors[sensor].sensor_data; + edata->units = ENVSYS_SFANRPM; + edata->state = ENVSYS_SINVALID; + edata->flags = ENVSYS_FHAS_ENTROPY; + (void)snprintf(edata->desc, sizeof(edata->desc), "fan %s %s speed", + name, fan_sensors[sensor].fs_name); + + error = sysmon_envsys_sensor_attach(sc->sc_sme, edata); + if (error) + goto fail1; + + return; + +fail1: apple_smc_release_key(sc->sc_smc, *keyp); +fail0: *keyp = NULL; + aprint_error_dev(sc->sc_dev, + "failed to attach fan %s %s speed sensor: %d\n", + name, fan_sensors[sensor].fs_name, error); +} + +static void +apple_smc_fan_refresh(struct sysmon_envsys *sme, struct envsys_data *edata) +{ + struct apple_smc_fan_softc *sc = sme->sme_cookie; + uint8_t fan, sensor; + struct apple_smc_key *key; + uint16_t rpm; + int error; + + CTASSERT(10 <= (SIZE_MAX / __arraycount(fan_sensors))); + KASSERT(sc->sc_nfans < 10); + if (edata->sensor >= (sc->sc_nfans * __arraycount(fan_sensors))) { + aprint_error_dev(sc->sc_dev, "unknown sensor %"PRIu32"\n", + edata->sensor); + return; + } + + fan = (edata->sensor / __arraycount(fan_sensors)); + sensor = (edata->sensor % __arraycount(fan_sensors)); + + KASSERT(fan < sc->sc_nfans); + KASSERT(sensor < __arraycount(fan_sensors)); + KASSERT(edata == &sc->sc_fans[fan].sensors[sensor].sensor_data); + + /* + * If we're refreshing, this sensor got attached, so we ought + * to have a sensor key. + */ + key = sc->sc_fans[fan].sensors[sensor].sensor_key; + KASSERT(key != NULL); + + error = apple_smc_read_key_2(sc->sc_smc, key, &rpm); + if (error) { + aprint_error_dev(sc->sc_dev, + "failed to read fan %d %s speed: %d\n", + fan, fan_sensors[sensor].fs_name, error); + edata->state = ENVSYS_SINVALID; + return; + } + + edata->value_cur = rpm; + edata->state = ENVSYS_SVALID; +} + +static void +apple_smc_fan_release_keys(struct apple_smc_fan_softc *sc) +{ + uint8_t fan, sensor; + + for (fan = 0; fan < sc->sc_nfans; fan++) { + for (sensor = 0; + sensor < __arraycount(fan_sensors); + sensor++) { + struct apple_smc_key **const keyp = + &sc->sc_fans[fan].sensors[sensor].sensor_key; + if (*keyp != NULL) { + apple_smc_release_key(sc->sc_smc, *keyp); + *keyp = NULL; + } + } + } +} + +#if 0 /* XXX sysctl */ +static int +apple_smc_fan_sysctl_setup(struct apple_smc_fan_softc *sc) +{ + ... +} + +static void +apple_smc_fan_sysctl_setup_1(struct apple_smc_fan_softc *sc, uint8_t fan) +{ +} +#endif Index: src/sys/dev/ic/apple_smc_temp.c diff -u /dev/null src/sys/dev/ic/apple_smc_temp.c:1.1 --- /dev/null Tue Apr 1 17:47:36 2014 +++ src/sys/dev/ic/apple_smc_temp.c Tue Apr 1 17:47:36 2014 @@ -0,0 +1,361 @@ +/* $NetBSD: apple_smc_temp.c,v 1.1 2014/04/01 17:47:36 riastradh Exp $ */ + +/* + * Apple System Management Controller: Temperature Sensors + */ + +/*- + * Copyright (c) 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Taylor R. Campbell. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: apple_smc_temp.c,v 1.1 2014/04/01 17:47:36 riastradh Exp $"); + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/device.h> +#include <sys/kmem.h> +#include <sys/systm.h> + +#include <dev/ic/apple_smc.h> + +#include <dev/sysmon/sysmonvar.h> + +struct apple_smc_temp_softc { + device_t sc_dev; + struct apple_smc_tag *sc_smc; + struct sysmon_envsys *sc_sme; + struct { + struct apple_smc_key *sensor_key; + struct envsys_data sensor_data; + } *sc_sensors; + size_t sc_nsensors; +}; + +static int apple_smc_temp_match(device_t, cfdata_t, void *); +static void apple_smc_temp_attach(device_t, device_t, void *); +static int apple_smc_temp_detach(device_t, int); +static void apple_smc_temp_refresh(struct sysmon_envsys *, + struct envsys_data *); +static int apple_smc_temp_count_sensors(struct apple_smc_tag *, + uint32_t *); +static void apple_smc_temp_count_sensors_scanner(struct apple_smc_tag *, + void *, struct apple_smc_key *); +static int apple_smc_temp_find_sensors(struct apple_smc_temp_softc *); +static int apple_smc_temp_find_sensors_init(struct apple_smc_tag *, + void *, uint32_t); +static void apple_smc_temp_find_sensors_scanner(struct apple_smc_tag *, + void *, struct apple_smc_key *); +static void apple_smc_temp_release_keys(struct apple_smc_temp_softc *); +static int apple_smc_scan_temp_sensors(struct apple_smc_tag *, void *, + int (*)(struct apple_smc_tag *, void *, uint32_t), + void (*)(struct apple_smc_tag *, void *, + struct apple_smc_key *)); +static int apple_smc_bound_temp_sensors(struct apple_smc_tag *, + uint32_t *, uint32_t *); +static bool apple_smc_temp_sensor_p(const struct apple_smc_key *); + +CFATTACH_DECL_NEW(apple_smc_temp, sizeof(struct apple_smc_temp_softc), + apple_smc_temp_match, apple_smc_temp_attach, apple_smc_temp_detach, NULL); + +static int +apple_smc_temp_match(device_t parent, cfdata_t match, void *aux) +{ + const struct apple_smc_attach_args *asa = aux; + uint32_t nsensors; + int error; + + error = apple_smc_temp_count_sensors(asa->asa_smc, &nsensors); + if (error) + return 0; + + if (nsensors == 0) + return 0; + + return 1; +} + +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; + + sc->sc_dev = self; + sc->sc_smc = asa->asa_smc; + 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); +} + +static int +apple_smc_temp_detach(device_t self, int flags) +{ + struct apple_smc_temp_softc *sc = device_private(self); + + KASSERT(sc->sc_sme != NULL); + sysmon_envsys_unregister(sc->sc_sme); + sc->sc_sme = NULL; + + if (sc->sc_sensors != NULL) { + KASSERT(sc->sc_nsensors > 0); + apple_smc_temp_release_keys(sc); + kmem_free(sc->sc_sensors, + (sizeof(sc->sc_sensors[0]) * sc->sc_nsensors)); + sc->sc_sensors = NULL; + sc->sc_nsensors = 0; + } + + 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; + const struct apple_smc_key *key; + uint16_t utemp16; + int32_t temp; + int error; + + if (edata->sensor >= sc->sc_nsensors) { + aprint_error_dev(sc->sc_dev, "unknown sensor %"PRIu32"\n", + edata->sensor); + return; + } + + key = sc->sc_sensors[edata->sensor].sensor_key; + error = apple_smc_read_key_2(sc->sc_smc, key, &utemp16); + if (error) { + aprint_error_dev(sc->sc_dev, + "failed to read temperature sensor %"PRIu32" (%s): %d\n", + edata->sensor, apple_smc_key_name(key), error); + edata->state = ENVSYS_SINVALID; + return; + } + + /* Sign-extend, in case we ever get below freezing... */ + temp = (int16_t)utemp16; + + /* Convert to `millicentigrade'. */ + temp *= 250; + temp >>= 6; + + /* Convert to millikelvins. */ + temp += 273150; + + /* Finally, convert to microkelvins as sysmon_envsys wants. */ + temp *= 1000; + + edata->value_cur = temp; + edata->state = ENVSYS_SVALID; +} + +static int +apple_smc_temp_count_sensors(struct apple_smc_tag *smc, uint32_t *nsensors) +{ + + *nsensors = 0; + + return apple_smc_scan_temp_sensors(smc, nsensors, + NULL, + &apple_smc_temp_count_sensors_scanner); +} + +static void +apple_smc_temp_count_sensors_scanner(struct apple_smc_tag *smc, void *arg, + struct apple_smc_key *key) +{ + uint32_t *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; +}; + +static int +apple_smc_temp_find_sensors(struct apple_smc_temp_softc *sc) +{ + struct fss fss; + int error; + + fss.fss_sc = sc; + fss.fss_sensor = 0; + + 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; + + /* Shrink the array if we overshot. */ + if (fss.fss_sensor < sc->sc_nsensors) { + void *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; + } + + return 0; +} + +static int +apple_smc_temp_find_sensors_init(struct apple_smc_tag *smc, void *arg, + uint32_t nsensors) +{ + struct fss *fss = arg; + + fss->fss_sc->sc_nsensors = nsensors; + 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 0; +} + +static void +apple_smc_temp_find_sensors_scanner(struct apple_smc_tag *smc, void *arg, + struct apple_smc_key *key) +{ + struct fss *fss = arg; + const uint32_t sensor = fss->fss_sensor; + struct envsys_data *edata = + &fss->fss_sc->sc_sensors[sensor].sensor_data; + int error; + + 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. + */ + CTASSERT(sizeof(edata->desc) >= 4); + (void)strlcpy(edata->desc, apple_smc_key_name(key), 4); + + error = sysmon_envsys_sensor_attach(fss->fss_sc->sc_sme, edata); + if (error) { + aprint_error_dev(fss->fss_sc->sc_dev, + "failed to attach temperature sensor %s: %d\n", + apple_smc_key_name(key), error); + return; + } + + fss->fss_sc->sc_sensors[sensor].sensor_key = key; + + fss->fss_sensor++; +} + +static void +apple_smc_temp_release_keys(struct apple_smc_temp_softc *sc) +{ + uint32_t sensor; + + for (sensor = 0; sensor < sc->sc_nsensors; sensor++) { + KASSERT(sc->sc_sensors[sensor].sensor_key != NULL); + apple_smc_release_key(sc->sc_smc, + sc->sc_sensors[sensor].sensor_key); + } +} + +static int +apple_smc_scan_temp_sensors(struct apple_smc_tag *smc, void *arg, + int (*init)(struct apple_smc_tag *, void *, uint32_t), + void (*scanner)(struct apple_smc_tag *, void *, struct apple_smc_key *)) +{ + uint32_t tstart, ustart, i; + struct apple_smc_key *key; + int error; + + error = apple_smc_bound_temp_sensors(smc, &tstart, &ustart); + if (error) + return error; + KASSERT(tstart <= ustart); + + if (init != NULL) { + error = (*init)(smc, arg, (ustart - tstart)); + if (error) + return error; + } + + for (i = tstart; i < ustart; i++) { + error = apple_smc_nth_key(smc, i, NULL, &key); + if (error) + continue; + + if (!apple_smc_temp_sensor_p(key)) { + apple_smc_release_key(smc, key); + continue; + } + + (*scanner)(smc, arg, key); + } + + return 0; +} + +static bool +apple_smc_temp_sensor_p(const struct apple_smc_key *key) +{ + + return (0 == memcmp(apple_smc_key_desc(key)->asd_type, + APPLE_SMC_TYPE_SP78, 4)); +} + +static int +apple_smc_bound_temp_sensors(struct apple_smc_tag *smc, uint32_t *tstart, + uint32_t *ustart) +{ + int error; + + error = apple_smc_key_search(smc, "T", tstart); + if (error) + return error; + + error = apple_smc_key_search(smc, "U", ustart); + if (error) + return error; + + if (!(*tstart <= *ustart)) + return EIO; + + return 0; +} Index: src/sys/dev/ic/apple_smcreg.h diff -u /dev/null src/sys/dev/ic/apple_smcreg.h:1.1 --- /dev/null Tue Apr 1 17:47:36 2014 +++ src/sys/dev/ic/apple_smcreg.h Tue Apr 1 17:47:36 2014 @@ -0,0 +1,54 @@ +/* $NetBSD: apple_smcreg.h,v 1.1 2014/04/01 17:47:36 riastradh Exp $ */ + +/* + * Apple System Management Controller Registers + */ + +/*- + * Copyright (c) 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Taylor R. Campbell. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DEV_IC_APPLE_SMCREG_H_ +#define _DEV_IC_APPLE_SMCREG_H_ + +#define APPLE_SMC_DATA 0x00 +#define APPLE_SMC_CSR 0x04 +#define APPLE_SMC_REGSIZE 0x08 + +#define APPLE_SMC_STATUS_READ_READY 0x01 /* Ready for read. */ +#define APPLE_SMC_STATUS_WRITE_PENDING 0x02 /* Write not yet accepted. */ +#define APPLE_SMC_STATUS_WRITE_ACCEPTED 0x04 /* Write accepted. */ + +#define APPLE_SMC_CMD_READ_KEY 0x10 +#define APPLE_SMC_CMD_WRITE_KEY 0x11 +#define APPLE_SMC_CMD_NTH_KEY 0x12 +#define APPLE_SMC_CMD_KEY_DESC 0x13 + +#define APPLE_SMC_NKEYS_KEY "#KEY" + +#endif /* _DEV_IC_APPLE_SMCREG_H_ */ Index: src/sys/dev/ic/apple_smcvar.h diff -u /dev/null src/sys/dev/ic/apple_smcvar.h:1.1 --- /dev/null Tue Apr 1 17:47:36 2014 +++ src/sys/dev/ic/apple_smcvar.h Tue Apr 1 17:47:36 2014 @@ -0,0 +1,70 @@ +/* $NetBSD: apple_smcvar.h,v 1.1 2014/04/01 17:47:36 riastradh Exp $ */ + +/* + * Apple System Management Controller State + */ + +/*- + * Copyright (c) 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Taylor R. Campbell. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DEV_IC_APPLE_SMCVAR_H_ +#define _DEV_IC_APPLE_SMCVAR_H_ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/bus.h> +#include <sys/device.h> +#include <sys/mutex.h> + +struct apple_smc_tag { + device_t smc_dev; + 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; + +#if 0 /* XXX sysctl */ + struct sysctllog *smc_sysctllog; + const struct sysctlnode *smc_sysctlnode; +#endif + + device_t smc_fan; + device_t smc_temp; +#if 0 /* XXX accelerometer */ + device_t smc_accel; +#endif +}; + +void apple_smc_attach(struct apple_smc_tag *); +int apple_smc_rescan(struct apple_smc_tag *, const char *, + const int *); +int apple_smc_detach(struct apple_smc_tag *, int); + +#endif /* _DEV_IC_APPLE_SMCVAR_H_ */ Index: src/sys/modules/apple_smc/Makefile diff -u /dev/null src/sys/modules/apple_smc/Makefile:1.1 --- /dev/null Tue Apr 1 17:47:36 2014 +++ src/sys/modules/apple_smc/Makefile Tue Apr 1 17:47:36 2014 @@ -0,0 +1,17 @@ +# $NetBSD: Makefile,v 1.1 2014/04/01 17:47:36 riastradh Exp $ + +.include "../Makefile.inc" + +.PATH: ${S}/dev/ic +.PATH: ${S}/dev/acpi + +KMOD= apple_smc +IOCONF= apple_smc.ioconf +SRCS= apple_smc.c apple_smc_acpi.c apple_smc_fan.c apple_smc_temp.c + +WARNS= 4 + +# XXX What's the right thing here? +CPPFLAGS+= -DDIAGNOSTIC + +.include <bsd.kmodule.mk> Index: src/sys/modules/apple_smc/apple_smc.ioconf diff -u /dev/null src/sys/modules/apple_smc/apple_smc.ioconf:1.1 --- /dev/null Tue Apr 1 17:47:36 2014 +++ src/sys/modules/apple_smc/apple_smc.ioconf Tue Apr 1 17:47:36 2014 @@ -0,0 +1,13 @@ +# $NetBSD: apple_smc.ioconf,v 1.1 2014/04/01 17:47:36 riastradh Exp $ + +ioconf apple_smc + +include "conf/files" +include "dev/acpi/files.acpi" + +pseudo-root acpi* + +applesmc* at acpi? +applesmcfan* at applesmc? +applesmctemp* at applesmc? +#applesmcaccel* at applesmc?