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