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?

Reply via email to