summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Mustacchi <rm@joyent.com>2019-04-24 03:05:13 +0000
committerRobert Mustacchi <rm@joyent.com>2019-06-27 01:29:11 +0000
commitdc90e12310982077796c5117ebfe92ee04b370a3 (patch)
tree7266c72ef469a9b7ce15389286cd9ffb6bdab119
parent2d6125aab2c7deca41a7689dae14eb32ec909f49 (diff)
downloadillumos-joyent-dc90e12310982077796c5117ebfe92ee04b370a3.tar.gz
11273 Want Intel PCH temperature sensor
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com> Reviewed by: Mike Zeller <mike.zeller@joyent.com> Reviewed by: Toomas Soome <tsoome@me.com> Reviewed by: Gergő Doma <domag02@gmail.com> Reviewed by: Paul Winder <Paul.Winder@wdc.com> Approved by: Richard Lowe <richlowe@richlowe.net>
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/hc.c3
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_hc.h3
-rw-r--r--usr/src/lib/fm/topo/maps/i86pc/i86pc-legacy-hc-topology.xml5
-rw-r--r--usr/src/lib/fm/topo/modules/i86pc/Makefile2
-rw-r--r--usr/src/lib/fm/topo/modules/i86pc/chipset/Makefile29
-rw-r--r--usr/src/lib/fm/topo/modules/i86pc/chipset/chipset.c190
-rw-r--r--usr/src/man/man7d/Makefile3
-rw-r--r--usr/src/man/man7d/pchtemp.7d69
-rw-r--r--usr/src/pkg/manifests/driver-cpu-sensor.mf12
-rw-r--r--usr/src/pkg/manifests/service-fault-management.mf2
-rw-r--r--usr/src/uts/common/sys/sunddi.h3
-rw-r--r--usr/src/uts/intel/Makefile.files7
-rw-r--r--usr/src/uts/intel/Makefile.intel3
-rw-r--r--usr/src/uts/intel/Makefile.rules4
-rw-r--r--usr/src/uts/intel/io/pchtemp/pchtemp.c525
-rw-r--r--usr/src/uts/intel/pchtemp/Makefile46
16 files changed, 898 insertions, 8 deletions
diff --git a/usr/src/lib/fm/topo/libtopo/common/hc.c b/usr/src/lib/fm/topo/libtopo/common/hc.c
index 9c64077549..15d7b769e9 100644
--- a/usr/src/lib/fm/topo/libtopo/common/hc.c
+++ b/usr/src/lib/fm/topo/libtopo/common/hc.c
@@ -22,7 +22,7 @@
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2019, Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
*/
#include <stdio.h>
@@ -154,6 +154,7 @@ static const hcc_t hc_canon[] = {
{ CENTERPLANE, TOPO_STABILITY_PRIVATE },
{ CHASSIS, TOPO_STABILITY_PRIVATE },
{ CHIP, TOPO_STABILITY_PRIVATE },
+ { CHIPSET, TOPO_STABILITY_PRIVATE },
{ CHIP_SELECT, TOPO_STABILITY_PRIVATE },
{ CORE, TOPO_STABILITY_PRIVATE },
{ CONTROLLER, TOPO_STABILITY_PRIVATE },
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_hc.h b/usr/src/lib/fm/topo/libtopo/common/topo_hc.h
index 26b307a20c..addc96803f 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_hc.h
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_hc.h
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2019, Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
*/
#ifndef _TOPO_HC_H
@@ -42,6 +42,7 @@ extern "C" {
#define CENTERPLANE "centerplane"
#define CHASSIS "chassis"
#define CHIP "chip"
+#define CHIPSET "chipset"
#define CORE "core"
#define STRAND "strand"
#define CHIP_SELECT "chip-select"
diff --git a/usr/src/lib/fm/topo/maps/i86pc/i86pc-legacy-hc-topology.xml b/usr/src/lib/fm/topo/maps/i86pc/i86pc-legacy-hc-topology.xml
index dc37ecfed4..0ddcd1e0d8 100644
--- a/usr/src/lib/fm/topo/maps/i86pc/i86pc-legacy-hc-topology.xml
+++ b/usr/src/lib/fm/topo/maps/i86pc/i86pc-legacy-hc-topology.xml
@@ -2,7 +2,7 @@
<!DOCTYPE topology SYSTEM "/usr/share/lib/xml/dtd/topology.dtd.1">
<!--
Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
-Copyright (c) 2018, Joyent, Inc. All rights reserved.
+Copyright 2019 Joyent, Inc.
CDDL HEADER START
@@ -86,6 +86,9 @@ Copyright (c) 2018, Joyent, Inc. All rights reserved.
<range name='usb-mobo' min='0' max='100'>
<enum-method name='usb' version='1' />
</range>
+ <range name='chipset' min='0' max='100'>
+ <enum-method name='chipset' version='1' />
+ </range>
</dependents>
</range>
diff --git a/usr/src/lib/fm/topo/modules/i86pc/Makefile b/usr/src/lib/fm/topo/modules/i86pc/Makefile
index f42af0a306..36c4ea5f95 100644
--- a/usr/src/lib/fm/topo/modules/i86pc/Makefile
+++ b/usr/src/lib/fm/topo/modules/i86pc/Makefile
@@ -24,7 +24,7 @@
# Use is subject to license terms.
#
-SUBDIRS = chip hostbridge pcibus x86pi
+SUBDIRS = chip chipset hostbridge pcibus x86pi
.PARALLEL: $(SUBDIRS)
diff --git a/usr/src/lib/fm/topo/modules/i86pc/chipset/Makefile b/usr/src/lib/fm/topo/modules/i86pc/chipset/Makefile
new file mode 100644
index 0000000000..cd8c8761f7
--- /dev/null
+++ b/usr/src/lib/fm/topo/modules/i86pc/chipset/Makefile
@@ -0,0 +1,29 @@
+#
+# 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 Joyent, Inc.
+#
+
+MODULE = chipset
+ARCH = i86pc
+CLASS = arch
+SHAREDDIR = ../../common/shared/
+
+MODULESRCS = chipset.c topo_sensor.c
+
+include ../../Makefile.plugin
+
+CPPFLAGS += -I$(SHAREDDIR)
+
+%.o: $(SHAREDDIR)/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
diff --git a/usr/src/lib/fm/topo/modules/i86pc/chipset/chipset.c b/usr/src/lib/fm/topo/modules/i86pc/chipset/chipset.c
new file mode 100644
index 0000000000..cc306b4b2f
--- /dev/null
+++ b/usr/src/lib/fm/topo/modules/i86pc/chipset/chipset.c
@@ -0,0 +1,190 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+/*
+ * Chipset Enumeration
+ *
+ * Most x86 systems have some form of chipset which are components that exist on
+ * the motherboard that provide additional services that range from I/O such as
+ * memory and PCIe controllers (though as of this writing those mostly are a
+ * part of the CPU now) to additional functionality like Ethernet and USB
+ * controllers. At the moment, this module opts to enumerate a chipset node if
+ * there's something that exists under it that we care about such as:
+ *
+ * o Temperature sensors
+ * o Firmware modules
+ *
+ * If we do not detect anything, then we do not bother enumerating and trying to
+ * determine the different chipsets that are on the system. Currently, the only
+ * method for doing this is the presence of an Intel platform controller hub
+ * (PCH) temperature sensor.
+ */
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#include <sys/fm/protocol.h>
+#include <fm/topo_mod.h>
+#include <fm/topo_list.h>
+#include <fm/topo_method.h>
+
+#include <topo_sensor.h>
+
+#define CHIPSET_VERSION 1
+
+/*
+ * This is the path to the temperature sensor that, if present, indicates we
+ * should construct a chipset node.
+ */
+static const char *topo_chipset_temp_sensor =
+ "/dev/sensors/temperature/pch/ts.0";
+
+/*
+ * Attempt to determine if there is enough information for us to enumerate a
+ * chipset node, which usually means that we would enumerate something under it
+ * such as a temperature sensor or provide information about some piece of
+ * firmware that it has. Currently, if there is no temperature sensor, then we
+ * don't consider one to be present and don't do anything else.
+ */
+static boolean_t
+topo_chipset_present(void)
+{
+ struct stat st;
+
+ if (stat(topo_chipset_temp_sensor, &st) == 0 &&
+ S_ISCHR(st.st_mode)) {
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+static int
+topo_chipset_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
+ topo_instance_t min, topo_instance_t max, void *modarg, void *data)
+{
+ int ret;
+ nvlist_t *fmri = NULL, *auth = NULL, *presource = NULL;
+ tnode_t *tn = NULL;
+ const topo_instance_t inst = 0;
+
+ topo_mod_dprintf(mod, "chipset_enum: asked to enumerate %s", name);
+
+ if (strcmp(name, CHIPSET) != 0) {
+ topo_mod_dprintf(mod, "chipset_enum: asked to enumerate "
+ "unknown component");
+ return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM));
+ }
+
+ if (!topo_chipset_present()) {
+ topo_mod_dprintf(mod, "chipset_enum: no device present", name);
+ return (0);
+ }
+
+ if ((auth = topo_mod_auth(mod, pnode)) == NULL) {
+ topo_mod_dprintf(mod, "chipset_enum: failed to get topo "
+ "auth: %s", topo_mod_errmsg(mod));
+ /* topo_mod_auth() sets the module error */
+ ret = -1;
+ goto err;
+ }
+
+ if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
+ CHIPSET, inst, NULL, auth, NULL, NULL, NULL)) == NULL) {
+ topo_mod_dprintf(mod, "chipset_enum: failed to get FMRI: %s",
+ topo_mod_errmsg(mod));
+ /* topo_mod_hcfmri() sets the module error */
+ ret = -1;
+ goto err;
+ }
+
+ if ((tn = topo_node_bind(mod, pnode, CHIPSET, inst, fmri)) == NULL) {
+ topo_mod_dprintf(mod, "chipset_enum: failed to bind node: %s",
+ topo_mod_errmsg(mod));
+ ret = -1;
+ goto err;
+ }
+
+ if (topo_node_resource(pnode, &presource, &ret) != 0) {
+ topo_mod_dprintf(mod, "chipset_enum: failed to get parent "
+ "resource %s\n", topo_strerror(ret));
+ ret = topo_mod_seterrno(mod, ret);
+ goto err;
+ }
+
+ if (topo_node_fru_set(tn, presource, 0, &ret) != 0) {
+ topo_mod_dprintf(mod, "chipset_enum: failed to set FRU: %s",
+ topo_strerror(ret));
+ ret = topo_mod_seterrno(mod, ret);
+ goto err;
+ }
+
+ /*
+ * Finally, create the temperature sensor.
+ */
+ if ((ret = topo_sensor_create_temp_sensor(mod, tn,
+ topo_chipset_temp_sensor, "temp")) != 0) {
+ topo_mod_dprintf(mod, "failed to create chipset temperature "
+ "sensor");
+ goto err;
+ }
+
+ nvlist_free(auth);
+ nvlist_free(fmri);
+ nvlist_free(presource);
+ return (0);
+err:
+ nvlist_free(auth);
+ nvlist_free(fmri);
+ nvlist_free(presource);
+ topo_node_unbind(tn);
+ return (ret);
+}
+
+static const topo_modops_t chipset_ops = {
+ topo_chipset_enum, NULL
+};
+
+static topo_modinfo_t chipset_mod = {
+ CHIPSET, FM_FMRI_SCHEME_HC, CHIPSET_VERSION, &chipset_ops
+};
+
+int
+_topo_init(topo_mod_t *mod, topo_version_t version)
+{
+ if (getenv("TOPOCHIPSETDEBUG") != NULL) {
+ topo_mod_setdebug(mod);
+ }
+
+ topo_mod_dprintf(mod, "_mod_init: initializing %s enumerator\n",
+ CHIPSET);
+
+ if (version != -1) {
+
+ }
+
+ if (topo_mod_register(mod, &chipset_mod, TOPO_VERSION) != 0) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+void
+_topo_fini(topo_mod_t *mod)
+{
+}
diff --git a/usr/src/man/man7d/Makefile b/usr/src/man/man7d/Makefile
index ceec9a8978..3f0523b84a 100644
--- a/usr/src/man/man7d/Makefile
+++ b/usr/src/man/man7d/Makefile
@@ -12,7 +12,7 @@
#
# Copyright 2011, Richard Lowe
# Copyright 2016 Garrett D'Amore <garrett@damore.org>
-# Copyright 2019, Joyent, Inc.
+# Copyright 2019 Joyent, Inc.
# Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
# Copyright 2018 Nexenta Systems, Inc.
# Copyright 2019 Peter Tribble
@@ -217,6 +217,7 @@ i386_MANFILES= ahci.7d \
ntxn.7d \
nv_sata.7d \
nvme.7d \
+ pchtemp.7d \
pcn.7d \
qede.7d \
ral.7d \
diff --git a/usr/src/man/man7d/pchtemp.7d b/usr/src/man/man7d/pchtemp.7d
new file mode 100644
index 0000000000..d89f09ab90
--- /dev/null
+++ b/usr/src/man/man7d/pchtemp.7d
@@ -0,0 +1,69 @@
+.\"
+.\" 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 Joyent, Inc.
+.\"
+.Dd April 26, 2019
+.Dt PCHTEMP 7D
+.Os
+.Sh NAME
+.Nm pchtemp
+.Nd Intel platform controller hub temperature sensor driver
+.Sh SYNOPSIS
+.Pa /dev/sensors/temperature/pch/*
+.Sh DESCRIPTION
+The
+.Nm
+driver provides the system the ability to read the digital temperature
+sensor found on several Intel platform controller hub (PCH) chipsets.
+The following chipsets are supported which cover most Intel Core familiy
+(non-Atom) CPUs starting with the Haswell generation:
+.Bl -dash
+.It
+Intel 8 Series / C220 Series Chipset Platform Controller Hub
+.It
+Intel 9 Series Chipset Family Platform Controller Hub
+.It
+Intel C610 Series Chipset and X99 Chipset Platform Controller Hub
+.It
+Intel 100 Series Chipset Family Platform Controller Hub
+.It
+Intel C620 Series Chipset Platform Controller Hub
+.It
+Intel 200 and Z370 Series Chipset Families Platform Controller Hub
+.It
+Intel 7th/8th Generation Processor Family U/Y Platforms
+.It
+Intel 300 Series and Intel C240 Series Chipset Family Platform
+Controller Hub
+.El
+.Pp
+Temperature information is available to the system via the fault
+management architecture
+.Pq FMA .
+The file system location and programming interface to the
+.Nm
+driver are considered
+.Sy Volatile ,
+subject to change without notice, and should not be used directly.
+Raw temperature information can be dumped through the FMA developer
+utility
+.Sy fmtopo .
+.Sh SEE ALSO
+.Xr fmadm 1M
+.Rs
+.%A Intel Corporation
+.%B Intel 300 Series and Intel C240 Series Chipset Family Platform Controller Hub
+.%D March 2019
+.%O Document Number 337347-005
+.%V 1
+.%U https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/300-series-chipset-pch-datasheet-vol-1.pdf
+.Re
diff --git a/usr/src/pkg/manifests/driver-cpu-sensor.mf b/usr/src/pkg/manifests/driver-cpu-sensor.mf
index 206456e092..b9161f82f6 100644
--- a/usr/src/pkg/manifests/driver-cpu-sensor.mf
+++ b/usr/src/pkg/manifests/driver-cpu-sensor.mf
@@ -33,11 +33,23 @@ driver name=amdf17nbdf \
alias=pci1022,1450 \
alias=pci1022,1460
driver name=coretemp
+driver name=pchtemp \
+ alias=pci8086,8c24 \
+ alias=pci8086,8ca4 \
+ alias=pci8086,8d24 \
+ alias=pci8086,9d31 \
+ alias=pci8086,a131 \
+ alias=pci8086,a1b1 \
+ alias=pci8086,a231 \
+ alias=pci8086,a2b1 \
+ alias=pci8086,a379
file path=kernel/drv/$(ARCH64)/amdf17nbdf 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/coretemp.7d
+file path=usr/share/man/man7d/pchtemp.7d
license lic_CDDL license=lic_CDDL
diff --git a/usr/src/pkg/manifests/service-fault-management.mf b/usr/src/pkg/manifests/service-fault-management.mf
index 8e97ddca9e..a6e616605d 100644
--- a/usr/src/pkg/manifests/service-fault-management.mf
+++ b/usr/src/pkg/manifests/service-fault-management.mf
@@ -758,6 +758,8 @@ $(i386_ONLY)file \
$(i386_ONLY)file path=usr/platform/i86pc/lib/fm/topo/maps/psu-hc-topology.xml \
mode=0444
$(i386_ONLY)file path=usr/platform/i86pc/lib/fm/topo/plugins/chip.so mode=0555
+$(i386_ONLY)file path=usr/platform/i86pc/lib/fm/topo/plugins/chipset.so \
+ mode=0555
$(i386_ONLY)file path=usr/platform/i86pc/lib/fm/topo/plugins/hostbridge.so \
mode=0555
$(i386_ONLY)file path=usr/platform/i86pc/lib/fm/topo/plugins/pcibus.so \
diff --git a/usr/src/uts/common/sys/sunddi.h b/usr/src/uts/common/sys/sunddi.h
index 5a98e6e625..f81a391f41 100644
--- a/usr/src/uts/common/sys/sunddi.h
+++ b/usr/src/uts/common/sys/sunddi.h
@@ -24,7 +24,7 @@
* Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved.
* Copyright (c) 2012 by Delphix. All rights reserved.
* Copyright 2016 Nexenta Systems, Inc. All rights reserved.
- * Copyright 2019, Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
*/
#ifndef _SYS_SUNDDI_H
@@ -264,6 +264,7 @@ extern "C" {
* Various device types used for sensors.
*/
#define DDI_NT_SENSOR_TEMP_CPU "ddi_sensor:temperature:cpu"
+#define DDI_NT_SENSOR_TEMP_PCH "ddi_sensor:temperature:pch"
/*
* DDI event definitions
diff --git a/usr/src/uts/intel/Makefile.files b/usr/src/uts/intel/Makefile.files
index 2e38cfcab8..3f95d5d78a 100644
--- a/usr/src/uts/intel/Makefile.files
+++ b/usr/src/uts/intel/Makefile.files
@@ -21,7 +21,7 @@
#
# Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright 2019, Joyent, Inc.
+# Copyright 2019 Joyent, Inc.
# Copyright 2018 Nexenta Systems, Inc.
#
@@ -337,3 +337,8 @@ CORETEMP_OBJS = coretemp.o
# AMD Family 17 northbridge driver
#
AMDF17NBDF_OBJS = amdf17nbdf.o
+
+#
+# Intel Platform Controller Hub Temperature Module
+#
+PCHTEMP_OBJS = pchtemp.o
diff --git a/usr/src/uts/intel/Makefile.intel b/usr/src/uts/intel/Makefile.intel
index 8e691a9d66..cbdcd84e7e 100644
--- a/usr/src/uts/intel/Makefile.intel
+++ b/usr/src/uts/intel/Makefile.intel
@@ -21,7 +21,7 @@
#
# Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2013 Andrew Stormont. All rights reserved.
-# Copyright 2019, Joyent, Inc.
+# Copyright 2019 Joyent, Inc.
# Copyright 2016 Garrett D'Amore <garrett@damore.org>
# Copyright 2018 Nexenta Systems, Inc.
#
@@ -734,3 +734,4 @@ LINTFLAGS += -D_MACHDEP -I$(UTSBASE)/i86pc
#
DRV_KMODS += amdf17nbdf
DRV_KMODS += coretemp
+DRV_KMODS += pchtemp
diff --git a/usr/src/uts/intel/Makefile.rules b/usr/src/uts/intel/Makefile.rules
index 2e193a40fb..6993b7f605 100644
--- a/usr/src/uts/intel/Makefile.rules
+++ b/usr/src/uts/intel/Makefile.rules
@@ -182,6 +182,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/intel/io/mc-amd/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/intel/io/pchtemp/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/intel/io/pci/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
diff --git a/usr/src/uts/intel/io/pchtemp/pchtemp.c b/usr/src/uts/intel/io/pchtemp/pchtemp.c
new file mode 100644
index 0000000000..2c2997c958
--- /dev/null
+++ b/usr/src/uts/intel/io/pchtemp/pchtemp.c
@@ -0,0 +1,525 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+/*
+ * Intel Platform Controller Hub (PCH) Thermal Sensor Driver
+ *
+ * The Intel PCH is a chip that was introduced around the Nehalem generation
+ * that provides many services for the broader system on a discrete chip from
+ * the CPU. While it existed prior to the Nehalem generation, it was previously
+ * two discrete chips called the Northbridge and Southbridge. Sometimes this
+ * device is also called a 'chipset'.
+ *
+ * The PCH contains everything from a USB controller, to an AHCI controller, to
+ * clocks, the Intel Management Engine, and more. Relevant to this driver is its
+ * thermal sensor which gives us the ability to read the temperature sensor that
+ * is embedded in the PCH.
+ *
+ * The format of this sensor varies based on the generation of the chipset. The
+ * current driver supports the following chipsets organized by datasheet, which
+ * corresponds with a change in format that was introduced in the Haswell
+ * generation:
+ *
+ * - Intel 8 Series PCH
+ * - Intel 9 Series PCH
+ * - Intel C610 Series and X99 PCH
+ * - Intel C620 Series PCH
+ * - Intel 100 Series PCH
+ * - Intel 200 Series and Z730 PCH
+ * - Intel Sunrise Point-LP (Kaby Lake-U) PCH
+ * - Intel 300 Series and C240 Chipset
+ *
+ * The following chipsets use a different format and are not currently
+ * supported:
+ *
+ * - Intel 5 Series and Xeon 3400 PCH
+ * - Intel 6 Series PCH
+ * - Intel 7 Series PCH
+ * - Intel C600 Series and X79 PCH
+ */
+
+#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/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/cmn_err.h>
+#include <sys/stat.h>
+#include <sys/sensors.h>
+
+/*
+ * In all cases the data we care about is in the first PCI bar, bar 0. Per
+ * pci(4)/pcie(4), this is always going to be register number 1.
+ */
+#define PCHTEMP_RNUMBER 1
+
+/*
+ * The PCH Temperature Sensor has a resolution of 1/2 a degree. This is a
+ * resolution of 2 in our parlance. The register reads 50 C higher than it is.
+ * Therefore our offset is 50 shifted over by one.
+ */
+#define PCHTEMP_TEMP_RESOLUTION 2
+#define PCHTEMP_TEMP_OFFSET (50 << 1)
+
+/*
+ * This register offset has the temperature that we want to read in the lower
+ * 8-bits. The resolution and offset are described above.
+ */
+#define PCHTEMP_REG_TEMP 0x00
+#define PCHTEMP_REG_TEMP_TSR 0x00ff
+
+/*
+ * Thermal Sensor Enable and Lock (TSEL) register. This register is a byte wide
+ * and has two bits that we care about. The ETS bit, enable thermal sensor,
+ * indicates whether or not the sensor is enabled. The control for this can be
+ * locked which is the PLDB, Policy Lock-Down Bit, bit. Which restricts
+ * additional control of this register.
+ */
+#define PCHTEMP_REG_TSEL 0x08
+#define PCHTEMP_REG_TSEL_ETS 0x01
+#define PCHTEMP_REG_TSEL_PLDB 0x80
+
+/*
+ * Threshold registers for the thermal sensors. These indicate the catastrophic,
+ * the high alert threshold, and the low alert threshold respectively.
+ */
+#define PCHTEMP_REG_CTT 0x10
+#define PCHTEMP_REG_TAHV 0x14
+#define PCHTEMP_REG_TALV 0x18
+
+typedef struct pchtemp {
+ dev_info_t *pcht_dip;
+ int pcht_fm_caps;
+ caddr_t pcht_base;
+ ddi_acc_handle_t pcht_handle;
+ kmutex_t pcht_mutex; /* Protects members below */
+ uint16_t pcht_temp_raw;
+ uint8_t pcht_tsel_raw;
+ uint16_t pcht_ctt_raw;
+ uint16_t pcht_tahv_raw;
+ uint16_t pcht_talv_raw;
+ int64_t pcht_temp;
+} pchtemp_t;
+
+void *pchtemp_state;
+
+static pchtemp_t *
+pchtemp_find_by_dev(dev_t dev)
+{
+ return (ddi_get_soft_state(pchtemp_state, getminor(dev)));
+}
+
+static int
+pchtemp_read_check(pchtemp_t *pch)
+{
+ ddi_fm_error_t de;
+
+ if (!DDI_FM_ACC_ERR_CAP(pch->pcht_fm_caps)) {
+ return (DDI_FM_OK);
+ }
+
+ ddi_fm_acc_err_get(pch->pcht_handle, &de, DDI_FME_VERSION);
+ ddi_fm_acc_err_clear(pch->pcht_handle, DDI_FME_VERSION);
+ return (de.fme_status);
+}
+
+static int
+pchtemp_read(pchtemp_t *pch)
+{
+ uint16_t temp, ctt, tahv, talv;
+ uint8_t tsel;
+
+ ASSERT(MUTEX_HELD(&pch->pcht_mutex));
+
+ temp = ddi_get16(pch->pcht_handle,
+ (uint16_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_TEMP));
+ tsel = ddi_get8(pch->pcht_handle,
+ (uint8_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_TSEL));
+ ctt = ddi_get16(pch->pcht_handle,
+ (uint16_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_CTT));
+ tahv = ddi_get16(pch->pcht_handle,
+ (uint16_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_TAHV));
+ talv = ddi_get16(pch->pcht_handle,
+ (uint16_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_TALV));
+
+ if (pchtemp_read_check(pch) != DDI_FM_OK) {
+ dev_err(pch->pcht_dip, CE_WARN, "failed to read temperature "
+ "data due to FM device error");
+ return (EIO);
+ }
+
+ pch->pcht_temp_raw = temp;
+ pch->pcht_tsel_raw = tsel;
+ pch->pcht_ctt_raw = ctt;
+ pch->pcht_tahv_raw = tahv;
+ pch->pcht_talv_raw = talv;
+
+ if ((tsel & PCHTEMP_REG_TSEL_ETS) == 0) {
+ return (ENXIO);
+ }
+
+ pch->pcht_temp = (temp & PCHTEMP_REG_TEMP_TSR) - PCHTEMP_TEMP_OFFSET;
+
+ return (0);
+}
+
+static int
+pchtemp_open(dev_t *devp, int flags, int otype, cred_t *credp)
+{
+ pchtemp_t *pch;
+
+ if (crgetzoneid(credp) != GLOBAL_ZONEID || drv_priv(credp)) {
+ return (EPERM);
+ }
+
+ if ((flags & (FEXCL | FNDELAY | FWRITE)) != 0) {
+ return (EINVAL);
+ }
+
+ if (otype != OTYP_CHR) {
+ return (EINVAL);
+ }
+
+ pch = pchtemp_find_by_dev(*devp);
+ if (pch == NULL) {
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+pchtemp_ioctl_kind(intptr_t arg, int mode)
+{
+ sensor_ioctl_kind_t kind;
+
+ bzero(&kind, sizeof (sensor_ioctl_kind_t));
+ kind.sik_kind = SENSOR_KIND_TEMPERATURE;
+
+ if (ddi_copyout((void *)&kind, (void *)arg, sizeof (kind),
+ mode & FKIOCTL) != 0) {
+ return (EFAULT);
+ }
+
+ return (0);
+}
+
+static int
+pchtemp_ioctl_temp(pchtemp_t *pch, intptr_t arg, int mode)
+{
+ int ret;
+ sensor_ioctl_temperature_t temp;
+
+ bzero(&temp, sizeof (temp));
+
+ mutex_enter(&pch->pcht_mutex);
+ if ((ret = pchtemp_read(pch)) != 0) {
+ mutex_exit(&pch->pcht_mutex);
+ return (ret);
+ }
+
+ temp.sit_unit = SENSOR_UNIT_CELSIUS;
+ temp.sit_gran = PCHTEMP_TEMP_RESOLUTION;
+ temp.sit_temp = pch->pcht_temp;
+ mutex_exit(&pch->pcht_mutex);
+
+ if (ddi_copyout(&temp, (void *)arg, sizeof (temp),
+ mode & FKIOCTL) != 0) {
+ return (EFAULT);
+ }
+
+ return (0);
+}
+
+static int
+pchtemp_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+ int *rvalp)
+{
+ pchtemp_t *pch;
+
+ pch = pchtemp_find_by_dev(dev);
+ if (pch == NULL) {
+ return (ENXIO);
+ }
+
+ if ((mode & FREAD) == 0) {
+ return (EINVAL);
+ }
+
+ switch (cmd) {
+ case SENSOR_IOCTL_TYPE:
+ return (pchtemp_ioctl_kind(arg, mode));
+ case SENSOR_IOCTL_TEMPERATURE:
+ return (pchtemp_ioctl_temp(pch, arg, mode));
+ default:
+ return (ENOTTY);
+ }
+}
+
+static int
+pchtemp_close(dev_t dev, int flags, int otype, cred_t *credp)
+{
+ return (0);
+}
+
+static void
+pchtemp_cleanup(pchtemp_t *pch)
+{
+ int inst;
+
+ ASSERT3P(pch->pcht_dip, !=, NULL);
+ inst = ddi_get_instance(pch->pcht_dip);
+
+ ddi_remove_minor_node(pch->pcht_dip, NULL);
+
+ if (pch->pcht_handle != NULL) {
+ ddi_regs_map_free(&pch->pcht_handle);
+ }
+
+ if (pch->pcht_fm_caps != DDI_FM_NOT_CAPABLE) {
+ ddi_fm_fini(pch->pcht_dip);
+ }
+
+ mutex_destroy(&pch->pcht_mutex);
+ ddi_soft_state_free(pchtemp_state, inst);
+}
+
+static int
+pchtemp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int inst, ret;
+ pchtemp_t *pch;
+ off_t memsize;
+ ddi_device_acc_attr_t da;
+ ddi_iblock_cookie_t iblk;
+ char name[1024];
+
+ 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(pchtemp_state, inst) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "failed to allocate soft state entry %d",
+ inst);
+ return (DDI_FAILURE);
+ }
+
+ pch = ddi_get_soft_state(pchtemp_state, inst);
+ if (pch == NULL) {
+ dev_err(dip, CE_WARN, "failed to retrieve soft state entry %d",
+ inst);
+ return (DDI_FAILURE);
+ }
+ pch->pcht_dip = dip;
+
+ pch->pcht_fm_caps = DDI_FM_ACCCHK_CAPABLE;
+ ddi_fm_init(dip, &pch->pcht_fm_caps, &iblk);
+
+ mutex_init(&pch->pcht_mutex, NULL, MUTEX_DRIVER, NULL);
+
+ if (ddi_dev_regsize(dip, PCHTEMP_RNUMBER, &memsize) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "failed to obtain register size for "
+ "register set %d", PCHTEMP_RNUMBER);
+ goto err;
+ }
+
+ bzero(&da, sizeof (ddi_device_acc_attr_t));
+ da.devacc_attr_version = DDI_DEVICE_ATTR_V0;
+ da.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
+ da.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
+
+ if (DDI_FM_ACC_ERR_CAP(pch->pcht_fm_caps)) {
+ da.devacc_attr_access = DDI_FLAGERR_ACC;
+ } else {
+ da.devacc_attr_access = DDI_DEFAULT_ACC;
+ }
+
+ if ((ret = ddi_regs_map_setup(dip, PCHTEMP_RNUMBER, &pch->pcht_base,
+ 0, memsize, &da, &pch->pcht_handle)) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "failed to map register set %d: %d",
+ PCHTEMP_RNUMBER, ret);
+ goto err;
+ }
+
+ if (snprintf(name, sizeof (name), "ts.%d", inst) >= sizeof (name)) {
+ dev_err(dip, CE_WARN, "failed to construct minor node name, "
+ "name too long");
+ goto err;
+ }
+
+ if (ddi_create_minor_node(pch->pcht_dip, name, S_IFCHR, (minor_t)inst,
+ DDI_NT_SENSOR_TEMP_PCH, 0) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "failed to create minor node %s", name);
+ goto err;
+ }
+
+ /*
+ * Attempt a single read to lock in the temperature. We don't mind if
+ * this fails for some reason.
+ */
+ mutex_enter(&pch->pcht_mutex);
+ (void) pchtemp_read(pch);
+ mutex_exit(&pch->pcht_mutex);
+
+ return (DDI_SUCCESS);
+
+err:
+ pchtemp_cleanup(pch);
+ return (DDI_FAILURE);
+}
+
+static int
+pchtemp_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
+ void **resultp)
+{
+ pchtemp_t *pch;
+
+ switch (cmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ pch = pchtemp_find_by_dev((dev_t)arg);
+ if (pch == NULL) {
+ return (DDI_FAILURE);
+ }
+
+ *resultp = pch->pcht_dip;
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ *resultp = (void *)(uintptr_t)getminor((dev_t)arg);
+ break;
+ default:
+ return (DDI_FAILURE);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+static int
+pchtemp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ int inst;
+ pchtemp_t *pch;
+
+ switch (cmd) {
+ case DDI_DETACH:
+ break;
+ case DDI_SUSPEND:
+ return (DDI_SUCCESS);
+ default:
+ return (DDI_FAILURE);
+ }
+
+ inst = ddi_get_instance(dip);
+ pch = ddi_get_soft_state(pchtemp_state, inst);
+ if (pch == NULL) {
+ dev_err(dip, CE_WARN, "asked to detached instance %d, but "
+ "it does not exist in soft state", inst);
+ return (DDI_FAILURE);
+ }
+
+ pchtemp_cleanup(pch);
+ return (DDI_SUCCESS);
+}
+
+static struct cb_ops pchtemp_cb_ops = {
+ .cb_open = pchtemp_open,
+ .cb_close = pchtemp_close,
+ .cb_strategy = nodev,
+ .cb_print = nodev,
+ .cb_dump = nodev,
+ .cb_read = nodev,
+ .cb_write = nodev,
+ .cb_ioctl = pchtemp_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 pchtemp_dev_ops = {
+ .devo_rev = DEVO_REV,
+ .devo_refcnt = 0,
+ .devo_getinfo = pchtemp_getinfo,
+ .devo_identify = nulldev,
+ .devo_probe = nulldev,
+ .devo_attach = pchtemp_attach,
+ .devo_detach = pchtemp_detach,
+ .devo_reset = nodev,
+ .devo_power = ddi_power,
+ .devo_quiesce = ddi_quiesce_not_needed,
+ .devo_cb_ops = &pchtemp_cb_ops
+};
+
+static struct modldrv pchtemp_modldrv = {
+ .drv_modops = &mod_driverops,
+ .drv_linkinfo = "Intel PCH Thermal Sensor",
+ .drv_dev_ops = &pchtemp_dev_ops
+};
+
+static struct modlinkage pchtemp_modlinkage = {
+ .ml_rev = MODREV_1,
+ .ml_linkage = { &pchtemp_modldrv, NULL }
+};
+
+int
+_init(void)
+{
+ int ret;
+
+ if (ddi_soft_state_init(&pchtemp_state, sizeof (pchtemp_t), 1) !=
+ DDI_SUCCESS) {
+ return (ENOMEM);
+ }
+
+ if ((ret = mod_install(&pchtemp_modlinkage)) != 0) {
+ ddi_soft_state_fini(&pchtemp_state);
+ return (ret);
+ }
+
+ return (ret);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&pchtemp_modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ int ret;
+
+ if ((ret = mod_remove(&pchtemp_modlinkage)) != 0) {
+ return (ret);
+ }
+
+ ddi_soft_state_fini(&pchtemp_state);
+ return (ret);
+}
diff --git a/usr/src/uts/intel/pchtemp/Makefile b/usr/src/uts/intel/pchtemp/Makefile
new file mode 100644
index 0000000000..9473aecceb
--- /dev/null
+++ b/usr/src/uts/intel/pchtemp/Makefile
@@ -0,0 +1,46 @@
+#
+# 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 Joyent, Inc.
+#
+
+UTSBASE = ../..
+
+MODULE = pchtemp
+OBJECTS = $(PCHTEMP_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