summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorRobert Mustacchi <rm@fingolfin.org>2019-12-20 21:54:14 +0000
committerRobert Mustacchi <rm@fingolfin.org>2020-01-20 00:18:56 +0000
commitf145c7bf459e3e8adb065780b1526104381e4dc5 (patch)
tree6f6d04b517c391c1f0716ae1b742e2e2f5cf59f7 /usr/src
parent7f2871dd6d7706d0fc556939604a0a457306c509 (diff)
downloadillumos-gate-f145c7bf459e3e8adb065780b1526104381e4dc5.tar.gz
12137 Want AMD temperature sensor for F10h-F16h
Reviewed by: Toomas Soome <tsoome@me.com> Reviewed by: Andy Fiddaman <andy@omniosce.org> Approved by: Dan McDonald <danmcd@joyent.com>
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/man/man7d/Makefile1
-rw-r--r--usr/src/man/man7d/amdnbtemp.7d78
-rw-r--r--usr/src/pkg/manifests/driver-cpu-sensor.mf11
-rw-r--r--usr/src/uts/intel/Makefile.files5
-rw-r--r--usr/src/uts/intel/Makefile.intel1
-rw-r--r--usr/src/uts/intel/Makefile.rules4
-rw-r--r--usr/src/uts/intel/amdnbtemp/Makefile40
-rw-r--r--usr/src/uts/intel/io/amdnbtemp/amdnbtemp.c558
8 files changed, 698 insertions, 0 deletions
diff --git a/usr/src/man/man7d/Makefile b/usr/src/man/man7d/Makefile
index 795789527f..79e13cabe6 100644
--- a/usr/src/man/man7d/Makefile
+++ b/usr/src/man/man7d/Makefile
@@ -179,6 +179,7 @@ sparc_MANFILES= audiocs.7d \
i386_MANFILES= ahci.7d \
amd8111s.7d \
amdf17nbdf.7d \
+ amdnbtemp.7d \
amr.7d \
arcmsr.7d \
arn.7d \
diff --git a/usr/src/man/man7d/amdnbtemp.7d b/usr/src/man/man7d/amdnbtemp.7d
new file mode 100644
index 0000000000..12e1ab5329
--- /dev/null
+++ b/usr/src/man/man7d/amdnbtemp.7d
@@ -0,0 +1,78 @@
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source. A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright 2019 Robert Mustacchi
+.\"
+.Dd December 24, 2019
+.Dt AMDNBTEMP 7D
+.Os
+.Sh NAME
+.Nm amdnbtemp
+.Nd AMD northbridge temperature sensor driver
+.Sh SYNOPSIS
+.Pa /dev/sensors/temperature/cpu/*
+.Sh DESCRIPTION
+The
+.Nm
+driver provides the system the ability to read the temperature sensor
+found in several AMD processors.
+This includes the K10 microarchitecture, and its derivatives, as well as
+the Bulldozer microarchitecture.
+The following AMD processors are supported:
+.Bl -dash
+.It
+AMD Family 10h Processors
+.Pq K10
+.It
+AMD Family 11h Processors
+.Pq Turion X2 Ultra
+.It
+AMD Family 12h Processors
+.Pq Llano APUs
+.It
+AMD Family 14h Processors
+.Pq Bobcat
+.It
+Family 15h models 0-fh, 10h-1fh, 30-3fh
+.Pq Bulldozer and Piledriver
+.It
+AMD Family 16h Processors
+.Pq Puma and Jaguar
+.El
+.Pp
+Some AMD Family 10h processors that utilize socket F and socket AM2 are
+subject to erratum 319 which indicates that the temperature sensor has
+an inaccurate measurement.
+If such a processor is detected, the system does not attach a
+temperature sensor to the device.
+.Pp
+AMD Family 15h models 60-7fh
+.Pq Steamroller and Excavator
+are not supported at this time.
+.Pp
+For AMD Family 17h
+.Pq Zen
+processors, the
+.Xr amdf17nbdf 7D
+driver provides access to the temperature sensors.
+.Sh SEE ALSO
+.Xr fmadm 1M ,
+.Xr amdf17nbdf 7D
+.Rs
+.%A AMD
+.%B BIOS and Kernel Developer’s Guide (BKDG) for AMD Family 16h Models 00h-0Fh Processors
+.%O 49125
+.Re
+.Rs
+.%A AMD
+.%B Revision Guide for AMD Family 10h Processors
+.%O 41322
+.Re
diff --git a/usr/src/pkg/manifests/driver-cpu-sensor.mf b/usr/src/pkg/manifests/driver-cpu-sensor.mf
index 1540e5a8f2..5ce38d340f 100644
--- a/usr/src/pkg/manifests/driver-cpu-sensor.mf
+++ b/usr/src/pkg/manifests/driver-cpu-sensor.mf
@@ -32,6 +32,15 @@ dir path=usr/share/man/man7d
driver name=amdf17nbdf \
alias=pci1022,1450,p \
alias=pci1022,1460,p
+driver name=amdnbtemp \
+ alias=pci1022,1203,p \
+ alias=pci1022,1303,p \
+ alias=pci1022,1403,p \
+ alias=pci1022,141d,p \
+ alias=pci1022,1533,p \
+ alias=pci1022,1583,p \
+ alias=pci1022,1603,p \
+ alias=pci1022,1703,p
driver name=coretemp
driver name=pchtemp \
alias=pci8086,8c24,p \
@@ -45,12 +54,14 @@ driver name=pchtemp \
alias=pci8086,a2b1,p \
alias=pci8086,a379,p
file path=kernel/drv/$(ARCH64)/amdf17nbdf group=sys
+file path=kernel/drv/$(ARCH64)/amdnbtemp group=sys
file path=kernel/drv/$(ARCH64)/coretemp group=sys
file path=kernel/drv/$(ARCH64)/pchtemp group=sys
file path=kernel/drv/coretemp.conf group=sys
file path=usr/include/sys/sensors.h mode=0644
file path=usr/lib/devfsadm/linkmod/SUNW_sensor_link.so group=sys
file path=usr/share/man/man7d/amdf17nbdf.7d
+file path=usr/share/man/man7d/amdnbtemp.7d
file path=usr/share/man/man7d/coretemp.7d
file path=usr/share/man/man7d/pchtemp.7d
license lic_CDDL license=lic_CDDL
diff --git a/usr/src/uts/intel/Makefile.files b/usr/src/uts/intel/Makefile.files
index 96972b3747..ab858501e1 100644
--- a/usr/src/uts/intel/Makefile.files
+++ b/usr/src/uts/intel/Makefile.files
@@ -346,3 +346,8 @@ AMDF17NBDF_OBJS = amdf17nbdf.o
# Intel Platform Controller Hub Temperature Module
#
PCHTEMP_OBJS = pchtemp.o
+
+#
+# AMD Family 10h-16h temperature driver
+#
+AMDNBTEMP_OBJS = amdnbtemp.o
diff --git a/usr/src/uts/intel/Makefile.intel b/usr/src/uts/intel/Makefile.intel
index 336f50de39..625c2cbfd1 100644
--- a/usr/src/uts/intel/Makefile.intel
+++ b/usr/src/uts/intel/Makefile.intel
@@ -774,3 +774,4 @@ LINTFLAGS += -D_MACHDEP -I$(UTSBASE)/i86pc
DRV_KMODS += amdf17nbdf
DRV_KMODS += coretemp
DRV_KMODS += pchtemp
+DRV_KMODS += amdnbtemp
diff --git a/usr/src/uts/intel/Makefile.rules b/usr/src/uts/intel/Makefile.rules
index 4d04cbfacb..cc271da50b 100644
--- a/usr/src/uts/intel/Makefile.rules
+++ b/usr/src/uts/intel/Makefile.rules
@@ -150,6 +150,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/intel/io/amdf17nbdf/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/intel/io/amdnbtemp/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/intel/io/amr/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
diff --git a/usr/src/uts/intel/amdnbtemp/Makefile b/usr/src/uts/intel/amdnbtemp/Makefile
new file mode 100644
index 0000000000..335e5e21fd
--- /dev/null
+++ b/usr/src/uts/intel/amdnbtemp/Makefile
@@ -0,0 +1,40 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019 Robert Mustacchi
+#
+
+UTSBASE = ../..
+
+MODULE = amdnbtemp
+OBJECTS = $(AMDNBTEMP_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/intel/io/amdf17nb
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET = $(BINARY) $(CONFMOD)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: $(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/io/amdnbtemp/amdnbtemp.c b/usr/src/uts/intel/io/amdnbtemp/amdnbtemp.c
new file mode 100644
index 0000000000..8be2177dca
--- /dev/null
+++ b/usr/src/uts/intel/io/amdnbtemp/amdnbtemp.c
@@ -0,0 +1,558 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019 Robert Mustacchi
+ */
+
+/*
+ * AMD Northbridge CPU Temperature Driver
+ *
+ * The AMD northbridge CPU temperature driver supports the temperature sensor
+ * that was found on the AMD northbridge on AMD CPUs from approximately AMD
+ * Family 10h to Family 16h. For Zen and newer processors (Family 17h+) see the
+ * 'amdf17nbdf' driver.
+ *
+ * The temperature is stored on the 'miscellaneous' device on the northbridge.
+ * This is always found at PCI Device 18h, Function 3h. When there is more than
+ * one 'node' (see cpuid.c for the AMD parlance), then the node id is added to
+ * the device to create a unique device. This allows us to map the given PCI
+ * device we find back to the corresponding CPU.
+ *
+ * While all family 10h, 11h, 12h, 14h, and 16h CPUs are supported, not all
+ * family 15h CPUs are. Models 60h+ require the SMN interface, which this does
+ * not know how to consume.
+ */
+
+#include <sys/modctl.h>
+#include <sys/conf.h>
+#include <sys/devops.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/open.h>
+#include <sys/cred.h>
+#include <sys/stat.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/cmn_err.h>
+#include <sys/pci.h>
+#include <sys/stddef.h>
+#include <sys/cpuvar.h>
+#include <sys/x86_archext.h>
+#include <sys/list.h>
+#include <sys/bitset.h>
+#include <sys/sensors.h>
+
+/*
+ * This register offset, in PCI config space, has the current temperature of the
+ * device.
+ */
+#define AMDNBTEMP_TEMPREG 0xa4
+#define AMDNBTEMP_TEMPREG_CURTMP(x) BITX(x, 31, 21)
+#define AMDNBTEMP_TEMPREG_TJSEL(x) BITX(x, 17, 16)
+
+/*
+ * Each bit in the temperature range represents 1/8th of a degree C.
+ */
+#define AMDNBTEMP_GRANULARITY 8
+#define AMDNBTEMP_GSHIFT 3
+
+/*
+ * If the value of the current CurTmpTjSel is set to three, then the range that
+ * the data is in is shifted by -49 degrees. In this mode, the bottom two bits
+ * always read as zero.
+ */
+#define AMDNBTEMP_TJSEL_ADJUST 0x3
+#define AMDNBTEMP_TEMP_ADJUST (49 << AMDNBTEMP_GSHIFT)
+
+/*
+ * There are a variable number of northbridges that exist in the system. The AMD
+ * BIOS and Kernel Developer's Guide (BKDG) says that for these families, the
+ * first node has a device of 0x18. This means that node 7, the maximum, has a
+ * device of 0x1f.
+ */
+#define AMDNBTEMP_FIRST_DEV 0x18
+
+typedef enum andnbtemp_state {
+ AMDNBTEMP_S_CFGSPACE = 1 << 0,
+ AMDNBTEMP_S_MUTEX = 1 << 1,
+ AMDNBTEMP_S_MINOR = 1 << 2,
+ AMDNBTEMP_S_LIST = 1 << 3
+} amdnbtemp_state_t;
+
+typedef struct amdnbtemp {
+ amdnbtemp_state_t at_state;
+ list_node_t at_link;
+ dev_info_t *at_dip;
+ ddi_acc_handle_t at_cfgspace;
+ uint_t at_bus;
+ uint_t at_dev;
+ uint_t at_func;
+ minor_t at_minor;
+ boolean_t at_tjsel;
+ kmutex_t at_mutex;
+ uint32_t at_raw;
+ int64_t at_temp;
+} amdnbtemp_t;
+
+static void *amdnbtemp_state;
+static list_t amdnbtemp_list;
+static kmutex_t amdnbtemp_mutex;
+
+static amdnbtemp_t *
+amdnbtemp_find_by_dev(dev_t dev)
+{
+ minor_t m = getminor(dev);
+ amdnbtemp_t *at;
+
+ mutex_enter(&amdnbtemp_mutex);
+ for (at = list_head(&amdnbtemp_list); at != NULL;
+ at = list_next(&amdnbtemp_list, at)) {
+ if (at->at_minor == m) {
+ break;
+ }
+ }
+ mutex_exit(&amdnbtemp_mutex);
+
+ return (at);
+}
+
+static int
+amdnbtemp_read(amdnbtemp_t *at)
+{
+ ASSERT(MUTEX_HELD(&at->at_mutex));
+
+ at->at_raw = pci_config_get32(at->at_cfgspace, AMDNBTEMP_TEMPREG);
+ if (at->at_raw == PCI_EINVAL32) {
+ return (EIO);
+ }
+
+ at->at_temp = AMDNBTEMP_TEMPREG_CURTMP(at->at_raw);
+ if (at->at_tjsel &&
+ AMDNBTEMP_TEMPREG_TJSEL(at->at_raw) == AMDNBTEMP_TJSEL_ADJUST) {
+ at->at_temp -= AMDNBTEMP_TEMP_ADJUST;
+ }
+
+ return (0);
+}
+
+static int
+amdnbtemp_open(dev_t *devp, int flags, int otype, cred_t *credp)
+{
+ amdnbtemp_t *at;
+
+ if (crgetzoneid(credp) != GLOBAL_ZONEID || drv_priv(credp) != 0) {
+ return (EPERM);
+ }
+
+ if ((flags & (FEXCL | FNDELAY | FWRITE)) != 0) {
+ return (EINVAL);
+ }
+
+ if (otype != OTYP_CHR) {
+ return (EINVAL);
+ }
+
+ at = amdnbtemp_find_by_dev(*devp);
+ if (at == NULL) {
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+amdnbtemp_ioctl_kind(intptr_t arg, int mode)
+{
+ sensor_ioctl_kind_t kind;
+
+ bzero(&kind, sizeof (kind));
+ kind.sik_kind = SENSOR_KIND_TEMPERATURE;
+
+ if (ddi_copyout(&kind, (void *)arg, sizeof (kind), mode & FKIOCTL) !=
+ 0) {
+ return (EFAULT);
+ }
+
+ return (0);
+}
+
+static int
+amdnbtemp_ioctl_temp(amdnbtemp_t *at, intptr_t arg, int mode)
+{
+ int ret;
+ sensor_ioctl_temperature_t temp;
+
+ bzero(&temp, sizeof (temp));
+
+ mutex_enter(&at->at_mutex);
+ if ((ret = amdnbtemp_read(at)) != 0) {
+ mutex_exit(&at->at_mutex);
+ return (ret);
+ }
+
+ temp.sit_unit = SENSOR_UNIT_CELSIUS;
+ temp.sit_gran = AMDNBTEMP_GRANULARITY;
+ temp.sit_temp = at->at_temp;
+ mutex_exit(&at->at_mutex);
+
+ if (ddi_copyout(&temp, (void *)arg, sizeof (temp), mode & FKIOCTL) !=
+ 0) {
+ return (EFAULT);
+ }
+
+ return (0);
+}
+
+static int
+amdnbtemp_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+ int *rvalp)
+{
+ amdnbtemp_t *at;
+
+ at = amdnbtemp_find_by_dev(dev);
+ if (at == NULL) {
+ return (ENXIO);
+ }
+
+ if ((mode & FREAD) == 0) {
+ return (EINVAL);
+ }
+
+ switch (cmd) {
+ case SENSOR_IOCTL_TYPE:
+ return (amdnbtemp_ioctl_kind(arg, mode));
+ case SENSOR_IOCTL_TEMPERATURE:
+ return (amdnbtemp_ioctl_temp(at, arg, mode));
+ default:
+ return (ENOTTY);
+ }
+}
+
+static int
+amdnbtemp_close(dev_t dev, int flags, int otype, cred_t *credp)
+{
+ return (0);
+}
+
+static void
+amdnbtemp_cleanup(amdnbtemp_t *at)
+{
+ int inst;
+ inst = ddi_get_instance(at->at_dip);
+
+ if ((at->at_state & AMDNBTEMP_S_LIST) != 0) {
+ mutex_enter(&amdnbtemp_mutex);
+ list_remove(&amdnbtemp_list, at);
+ mutex_exit(&amdnbtemp_mutex);
+ at->at_state &= ~AMDNBTEMP_S_LIST;
+ }
+
+ if ((at->at_state & AMDNBTEMP_S_MINOR) != 0) {
+ ddi_remove_minor_node(at->at_dip, NULL);
+ at->at_state &= ~AMDNBTEMP_S_MINOR;
+ }
+
+ if ((at->at_state & AMDNBTEMP_S_MUTEX) != 0) {
+ mutex_destroy(&at->at_mutex);
+ at->at_state &= ~AMDNBTEMP_S_MUTEX;
+ }
+
+ if ((at->at_state & AMDNBTEMP_S_CFGSPACE) != 0) {
+ pci_config_teardown(&at->at_cfgspace);
+ at->at_state &= ~AMDNBTEMP_S_CFGSPACE;
+ }
+
+ ASSERT0(at->at_state);
+ ddi_soft_state_free(amdnbtemp_state, inst);
+}
+
+/*
+ * For several family 10h processors, certain models have an erratum which says
+ * that temperature information is unreliable. If we're on a platform that is
+ * subject to this erratum, do not attach to the device.
+ */
+static boolean_t
+amdnbtemp_erratum_319(void)
+{
+ uint32_t socket;
+
+ if (cpuid_getfamily(CPU) != 0x10) {
+ return (B_FALSE);
+ }
+
+ /*
+ * All Family 10h socket F parts are impacted. Socket AM2 parts are all
+ * impacted. The family 10h socket bits in cpuid share the same bit for
+ * socket AM2 and AM3. If you look at the erratum description, they use
+ * information about the memory controller to do DDR2/DDR3
+ * disambiguation to determine whether it's socket AM2 or AM3. Our cpuid
+ * subroutines already do the DDR2/DDR3 disambiguation so we can just
+ * check the socket type as the disambiguation has already been done.
+ */
+ socket = cpuid_getsockettype(CPU);
+ if (socket == X86_SOCKET_F1207 || socket == X86_SOCKET_AM2R2) {
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+static int
+amdnbtemp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int inst, *regs;
+ amdnbtemp_t *at;
+ uint_t nregs;
+ char buf[128];
+
+ switch (cmd) {
+ case DDI_RESUME:
+ return (DDI_SUCCESS);
+ case DDI_ATTACH:
+ break;
+ default:
+ return (DDI_FAILURE);
+ }
+
+ inst = ddi_get_instance(dip);
+ if (ddi_soft_state_zalloc(amdnbtemp_state, inst) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "failed to allocate soft state entry %d",
+ inst);
+ return (DDI_FAILURE);
+ }
+
+ at = ddi_get_soft_state(amdnbtemp_state, inst);
+ if (at == NULL) {
+ dev_err(dip, CE_WARN, "failed to retrieve soft state entry %d",
+ inst);
+ return (DDI_FAILURE);
+ }
+
+ at->at_dip = dip;
+
+ if (pci_config_setup(dip, &at->at_cfgspace) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "failed to set up PCI config space");
+ goto err;
+ }
+ at->at_state |= AMDNBTEMP_S_CFGSPACE;
+
+ if (amdnbtemp_erratum_319()) {
+ dev_err(dip, CE_WARN, "!device subject to AMD Erratum 319, "
+ "not attaching to unreliable sensor");
+ goto err;
+ }
+
+ mutex_init(&at->at_mutex, NULL, MUTEX_DRIVER, NULL);
+ at->at_state |= AMDNBTEMP_S_MUTEX;
+
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, 0, "reg",
+ &regs, &nregs) != DDI_PROP_SUCCESS) {
+ dev_err(dip, CE_WARN, "failed to get pci 'reg' property");
+ goto err;
+ }
+
+ if (nregs < 1) {
+ dev_err(dip, CE_WARN, "'reg' property missing PCI b/d/f");
+ ddi_prop_free(regs);
+ goto err;
+ }
+
+ at->at_bus = PCI_REG_BUS_G(regs[0]);
+ at->at_dev = PCI_REG_DEV_G(regs[0]);
+ at->at_func = PCI_REG_DEV_G(regs[0]);
+ ddi_prop_free(regs);
+
+ if (at->at_dev < AMDNBTEMP_FIRST_DEV) {
+ dev_err(dip, CE_WARN, "Invalid pci b/d/f device, found 0x%x",
+ at->at_dev);
+ goto err;
+ }
+
+ at->at_minor = at->at_dev - AMDNBTEMP_FIRST_DEV;
+ if (snprintf(buf, sizeof (buf), "procnode.%u", at->at_minor) >=
+ sizeof (buf)) {
+ dev_err(dip, CE_WARN, "unexpected buffer name overrun "
+ "constructing minor %u", at->at_minor);
+ goto err;
+ }
+
+ if (ddi_create_minor_node(dip, buf, S_IFCHR, at->at_minor,
+ DDI_NT_SENSOR_TEMP_CPU, 0) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "failed to create minor node %s",
+ buf);
+ goto err;
+ }
+ at->at_state |= AMDNBTEMP_S_MINOR;
+
+ mutex_enter(&amdnbtemp_mutex);
+ list_insert_tail(&amdnbtemp_list, at);
+ mutex_exit(&amdnbtemp_mutex);
+ at->at_state |= AMDNBTEMP_S_LIST;
+
+ /*
+ * On families 15h and 16h the BKDG documents that the CurTmpTjSel bits
+ * of the temperature register dictate how the temperature reading
+ * should be interpreted. Capture that now.
+ */
+ if (cpuid_getfamily(CPU) >= 0x15) {
+ at->at_tjsel = B_TRUE;
+ }
+
+ mutex_enter(&at->at_mutex);
+ (void) amdnbtemp_read(at);
+ mutex_exit(&at->at_mutex);
+
+ return (DDI_SUCCESS);
+
+err:
+ amdnbtemp_cleanup(at);
+ return (DDI_FAILURE);
+}
+
+static int
+amdnbtemp_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
+ void **resultp)
+{
+ amdnbtemp_t *at;
+
+ if (cmd != DDI_INFO_DEVT2DEVINFO && cmd != DDI_INFO_DEVT2INSTANCE) {
+ return (DDI_FAILURE);
+ }
+
+ at = amdnbtemp_find_by_dev((dev_t)arg);
+ if (at == NULL) {
+ return (DDI_FAILURE);
+ }
+
+ if (cmd == DDI_INFO_DEVT2DEVINFO) {
+ *resultp = at->at_dip;
+ } else {
+ *resultp = (void *)(uintptr_t)ddi_get_instance(at->at_dip);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+static int
+amdnbtemp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ int inst;
+ amdnbtemp_t *at;
+
+ switch (cmd) {
+ case DDI_DETACH:
+ break;
+ case DDI_SUSPEND:
+ return (DDI_SUCCESS);
+ default:
+ return (DDI_FAILURE);
+ }
+
+ inst = ddi_get_instance(dip);
+ at = ddi_get_soft_state(amdnbtemp_state, inst);
+ if (at == NULL) {
+ dev_err(dip, CE_WARN, "asked to detach instance %d, but it is "
+ "missing from the soft state", inst);
+ return (DDI_FAILURE);
+ }
+
+ amdnbtemp_cleanup(at);
+ return (DDI_SUCCESS);
+}
+
+static struct cb_ops amdnbtemp_cb_ops = {
+ .cb_open = amdnbtemp_open,
+ .cb_close = amdnbtemp_close,
+ .cb_strategy = nodev,
+ .cb_print = nodev,
+ .cb_dump = nodev,
+ .cb_read = nodev,
+ .cb_write = nodev,
+ .cb_ioctl = amdnbtemp_ioctl,
+ .cb_devmap = nodev,
+ .cb_mmap = nodev,
+ .cb_segmap = nodev,
+ .cb_chpoll = nochpoll,
+ .cb_prop_op = ddi_prop_op,
+ .cb_flag = D_MP,
+ .cb_rev = CB_REV,
+ .cb_aread = nodev,
+ .cb_awrite = nodev
+};
+
+static struct dev_ops amdnbtemp_dev_ops = {
+ .devo_rev = DEVO_REV,
+ .devo_refcnt = 0,
+ .devo_getinfo = amdnbtemp_getinfo,
+ .devo_identify = nulldev,
+ .devo_probe = nulldev,
+ .devo_attach = amdnbtemp_attach,
+ .devo_detach = amdnbtemp_detach,
+ .devo_reset = nodev,
+ .devo_power = ddi_power,
+ .devo_quiesce = ddi_quiesce_not_needed,
+ .devo_cb_ops = &amdnbtemp_cb_ops
+};
+
+static struct modldrv amdnbtemp_modldrv = {
+ .drv_modops = &mod_driverops,
+ .drv_linkinfo = "AMD NB Temp Driver",
+ .drv_dev_ops = &amdnbtemp_dev_ops
+};
+
+static struct modlinkage amdnbtemp_modlinkage = {
+ .ml_rev = MODREV_1,
+ .ml_linkage = { &amdnbtemp_modldrv, NULL }
+};
+
+int
+_init(void)
+{
+ int ret;
+
+ if (ddi_soft_state_init(&amdnbtemp_state, sizeof (amdnbtemp_t), 2) !=
+ DDI_SUCCESS) {
+ return (ENOMEM);
+ }
+
+ if ((ret = mod_install(&amdnbtemp_modlinkage)) != 0) {
+ ddi_soft_state_fini(&amdnbtemp_state);
+ return (ret);
+ }
+
+ list_create(&amdnbtemp_list, sizeof (amdnbtemp_t),
+ offsetof(amdnbtemp_t, at_link));
+ mutex_init(&amdnbtemp_mutex, NULL, MUTEX_DRIVER, NULL);
+
+ return (ret);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&amdnbtemp_modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ int ret;
+
+ if ((ret = mod_remove(&amdnbtemp_modlinkage)) != 0) {
+ return (ret);
+ }
+
+ mutex_destroy(&amdnbtemp_mutex);
+ list_destroy(&amdnbtemp_list);
+ ddi_soft_state_fini(&amdnbtemp_state);
+ return (ret);
+}