changeset 25208:b370e054b126

12835 Want support for I350 temperature sensor Reviewed by: Ryan Zezeski <ryan@zinascii.com> Reviewed by: Toomas Soome <toomas@me.com> Approved by: Dan McDonald <danmcd@joyent.com>
author Robert Mustacchi <rm@fingolfin.org>
date Fri, 05 Jun 2020 09:22:48 -0700
parents c4c7c8c14ae3
children 86d9f1b34087 25844479db93
files usr/src/uts/common/Makefile.files usr/src/uts/common/io/e1000api/e1000_defines.h usr/src/uts/common/io/igb/igb_main.c usr/src/uts/common/io/igb/igb_sensor.c usr/src/uts/common/io/igb/igb_sw.h usr/src/uts/intel/igb/Makefile
diffstat 6 files changed, 281 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/common/Makefile.files	Tue May 05 22:21:07 2020 -0700
+++ b/usr/src/uts/common/Makefile.files	Fri Jun 05 09:22:48 2020 -0700
@@ -2003,7 +2003,7 @@
 #	Intel 82575 1G NIC driver module
 #
 IGB_OBJS =	igb_buf.o igb_debug.o igb_gld.o igb_log.o igb_main.o \
-		igb_rx.o igb_stat.o igb_tx.o igb_osdep.o
+		igb_rx.o igb_stat.o igb_tx.o igb_osdep.o igb_sensor.o
 
 #
 #	Intel Pro/100 NIC driver module
--- a/usr/src/uts/common/io/e1000api/e1000_defines.h	Tue May 05 22:21:07 2020 -0700
+++ b/usr/src/uts/common/io/e1000api/e1000_defines.h	Fri Jun 05 09:22:48 2020 -0700
@@ -252,6 +252,7 @@
 #define E1000_SWFW_CSR_SM	0x08
 #define E1000_SWFW_PHY2_SM	0x20
 #define E1000_SWFW_PHY3_SM	0x40
+#define	E1000_SWFW_PWRTS_SM	0x80
 #define E1000_SWFW_SW_MNG_SM	0x400
 
 /* Device Control */
--- a/usr/src/uts/common/io/igb/igb_main.c	Tue May 05 22:21:07 2020 -0700
+++ b/usr/src/uts/common/io/igb/igb_main.c	Fri Jun 05 09:22:48 2020 -0700
@@ -579,6 +579,12 @@
 	igb->attach_progress |= ATTACH_PROGRESS_INIT_ADAPTER;
 
 	/*
+	 * Initialize sensors. This swallows any errors to ensure that access to
+	 * the network is still available.
+	 */
+	igb_init_sensors(igb);
+
+	/*
 	 * Initialize statistics
 	 */
 	if (igb_init_stats(igb) != IGB_SUCCESS) {
@@ -831,6 +837,11 @@
 	}
 
 	/*
+	 * Clean up sensors
+	 */
+	igb_fini_sensors(igb);
+
+	/*
 	 * Free multicast table
 	 */
 	igb_release_multicast(igb);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/igb/igb_sensor.c	Fri Jun 05 09:22:48 2020 -0700
