xref: /illumos-gate/usr/src/uts/common/io/igb/igb_sensor.c (revision 2509632a43b285708c172d4221818e8b79cea126)
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_temp(void *arg, sensor_ioctl_temperature_t *temp)
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 	temp->sit_unit = SENSOR_UNIT_CELSIUS;
91 	temp->sit_gran = E1000_THMJT_RESOLUTION;
92 	temp->sit_prec = E1000_THMJT_PRECISION;
93 	temp->sit_temp = 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_temp = igb_sensor_reg_temp
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_temp_pcidev(igb->dip, &igb_sensor_reg_ops,
110 	    igb, "builtin", &sp->isn_reg_ksensor)) != 0) {
111 		igb_log(igb, IGB_LOG_ERROR, "failed to create main sensor: %d",
112 		    ret);
113 		return (B_FALSE);
114 	}
115 
116 	return (B_TRUE);
117 }
118 
119 static boolean_t
120 igb_sensors_init_ets(igb_t *igb, uint_t ets_off, uint_t index)
121 {
122 	uint16_t val;
123 	int ret;
124 	igb_sensors_t *sensors = &igb->igb_sensors;
125 	igb_ets_t *etsp = &sensors->isn_ets[sensors->isn_nents];
126 	igb_ets_loc_t loc;
127 
128 	if ((ret = e1000_read_nvm(&igb->hw, ets_off, 1, &val)) !=
129 	    E1000_SUCCESS) {
130 		igb_log(igb, IGB_LOG_ERROR, "failed to read ETS word "
131 		    "at offset 0x%x: error %d", ets_off, ret);
132 		return (B_FALSE);
133 	}
134 
135 	/*
136 	 * The data sheet says that if the location is listed as N/A, then we
137 	 * should not display this sensor. In this case, we just skip it.
138 	 */
139 	loc = IGB_NVM_ETS_SENSOR_LOC(val);
140 	if (loc == IGB_ETS_LOC_NA) {
141 		return (B_TRUE);
142 	}
143 
144 	etsp->iet_loc = loc;
145 	etsp->iet_index = IGB_NVM_ETS_SENSOR_INDEX(val);
146 	etsp->iet_thresh = IGB_NVM_ETS_SENSOR_THRESH(val);
147 	sensors->isn_nents++;
148 
149 	return (B_TRUE);
150 }
151 
152 void
153 igb_init_sensors(igb_t *igb)
154 {
155 	struct e1000_hw *hw = &igb->hw;
156 	uint16_t ets_off;
157 
158 	/*
159 	 * Only the I350 supports the thermal temperature sensor values. This is
160 	 * device-wide, so only enumerate on bus zero.
161 	 */
162 	hw = &igb->hw;
163 	if (hw->mac.type != e1000_i350 || hw->bus.func != 0) {
164 		return;
165 	}
166 
167 	ets_off = 0xffff;
168 	(void) e1000_read_nvm(hw, IGB_NVM_ETS_CFG, 1, &ets_off);
169 	if (ets_off != 0 && ets_off != 0xffff) {
170 		int ret;
171 		uint_t nents, i;
172 		uint16_t val;
173 
174 		/*
175 		 * Swallow the fact that we can't read the ETS config.
176 		 */
177 		if ((ret = e1000_read_nvm(hw, ets_off, 1, &val)) !=
178 		    E1000_SUCCESS) {
179 			igb_log(igb, IGB_LOG_ERROR, "failed to read ETS word "
180 			    "at offset 0x%x: error %d", ets_off, ret);
181 			return;
182 		}
183 
184 		/*
185 		 * If we don't find this, assume we can't use the external
186 		 * sensor either.
187 		 */
188 		if (IGB_NVM_ETS_CFG_TYPE(val) != IGB_NVM_ETS_CFG_TYPE_EMC1413) {
189 			return;
190 		}
191 
192 		nents = IGB_NVM_ETS_CFG_NSENSORS(val);
193 		if (nents > IGB_ETS_MAX) {
194 			igb_log(igb, IGB_LOG_ERROR, "firmware NVM ETS "
195 			    "configuration has more entries (%d) than allowed",
196 			    nents);
197 			nents = IGB_ETS_MAX;
198 		}
199 
200 		for (i = 0; i < nents; i++) {
201 			if (!igb_sensors_init_ets(igb, ets_off, i)) {
202 				return;
203 			}
204 		}
205 	}
206 
207 	if (!igb_sensors_create_minors(igb)) {
208 		(void) ksensor_remove(igb->dip, KSENSOR_ALL_IDS);
209 		return;
210 	}
211 
212 	igb->igb_sensors.isn_valid = B_TRUE;
213 }
214 
215 void
216 igb_fini_sensors(igb_t *igb)
217 {
218 	if (igb->igb_sensors.isn_valid) {
219 		(void) ksensor_remove(igb->dip, KSENSOR_ALL_IDS);
220 		igb->igb_sensors.isn_valid = B_FALSE;
221 	}
222 }
223