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