summaryrefslogtreecommitdiffstats
path: root/sys/dev/amdtemp
diff options
context:
space:
mode:
authorrpaulo <rpaulo@FreeBSD.org>2009-03-13 16:08:08 +0000
committerrpaulo <rpaulo@FreeBSD.org>2009-03-13 16:08:08 +0000
commit1255db75cdf186a14895fa5f97fc423ba47c94c8 (patch)
tree69cb71746e0a1bc2159586bec7dc7c5c1be9c138 /sys/dev/amdtemp
parentef663c5779aeca4e3c355f892bdf1f816ba15ec3 (diff)
downloadFreeBSD-src-1255db75cdf186a14895fa5f97fc423ba47c94c8.zip
FreeBSD-src-1255db75cdf186a14895fa5f97fc423ba47c94c8.tar.gz
Rename the k8temp driver to amdtemp.
MFC after: 2 weeks
Diffstat (limited to 'sys/dev/amdtemp')
-rw-r--r--sys/dev/amdtemp/amdtemp.c342
1 files changed, 342 insertions, 0 deletions
diff --git a/sys/dev/amdtemp/amdtemp.c b/sys/dev/amdtemp/amdtemp.c
new file mode 100644
index 0000000..35cca98
--- /dev/null
+++ b/sys/dev/amdtemp/amdtemp.c
@@ -0,0 +1,342 @@
+/*-
+ * Copyright (c) 2008 Rui Paulo <rpaulo@FreeBSD.org>
+ * 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 ``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 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.
+ */
+
+/*
+ * Driver for the AMD K8 thermal sensors. Based on a Linux driver by the
+ * same name.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/module.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+
+#include <machine/specialreg.h>
+#include <machine/cpufunc.h>
+#include <machine/md_var.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+struct k8temp_softc {
+ device_t sc_dev;
+ int sc_temps[4];
+ int sc_ntemps;
+ struct sysctl_oid *sc_oid;
+ struct sysctl_oid *sc_sysctl_cpu[2];
+ struct intr_config_hook sc_ich;
+};
+
+#define VENDORID_AMD 0x1022
+#define DEVICEID_AMD_MISC 0x1103
+
+static struct k8temp_product {
+ uint16_t k8temp_vendorid;
+ uint16_t k8temp_deviceid;
+} k8temp_products[] = {
+ { VENDORID_AMD, DEVICEID_AMD_MISC },
+ { 0, 0 }
+};
+
+/*
+ * Register control
+ */
+#define K8TEMP_REG 0xe4
+#define K8TEMP_REG_SELSENSOR 0x40
+#define K8TEMP_REG_SELCORE 0x04
+
+#define K8TEMP_MINTEMP 49 /* -49 C is the mininum temperature */
+
+typedef enum {
+ SENSOR0_CORE0,
+ SENSOR0_CORE1,
+ SENSOR1_CORE0,
+ SENSOR1_CORE1,
+ CORE0,
+ CORE1
+} k8sensor_t;
+
+/*
+ * Device methods.
+ */
+static void k8temp_identify(driver_t *driver, device_t parent);
+static int k8temp_probe(device_t dev);
+static int k8temp_attach(device_t dev);
+static void k8temp_intrhook(void *arg);
+static int k8temp_detach(device_t dev);
+static int k8temp_match(device_t dev);
+static int32_t k8temp_gettemp(device_t dev, k8sensor_t sensor);
+static int k8temp_sysctl(SYSCTL_HANDLER_ARGS);
+
+static device_method_t k8temp_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, k8temp_identify),
+ DEVMETHOD(device_probe, k8temp_probe),
+ DEVMETHOD(device_attach, k8temp_attach),
+ DEVMETHOD(device_detach, k8temp_detach),
+
+ {0, 0}
+};
+
+static driver_t k8temp_driver = {
+ "k8temp",
+ k8temp_methods,
+ sizeof(struct k8temp_softc),
+};
+
+static devclass_t k8temp_devclass;
+DRIVER_MODULE(k8temp, hostb, k8temp_driver, k8temp_devclass, NULL, NULL);
+
+static int
+k8temp_match(device_t dev)
+{
+ int i;
+ uint16_t vendor, devid;
+
+ vendor = pci_get_vendor(dev);
+ devid = pci_get_device(dev);
+
+ for (i = 0; k8temp_products[i].k8temp_vendorid != 0; i++) {
+ if (vendor == k8temp_products[i].k8temp_vendorid &&
+ devid == k8temp_products[i].k8temp_deviceid)
+ return (1);
+ }
+
+ return (0);
+}
+
+static void
+k8temp_identify(driver_t *driver, device_t parent)
+{
+ device_t child;
+
+ /* Make sure we're not being doubly invoked. */
+ if (device_find_child(parent, "k8temp", -1) != NULL)
+ return;
+
+ if (k8temp_match(parent)) {
+ child = device_add_child(parent, "k8temp", -1);
+ if (child == NULL)
+ device_printf(parent, "add k8temp child failed\n");
+ }
+
+}
+
+static int
+k8temp_probe(device_t dev)
+{
+ uint32_t regs[4];
+
+ if (resource_disabled("k8temp", 0))
+ return (ENXIO);
+
+ do_cpuid(1, regs);
+ switch (regs[0]) {
+ case 0xf40:
+ case 0xf50:
+ case 0xf51:
+ return (ENXIO);
+ }
+ device_set_desc(dev, "AMD K8 Thermal Sensors");
+
+ return (BUS_PROBE_GENERIC);
+}
+
+static int
+k8temp_attach(device_t dev)
+{
+ struct k8temp_softc *sc = device_get_softc(dev);
+ struct sysctl_ctx_list *sysctlctx;
+ struct sysctl_oid *sysctlnode;
+
+
+ /*
+ * Setup intrhook function to create dev.cpu sysctl entries. This is
+ * needed because the cpu driver may be loaded late on boot, after
+ * us.
+ */
+ sc->sc_ich.ich_func = k8temp_intrhook;
+ sc->sc_ich.ich_arg = dev;
+ if (config_intrhook_establish(&sc->sc_ich) != 0) {
+ device_printf(dev, "config_intrhook_establish "
+ "failed!\n");
+ return (ENXIO);
+ }
+
+ /*
+ * dev.k8temp.N tree.
+ */
+ sysctlctx = device_get_sysctl_ctx(dev);
+ sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor0",
+ CTLFLAG_RD, 0, "Sensor 0");
+
+ SYSCTL_ADD_PROC(sysctlctx,
+ SYSCTL_CHILDREN(sysctlnode),
+ OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD,
+ dev, SENSOR0_CORE0, k8temp_sysctl, "I",
+ "Sensor 0 / Core 0 temperature");
+
+ SYSCTL_ADD_PROC(sysctlctx,
+ SYSCTL_CHILDREN(sysctlnode),
+ OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD,
+ dev, SENSOR0_CORE1, k8temp_sysctl, "I",
+ "Sensor 0 / Core 1 temperature");
+
+ sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor1",
+ CTLFLAG_RD, 0, "Sensor 1");
+
+ SYSCTL_ADD_PROC(sysctlctx,
+ SYSCTL_CHILDREN(sysctlnode),
+ OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD,
+ dev, SENSOR1_CORE0, k8temp_sysctl, "I",
+ "Sensor 1 / Core 0 temperature");
+
+ SYSCTL_ADD_PROC(sysctlctx,
+ SYSCTL_CHILDREN(sysctlnode),
+ OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD,
+ dev, SENSOR1_CORE1, k8temp_sysctl, "I",
+ "Sensor 1 / Core 1 temperature");
+
+ return (0);
+}
+
+void
+k8temp_intrhook(void *arg)
+{
+ int i;
+ device_t nexus, acpi, cpu;
+ device_t dev = (device_t) arg;
+ struct k8temp_softc *sc;
+ struct sysctl_ctx_list *sysctlctx;
+
+ sc = device_get_softc(dev);
+
+ /*
+ * dev.cpu.N.temperature.
+ */
+ nexus = device_find_child(root_bus, "nexus", 0);
+ acpi = device_find_child(nexus, "acpi", 0);
+
+ for (i = 0; i < 2; i++) {
+ cpu = device_find_child(acpi, "cpu",
+ device_get_unit(dev) * 2 + i);
+ if (cpu) {
+ sysctlctx = device_get_sysctl_ctx(cpu);
+
+ sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
+ SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)),
+ OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD,
+ dev, CORE0, k8temp_sysctl, "I",
+ "Max of sensor 0 / 1");
+ }
+ }
+ config_intrhook_disestablish(&sc->sc_ich);
+}
+
+int
+k8temp_detach(device_t dev)
+{
+ int i;
+ struct k8temp_softc *sc = device_get_softc(dev);
+
+ for (i = 0; i < 2; i++) {
+ if (sc->sc_sysctl_cpu[i])
+ sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0);
+ }
+
+ /* NewBus removes the dev.k8temp.N tree by itself. */
+
+ return (0);
+}
+
+static int
+k8temp_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ device_t dev = (device_t) arg1;
+ int error;
+ int32_t temp, auxtemp[2];
+
+ switch (arg2) {
+ case CORE0:
+ auxtemp[0] = k8temp_gettemp(dev, SENSOR0_CORE0);
+ auxtemp[1] = k8temp_gettemp(dev, SENSOR1_CORE0);
+ temp = imax(auxtemp[0], auxtemp[1]);
+ break;
+ case CORE1:
+ auxtemp[0] = k8temp_gettemp(dev, SENSOR0_CORE1);
+ auxtemp[1] = k8temp_gettemp(dev, SENSOR1_CORE1);
+ temp = imax(auxtemp[0], auxtemp[1]);
+ break;
+ default:
+ temp = k8temp_gettemp(dev, arg2);
+ break;
+ }
+ error = sysctl_handle_int(oidp, &temp, 0, req);
+
+ return (error);
+}
+
+static int32_t
+k8temp_gettemp(device_t dev, k8sensor_t sensor)
+{
+ uint8_t cfg;
+ uint32_t temp;
+
+ cfg = pci_read_config(dev, K8TEMP_REG, 1);
+ switch (sensor) {
+ case SENSOR0_CORE0:
+ cfg &= ~(K8TEMP_REG_SELSENSOR | K8TEMP_REG_SELCORE);
+ break;
+ case SENSOR0_CORE1:
+ cfg &= ~K8TEMP_REG_SELSENSOR;
+ cfg |= K8TEMP_REG_SELCORE;
+ break;
+ case SENSOR1_CORE0:
+ cfg &= ~K8TEMP_REG_SELCORE;
+ cfg |= K8TEMP_REG_SELSENSOR;
+ break;
+ case SENSOR1_CORE1:
+ cfg |= (K8TEMP_REG_SELSENSOR | K8TEMP_REG_SELCORE);
+ break;
+ default:
+ cfg = 0;
+ break;
+ }
+ pci_write_config(dev, K8TEMP_REG, cfg, 1);
+ temp = pci_read_config(dev, K8TEMP_REG, 4);
+ temp = ((temp >> 16) & 0xff) - K8TEMP_MINTEMP;
+
+ return (temp);
+}
OpenPOWER on IntegriCloud