@@ -0,0 +1,222 @@
+/*
+ * 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 2020 Oxide Computer Company
+ */
+
+/*
+ * Handle and report sensors found on some igb parts.
+ *
+ * The Intel I350 has a built-in thermal sensor diode and an optional External
+ * Thermal Sensor configuration. This external configuration is provided through
+ * an optional space in the NVM and allows for up to 4 external sensors to be
+ * defined. Currently, the only defined external thermal sensor is the Microchip
+ * EMC 1413. As of this time, we haven't encountered a device that uses the EMC
+ * 1413 in the wild, so while the definitions here are present, that is stubbed
+ * out for the time.
+ *
+ * When accessing the internal sensor, the I350 Datasheet requires that we take
+ * software/firmware semaphore before proceeding.
+ */
+
+#include "igb_sw.h"
+#include <sys/sensors.h>
+#include <sys/bitmap.h>
+
+/*
+ * Thermal register values.
+ */
+#define	E1000_THMJT_TEMP(x)	BITX(x, 8, 0)
+#define	E1000_THMJT_VALID(x)	BITX(x, 31, 31)
+#define	E1000_THMJT_RESOLUTION	1
+#define	E1000_THMJT_PRECISION	5
+
+/*
+ * Misc. definitions required for accessing the NVM space.
+ */
+#define	IGB_NVM_ETS_CFG	0x3e
+#define	IGB_NVM_ETS_CFG_NSENSORS(x)	BITX(x, 2, 0)
+#define	IGB_NVM_ETS_CFG_TYPE(x)		BITX(x, 5, 3)
+#define	IGB_NVM_ETS_CFG_TYPE_EMC1413	0
+
+#define	IGB_NVM_ETS_SENSOR_LOC(x)	BITX(x, 13, 10)
+#define	IGB_NVM_ETS_SENSOR_INDEX(x)	BITX(x, 9, 8)
+#define	IGB_NVM_ETS_SENSOR_THRESH(x)	BITX(x, 7, 0)
+
+#define	IGB_ETS_I2C_ADDRESS	0xf8
+
+/*
+ * These definitions come from the Microchip datasheet for the thermal diode
+ * sensor defined by the external spec. These parts have an accuracy of 1 degree
+ * and a granularity of 1/8th of a degree.
+ */
+#define	EMC1413_REG_CFG			0x03
+#define	EMC1413_REG_CFG_RANGE		(1 << 2)
+#define	EMC1413_RANGE_ADJ		(-64)
+#define	EMC1413_REG_INT_DIODE_HI	0x00
+#define	EMC1413_REG_INT_DIODE_LO	0x29
+#define	EMC1413_REG_EXT1_DIODE_HI	0x01
+#define	EMC1413_REG_EXT1_DIODE_LO	0x10
+#define	EMC1413_REG_EXT2_DIODE_HI	0x23
+#define	EMC1413_REG_EXT2_DIODE_LO	0x24
+#define	EMC1413_REG_EXT3_DIODE_HI	0x2a
+#define	EMC1413_REG_EXT3_DIODE_LO	0x2b
+
+static int
+igb_sensor_reg_temp(void *arg, sensor_ioctl_temperature_t *temp)
+{
+	igb_t *igb = arg;
+	uint32_t reg;
+
+	if (igb->hw.mac.ops.acquire_swfw_sync(&igb->hw, E1000_SWFW_PWRTS_SM) !=
+	    E1000_SUCCESS) {
+		return (EIO);
+	}
+	reg = E1000_READ_REG(&igb->hw, E1000_THMJT);
+	igb->hw.mac.ops.release_swfw_sync(&igb->hw, E1000_SWFW_PWRTS_SM);
+	if (E1000_THMJT_VALID(reg) == 0) {
+		return (EIO);
+	}
+
+	temp->sit_unit = SENSOR_UNIT_CELSIUS;
+	temp->sit_gran = E1000_THMJT_RESOLUTION;
+	temp->sit_prec = E1000_THMJT_PRECISION;
+	temp->sit_temp = E1000_THMJT_TEMP(reg);
+
+	return (0);
+}
+
+static const ksensor_ops_t igb_sensor_reg_ops = {
+	.kso_kind = ksensor_kind_temperature,
+	.kso_temp = igb_sensor_reg_temp
+};
+
+static boolean_t
+igb_sensors_create_minors(igb_t *igb)
+{
+	int ret;
+	igb_sensors_t *sp = &igb->igb_sensors;
+
+	if ((ret = ksensor_create_temp_pcidev(igb->dip, &igb_sensor_reg_ops,
+	    igb, "builtin", &sp->isn_reg_ksensor)) != 0) {
+		igb_log(igb, IGB_LOG_ERROR, "failed to create main sensor: %d",
+		    ret);
+		return (B_FALSE);
+	}
+
+	return (B_TRUE);
+}
+
+static boolean_t
+igb_sensors_init_ets(igb_t *igb, uint_t ets_off, uint_t index)
+{
+	uint16_t val;
+	int ret;
+	igb_sensors_t *sensors = &igb->igb_sensors;
+	igb_ets_t *etsp = &sensors->isn_ets[sensors->isn_nents];
+	igb_ets_loc_t loc;
+
+	if ((ret = e1000_read_nvm(&igb->hw, ets_off, 1, &val)) !=
+	    E1000_SUCCESS) {
+		igb_log(igb, IGB_LOG_ERROR, "failed to read ETS word "
+		    "at offset 0x%x: error %d", ets_off, ret);
+		return (B_FALSE);
+	}
+
+	/*
+	 * The data sheet says that if the location is listed as N/A, then we
+	 * should not display this sensor. In this case, we just skip it.
+	 */
+	loc = IGB_NVM_ETS_SENSOR_LOC(val);
+	if (loc == IGB_ETS_LOC_NA) {
+		return (B_TRUE);
+	}
+
+	etsp->iet_loc = loc;
+	etsp->iet_index = IGB_NVM_ETS_SENSOR_INDEX(val);
+	etsp->iet_thresh = IGB_NVM_ETS_SENSOR_THRESH(val);
+	sensors->isn_nents++;
+
+	return (B_TRUE);
+}
+
+void
+igb_init_sensors(igb_t *igb)
+{
+	struct e1000_hw *hw = &igb->hw;
+	uint16_t ets_off;
+
+	/*
+	 * Only the I350 supports the thermal temperature sensor values. This is
+	 * device-wide, so only enumerate on bus zero.
+	 */
+	hw = &igb->hw;
+	if (hw->mac.type != e1000_i350 || hw->bus.func != 0) {
+		return;
+	}
+
+	ets_off = 0xffff;
+	(void) e1000_read_nvm(hw, IGB_NVM_ETS_CFG, 1, &ets_off);
+	if (ets_off != 0 && ets_off != 0xffff) {
+		int ret;
+		uint_t nents, i;
+		uint16_t val;
+
+		/*
+		 * Swallow the fact that we can't read the ETS config.
+		 */
+		if ((ret = e1000_read_nvm(hw, ets_off, 1, &val)) !=
+		    E1000_SUCCESS) {
+			igb_log(igb, IGB_LOG_ERROR, "failed to read ETS word "
+			    "at offset 0x%x: error %d", ets_off, ret);
+			return;
+		}
+
+		/*
+		 * If we don't find this, assume we can't use the external
+		 * sensor either.
+		 */
+		if (IGB_NVM_ETS_CFG_TYPE(val) != IGB_NVM_ETS_CFG_TYPE_EMC1413) {
+			return;
+		}
+
+		nents = IGB_NVM_ETS_CFG_NSENSORS(val);
+		if (nents > IGB_ETS_MAX) {
+			igb_log(igb, IGB_LOG_ERROR, "firmware NVM ETS "
+			    "configuration has more entries (%d) than allowed",
+			    nents);
+			nents = IGB_ETS_MAX;
+		}
+
+		for (i = 0; i < nents; i++) {
+			if (!igb_sensors_init_ets(igb, ets_off, i)) {
+				return;
+			}
+		}
+	}
+
+	if (!igb_sensors_create_minors(igb)) {
+		(void) ksensor_remove(igb->dip, KSENSOR_ALL_IDS);
+		return;
+	}
+
+	igb->igb_sensors.isn_valid = B_TRUE;
+}
+
+void
+igb_fini_sensors(igb_t *igb)
+{
+	if (igb->igb_sensors.isn_valid) {
+		(void) ksensor_remove(igb->dip, KSENSOR_ALL_IDS);
+		igb->igb_sensors.isn_valid = B_FALSE;
+	}
+}
--- a/usr/src/uts/common/io/igb/igb_sw.h	Tue May 05 22:21:07 2020 -0700
+++ b/usr/src/uts/common/io/igb/igb_sw.h	Fri Jun 05 09:22:48 2020 -0700
@@ -561,6 +561,44 @@
 	struct igb		*igb;		/* Pointer to igb struct */
 } igb_rx_group_t;
 
