diff options
author | Robert Mustacchi <rm@fingolfin.org> | 2019-12-20 21:54:14 +0000 |
---|---|---|
committer | Robert Mustacchi <rm@fingolfin.org> | 2020-01-20 00:18:56 +0000 |
commit | f145c7bf459e3e8adb065780b1526104381e4dc5 (patch) | |
tree | 6f6d04b517c391c1f0716ae1b742e2e2f5cf59f7 /usr/src | |
parent | 7f2871dd6d7706d0fc556939604a0a457306c509 (diff) | |
download | illumos-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/Makefile | 1 | ||||
-rw-r--r-- | usr/src/man/man7d/amdnbtemp.7d | 78 | ||||
-rw-r--r-- | usr/src/pkg/manifests/driver-cpu-sensor.mf | 11 | ||||
-rw-r--r-- | usr/src/uts/intel/Makefile.files | 5 | ||||
-rw-r--r-- | usr/src/uts/intel/Makefile.intel | 1 | ||||
-rw-r--r-- | usr/src/uts/intel/Makefile.rules | 4 | ||||
-rw-r--r-- | usr/src/uts/intel/amdnbtemp/Makefile | 40 | ||||
-rw-r--r-- | usr/src/uts/intel/io/amdnbtemp/amdnbtemp.c | 558 |
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", + ®s, &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); +} |