xref: /illumos-gate/usr/src/uts/common/io/igb/igb_sensor.c (revision a92282e44f968185a6bba094d1e5fece2da819cf)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2020 Oxide Computer Company
14  */
15 
16 /*
17  * Handle and report sensors found on some igb parts.
18  *
19  * The Intel I350 has a built-in thermal sensor diode and an optional External
20  * Thermal Sensor configuration. This external configuration is provided through
21  * an optional space in the NVM and allows for up to 4 external sensors to be
22  * defined. Currently, the only defined external thermal sensor is the Microchip
23  * EMC 1413. As of this time, we haven't encountered a device that uses the EMC
24  * 1413 in the wild, so while the definitions here are present, that is stubbed
25  * out for the time.
26  *
27  * When accessing the internal sensor, the I350 Datasheet requires that we take
28  * software/firmware semaphore before proceeding.
29  */
30 
31 #include "igb_sw.h"
32 #include <sys/sensors.h>
33 #include <sys/bitmap.h>
34 
35 /*
36  * Thermal register values.
37  */
38 #define	E1000_THMJT_TEMP(x)	BITX(x, 8, 0)
39 #define	E1000_THMJT_VALID(x)	BITX(x, 31, 31)
40 #define	E1000_THMJT_RESOLUTION	1
41 #define	E1000_THMJT_PRECISION	5
42 
43 /*
44  * Misc. definitions required for accessing the NVM space.
45  */
46 #define	IGB_NVM_ETS_CFG	0x3e
47 #define	IGB_NVM_ETS_CFG_NSENSORS(x)	BITX(x, 2, 0)
48 #define	IGB_NVM_ETS_CFG_TYPE(x)		BITX(x, 5, 3)
49 #define	IGB_NVM_ETS_CFG_TYPE_EMC1413	0
50 
51 #define	IGB_NVM_ETS_SENSOR_LOC(x)	BITX(x, 13, 10)
52 #define	IGB_NVM_ETS_SENSOR_INDEX(x)	BITX(x, 9, 8)
53 #define	IGB_NVM_ETS_SENSOR_THRESH(x)	BITX(x, 7, 0)
54 
55 #define	IGB_ETS_I2C_ADDRESS	0xf8
56 
57 /*
58  * These definitions come from the Microchip datasheet for the thermal diode
59  * sensor defined by the external spec. These parts have an accuracy of 1 degree
60  * and a granularity of 1/8th of a degree.
61  */
62 #define	EMC1413_REG_CFG			0x03
63 #define	EMC1413_REG_CFG_RANGE		(1 << 2)
64 #define	EMC1413_RANGE_ADJ		(-64)
65 #define	EMC1413_REG_INT_DIODE_HI	0x00
66 #define	EMC1413_REG_INT_DIODE_LO	0x29
67 #define	EMC1413_REG_EXT1_DIODE_HI	0x01
68 #define	EMC1413_REG_EXT1_DIODE_LO	0x10
69 #define	EMC1413_REG_EXT2_DIODE_HI	0x23
70 #define	EMC1413_REG_EXT2_DIODE_LO	0x24
71 #define	EMC1413_REG_EXT3_DIODE_HI	0x2a
72 #define	EMC1413_REG_EXT3_DIODE_LO	0x2b
73 
74 static int
75 igb_sensor_reg_temperature(void *arg, sensor_ioctl_scalar_t *scalar)
76 {
77 	igb_t *igb = arg;
78 	uint32_t reg;
79 
80 	if (igb->hw.mac.ops.acquire_swfw_sync(&igb->hw, E1000_SWFW_PWRTS_SM) !=
81 	    E1000_SUCCESS) {
82 		return (EIO);
83 	}
84 	reg = E1000_READ_REG(&igb->hw, E1000_THMJT);
85 	igb->hw.mac.ops.release_swfw_sync(&igb->hw, E1000_SWFW_PWRTS_SM);
86 	if (E1000_THMJT_VALID(reg) == 0) {
87 		return (EIO);
88 	}
89 
90 	scalar->sis_unit = SENSOR_UNIT_CELSIUS;
91 	scalar->sis_gran = E1000_THMJT_RESOLUTION;
92 	scalar->sis_prec = E1000_THMJT_PRECISION;
93 	scalar->sis_value = E1000_THMJT_TEMP(reg);
94 
95 	return (0);
96 }
97 
98 static const ksensor_ops_t igb_sensor_reg_ops = {
99 	.kso_kind = ksensor_kind_temperature,
100 	.kso_scalar = igb_sensor_reg_temperature
101 };
102 
103 static boolean_t
104 igb_sensors_create_minors(igb_t *igb)
105 {
106 	int ret;
107 	igb_sensors_t *sp = &igb->igb_sensors;
108 
109 	if ((ret = ksensor_create_scalar_pcidev(igb->dip,
110 	    SENSOR_KIND_TEMPERATURE, &igb_sensor_reg_ops, igb, "builtin",
111 	    &sp->isn_reg_ksensor)) != 0) {
112 		igb_log(igb, IGB_LOG_ERROR, "failed to create main sensor: %d",
113 		    ret);
114 		return (B_FALSE);
115 	}
116 
117 	return (B_TRUE);
118 }
119 
120 static boolean_t
121 igb_sensors_init_ets(igb_t *igb, uint_t ets_off, uint_t index)
122 {
123 	uint16_t val;
124 	int ret;
125 	igb_sensors_t *sensors = &igb->igb_sensors;
126 	igb_ets_t *etsp = &sensors->isn_ets[sensors->isn_nents];
127 	igb_ets_loc_t loc;
128 
129 	if ((ret = e1000_read_nvm(&igb->hw, ets_off, 1, &val)) !=
130 	    E1000_SUCCESS) {
131 		igb_log(igb, IGB_LOG_ERROR, "failed to read ETS word "
132 		    "at offset 0x%x: error %d", ets_off, ret);
133 		return (B_FALSE);
134 	}
135 
136 	/*
137 	 * The data sheet says that if the location is listed as N/A, then we
138 	 * should not display this sensor. In this case, we just skip it.
139 	 */
140 	loc = IGB_NVM_ETS_SENSOR_LOC(val);
141 	if (loc == IGB_ETS_LOC_NA) {
142 		return (B_TRUE);
143 	}
144 
145 	etsp->iet_loc = loc;
146 	etsp->iet_index = IGB_NVM_ETS_SENSOR_INDEX(val);
147 	etsp->iet_thresh = IGB_NVM_ETS_SENSOR_THRESH(val);
148 	sensors->isn_nents++;
149 
150 	return (B_TRUE);
151 }
152 
153 void
154 igb_init_sensors(igb_t *igb)
155 {
156 	struct e1000_hw *hw = &igb->hw;
157 	uint16_t ets_off;
158 
159 	/*
160 	 * Only the I350 supports the thermal temperature sensor values. This is
161 	 * device-wide, so only enumerate on bus zero.
162 	 */
163 	hw = &igb->hw;
164 	if (hw->mac.type != e1000_i350 || hw->bus.func != 0) {
165 		return;
166 	}
167 
168 	ets_off = 0xffff;
169 	(void) e1000_read_nvm(hw, IGB_NVM_ETS_CFG, 1, &ets_off);
170 	if (ets_off != 0 && ets_off != 0xffff) {
171 		int ret;
172 		uint_t nents, i;
173 		uint16_t val;
174 
175 		/*
176 		 * Swallow the fact that we can't read the ETS config.
177 		 */
178 		if ((ret = e1000_read_nvm(hw, ets_off, 1, &val)) !=
179 		    E1000_SUCCESS) {
180 			igb_log(igb, IGB_LOG_ERROR, "failed to read ETS word "
181 			    "at offset 0x%x: error %d", ets_off, ret);
182 			return;
183 		}
184 
185 		/*
186 		 * If we don't find this, assume we can't use the external
187 		 * sensor either.
188 		 */
189 		if (IGB_NVM_ETS_CFG_TYPE(val) != IGB_NVM_ETS_CFG_TYPE_EMC1413) {
190 			return;
191 		}
192 
193 		nents = IGB_NVM_ETS_CFG_NSENSORS(val);
194 		if (nents > IGB_ETS_MAX) {
195 			igb_log(igb, IGB_LOG_ERROR, "firmware NVM ETS "
196 			    "configuration has more entries (%d) than allowed",
197 			    nents);
198 			nents = IGB_ETS_MAX;
199 		}
200 
201 		for (i = 0; i < nents; i++) {
202 			if (!igb_sensors_init_ets(igb, ets_off, i)) {
203 				return;
204 			}
205 		}
206 	}
207 
208 	if (!igb_sensors_create_minors(igb)) {
209 		(void) ksensor_remove(igb->dip, KSENSOR_ALL_IDS);
210 		return;
211 	}
212 
213 	igb->igb_sensors.isn_valid = B_TRUE;
214 }
215 
216 void
217 igb_fini_sensors(igb_t *igb)
218 {
219 	if (igb->igb_sensors.isn_valid) {
220 		(void) ksensor_remove(igb->dip, KSENSOR_ALL_IDS);
221 		igb->igb_sensors.isn_valid = B_FALSE;
222 	}
223 }
224