+typedef enum {
+	IGB_ETS_INDEX_INTERNAL		= 0,
+	IGB_ETS_INDEX_EXTERNAL_1	= 1,
+	IGB_ETS_INDEX_EXTERNAL_2	= 2,
+	IGB_ETS_INDEX_EXTERNAL_3	= 3
+} igb_ets_index_t;
+
+typedef enum {
+	IGB_ETS_LOC_NA		= 0,
+	IGB_ETS_LOC_HOT_SPOT	= 2,
+	IGB_ETS_LOC_PCIE	= 3,
+	IGB_ETS_LOC_BULKHEAD	= 4,
+	IGB_ETS_LOC_BOARD	= 5,
+	IGB_ETS_LOC_INLET	= 7
+} igb_ets_loc_t;
+
+/*
+ * Sensor data
+ */
+typedef struct igb_ets {
+	igb_ets_index_t	iet_index;
+	igb_ets_loc_t	iet_loc;
+	uint8_t		iet_thresh;
+	id_t		iet_ksensor;
+} igb_ets_t;
+
+/*
+ * There are only four words defined for sensors.
+ */
+#define	IGB_ETS_MAX	4
+
+typedef struct igb_sensors {
+	boolean_t isn_valid;
+	id_t isn_reg_ksensor;
+	uint_t isn_nents;
+	igb_ets_t isn_ets[IGB_ETS_MAX];
+} igb_sensors_t;
+
 typedef struct igb {
 	int			instance;
 	mac_handle_t		mac_hdl;
@@ -737,6 +775,7 @@
 
 	ulong_t			page_size;
 	ddi_ufm_handle_t	*igb_ufmh;
+	igb_sensors_t		igb_sensors;
 } igb_t;
 
 typedef struct igb_stat {
@@ -876,6 +915,12 @@
 int igb_rx_ring_stat(mac_ring_driver_t, uint_t, uint64_t *);
 int igb_tx_ring_stat(mac_ring_driver_t, uint_t, uint64_t *);
 
+/*
+ * Function prootypes in igb_sesnor.c
+ */
+void igb_init_sensors(igb_t *);
+void igb_fini_sensors(igb_t *);
+
 #ifdef __cplusplus
 }
 #endif
--- a/usr/src/uts/intel/igb/Makefile	Tue May 05 22:21:07 2020 -0700
+++ b/usr/src/uts/intel/igb/Makefile	Fri Jun 05 09:22:48 2020 -0700
@@ -69,7 +69,7 @@
 # Driver depends on MAC
 #
 LDFLAGS		+= -dy -N misc/mac
-MAPFILES	+= ddi mac random kernel
+MAPFILES	+= ddi mac random kernel ksensor
 
 #
 #	Default build targets.