1 /* 2 * Copyright (c) 2015 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Sepherosa Ziehau <sepherosa@gmail.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/bitops.h> 38 #include <sys/bus.h> 39 #include <sys/cpu_topology.h> 40 #include <sys/kernel.h> 41 #include <sys/malloc.h> 42 #include <sys/sensors.h> 43 #include <sys/sysctl.h> 44 45 #include <bus/pci/pcivar.h> 46 #include <bus/pci/pcireg.h> 47 #include <bus/pci/pcibus.h> 48 #include <bus/pci/pci_cfgreg.h> 49 #include <bus/pci/pcib_private.h> 50 51 #include "pcib_if.h" 52 53 #include <dev/misc/dimm/dimm.h> 54 #include <dev/misc/ecc/e5_imc_reg.h> 55 #include <dev/misc/ecc/e5_imc_var.h> 56 57 #define MEMTEMP_E5_DIMM_TEMP_HIWAT 85 /* spec default TEMPLO */ 58 #define MEMTEMP_E5_DIMM_TEMP_STEP 5 /* spec TEMPLO/MID/HI step */ 59 60 struct memtemp_e5_softc; 61 62 struct memtemp_e5_dimm { 63 TAILQ_ENTRY(memtemp_e5_dimm) dimm_link; 64 struct ksensor dimm_sensor; 65 struct memtemp_e5_softc *dimm_parent; 66 int dimm_id; 67 int dimm_flags; 68 69 struct dimm_softc *dimm_softc; 70 struct sensor_task *dimm_senstask; 71 }; 72 73 #define MEMTEMP_E5_DIMM_FLAG_CRIT 0x1 74 75 struct memtemp_e5_softc { 76 device_t temp_dev; 77 const struct e5_imc_chan *temp_chan; 78 int temp_node; 79 TAILQ_HEAD(, memtemp_e5_dimm) temp_dimm; 80 }; 81 82 static int memtemp_e5_probe(device_t); 83 static int memtemp_e5_attach(device_t); 84 static int memtemp_e5_detach(device_t); 85 86 static int memtemp_e5_tempth_adjust(int); 87 static void memtemp_e5_tempth_str(int, char *, int); 88 static void memtemp_e5_sensor_task(void *); 89 90 #define MEMTEMP_E5_CHAN(v, imc, c, c_ext) \ 91 { \ 92 .did = PCI_E5V##v##_IMC##imc##_THERMAL_CHN##c##_DID_ID, \ 93 .slot = PCISLOT_E5V##v##_IMC##imc##_THERMAL_CHN##c, \ 94 .func = PCIFUNC_E5V##v##_IMC##imc##_THERMAL_CHN##c, \ 95 .desc = "Intel E5 v" #v " memory thermal sensor", \ 96 \ 97 E5_IMC_CHAN_FIELDS(v, imc, c, c_ext) \ 98 } 99 100 #define MEMTEMP_E5_CHAN_V2(c) MEMTEMP_E5_CHAN(2, 0, c, c) 101 #define MEMTEMP_E5_CHAN_IMC0_V3(c) MEMTEMP_E5_CHAN(3, 0, c, c) 102 #define MEMTEMP_E5_CHAN_IMC1_V3(c, c_ext) \ 103 MEMTEMP_E5_CHAN(3, 1, c, c_ext) 104 #define MEMTEMP_E5_CHAN_END E5_IMC_CHAN_END 105 106 static const struct e5_imc_chan memtemp_e5_chans[] = { 107 MEMTEMP_E5_CHAN_V2(0), 108 MEMTEMP_E5_CHAN_V2(1), 109 MEMTEMP_E5_CHAN_V2(2), 110 MEMTEMP_E5_CHAN_V2(3), 111 112 MEMTEMP_E5_CHAN_IMC0_V3(0), 113 MEMTEMP_E5_CHAN_IMC0_V3(1), 114 MEMTEMP_E5_CHAN_IMC0_V3(2), 115 MEMTEMP_E5_CHAN_IMC0_V3(3), 116 MEMTEMP_E5_CHAN_IMC1_V3(0, 2), /* IMC1 chan0 -> channel2 */ 117 MEMTEMP_E5_CHAN_IMC1_V3(1, 3), /* IMC1 chan1 -> channel3 */ 118 119 MEMTEMP_E5_CHAN_END 120 }; 121 122 #undef MEMTEMP_E5_CHAN_END 123 #undef MEMTEMP_E5_CHAN_V2 124 #undef MEMTEMP_E5_CHAN 125 126 static device_method_t memtemp_e5_methods[] = { 127 /* Device interface */ 128 DEVMETHOD(device_probe, memtemp_e5_probe), 129 DEVMETHOD(device_attach, memtemp_e5_attach), 130 DEVMETHOD(device_detach, memtemp_e5_detach), 131 DEVMETHOD(device_shutdown, bus_generic_shutdown), 132 DEVMETHOD(device_suspend, bus_generic_suspend), 133 DEVMETHOD(device_resume, bus_generic_resume), 134 DEVMETHOD_END 135 }; 136 137 static driver_t memtemp_e5_driver = { 138 "memtemp", 139 memtemp_e5_methods, 140 sizeof(struct memtemp_e5_softc) 141 }; 142 static devclass_t memtemp_devclass; 143 DRIVER_MODULE(memtemp_e5, pci, memtemp_e5_driver, memtemp_devclass, NULL, NULL); 144 MODULE_DEPEND(memtemp_e5, pci, 1, 1, 1); 145 MODULE_DEPEND(memtemp_e5, dimm, 1, 1, 1); 146 147 static int 148 memtemp_e5_probe(device_t dev) 149 { 150 const struct e5_imc_chan *c; 151 uint16_t vid, did; 152 int slot, func; 153 154 vid = pci_get_vendor(dev); 155 if (vid != PCI_E5_IMC_VID_ID) 156 return ENXIO; 157 158 did = pci_get_device(dev); 159 slot = pci_get_slot(dev); 160 func = pci_get_function(dev); 161 162 for (c = memtemp_e5_chans; c->desc != NULL; ++c) { 163 if (c->did == did && c->slot == slot && c->func == func) { 164 struct memtemp_e5_softc *sc = device_get_softc(dev); 165 uint32_t cfg; 166 int node; 167 168 node = e5_imc_node_probe(dev, c); 169 if (node < 0) 170 break; 171 172 /* 173 * XXX 174 * It seems that memory thermal sensor is available, 175 * only if CLTT is set (OLTT_EN does not seem matter). 176 */ 177 cfg = pci_read_config(dev, 178 PCI_E5_IMC_THERMAL_CHN_TEMP_CFG, 4); 179 if ((cfg & PCI_E5_IMC_THERMAL_CHN_TEMP_CFG_CLTT) == 0) 180 break; 181 182 device_set_desc(dev, c->desc); 183 184 sc->temp_chan = c; 185 sc->temp_node = node; 186 187 return 0; 188 } 189 } 190 return ENXIO; 191 } 192 193 static int 194 memtemp_e5_tempth_adjust(int temp) 195 { 196 if (temp == PCI_E5_IMC_THERMAL_DIMM_TEMP_TH_DISABLE) 197 return 0; 198 else if (temp < PCI_E5_IMC_THERMAL_DIMM_TEMP_TH_TEMPMIN || 199 temp >= PCI_E5_IMC_THERMAL_DIMM_TEMP_TH_TEMPMAX) 200 return -1; 201 return temp; 202 } 203 204 static void 205 memtemp_e5_tempth_str(int temp, char *temp_str, int temp_strlen) 206 { 207 if (temp < 0) 208 strlcpy(temp_str, "reserved", temp_strlen); 209 else if (temp == 0) 210 strlcpy(temp_str, "disabled", temp_strlen); 211 else 212 ksnprintf(temp_str, temp_strlen, "%dC", temp); 213 } 214 215 static int 216 memtemp_e5_attach(device_t dev) 217 { 218 struct memtemp_e5_softc *sc = device_get_softc(dev); 219 const cpu_node_t *node; 220 int dimm, cpuid = -1; 221 222 sc->temp_dev = dev; 223 TAILQ_INIT(&sc->temp_dimm); 224 225 node = get_cpu_node_by_chipid(sc->temp_node); 226 if (node != NULL && node->child_no > 0) { 227 cpuid = BSRCPUMASK(node->members); 228 if (bootverbose) { 229 device_printf(dev, "node%d chan%d -> cpu%d\n", 230 sc->temp_node, sc->temp_chan->chan_ext, cpuid); 231 } 232 } 233 234 for (dimm = 0; dimm < PCI_E5_IMC_CHN_DIMM_MAX; ++dimm) { 235 char temp_lostr[16], temp_midstr[16], temp_histr[16]; 236 struct memtemp_e5_dimm *dimm_sc; 237 int temp_lo, temp_mid, temp_hi; 238 int temp_hiwat, temp_lowat, has_temp_thresh = 1; 239 uint32_t dimmmtr, temp_th; 240 struct ksensor *sens; 241 242 dimmmtr = IMC_CTAD_READ_4(dev, sc->temp_chan, 243 PCI_E5_IMC_CTAD_DIMMMTR(dimm)); 244 245 if ((dimmmtr & PCI_E5_IMC_CTAD_DIMMMTR_DIMM_POP) == 0) 246 continue; 247 248 dimm_sc = kmalloc(sizeof(*dimm_sc), M_DEVBUF, 249 M_WAITOK | M_ZERO); 250 dimm_sc->dimm_id = dimm; 251 dimm_sc->dimm_parent = sc; 252 253 temp_th = pci_read_config(dev, 254 PCI_E5_IMC_THERMAL_DIMM_TEMP_TH(dimm), 4); 255 256 temp_lo = __SHIFTOUT(temp_th, 257 PCI_E5_IMC_THERMAL_DIMM_TEMP_TH_TEMPLO); 258 temp_lo = memtemp_e5_tempth_adjust(temp_lo); 259 memtemp_e5_tempth_str(temp_lo, temp_lostr, sizeof(temp_lostr)); 260 261 temp_mid = __SHIFTOUT(temp_th, 262 PCI_E5_IMC_THERMAL_DIMM_TEMP_TH_TEMPMID); 263 temp_mid = memtemp_e5_tempth_adjust(temp_mid); 264 memtemp_e5_tempth_str(temp_mid, temp_midstr, 265 sizeof(temp_midstr)); 266 267 temp_hi = __SHIFTOUT(temp_th, 268 PCI_E5_IMC_THERMAL_DIMM_TEMP_TH_TEMPHI); 269 temp_hi = memtemp_e5_tempth_adjust(temp_hi); 270 memtemp_e5_tempth_str(temp_hi, temp_histr, sizeof(temp_histr)); 271 272 /* 273 * NOTE: 274 * - TEMPHI initiates THRTCRIT. 275 * - TEMPMID initiates THRTHI, so it is also taken into 276 * consideration. 277 * - Some BIOSes program temp_lo to a rediculous low value, 278 * so ignore TEMPLO here. 279 */ 280 if (temp_mid <= 0) { 281 if (temp_hi <= 0) { 282 temp_hiwat = MEMTEMP_E5_DIMM_TEMP_HIWAT; 283 has_temp_thresh = 0; 284 } else { 285 temp_hiwat = temp_hi; 286 } 287 } else { 288 temp_hiwat = temp_mid; 289 } 290 if (temp_hiwat < MEMTEMP_E5_DIMM_TEMP_STEP) { 291 temp_hiwat = MEMTEMP_E5_DIMM_TEMP_HIWAT; 292 has_temp_thresh = 0; 293 } 294 temp_lowat = temp_hiwat - MEMTEMP_E5_DIMM_TEMP_STEP; 295 296 if (bootverbose) { 297 device_printf(dev, "DIMM%d " 298 "temp_hi %s, temp_mid %s, temp_lo %s\n", dimm, 299 temp_histr, temp_midstr, temp_lostr); 300 } 301 302 dimm_sc->dimm_softc = dimm_create(sc->temp_node, 303 sc->temp_chan->chan_ext, dimm); 304 305 if (has_temp_thresh) { 306 if (bootverbose) { 307 device_printf(dev, "DIMM%d " 308 "hiwat %dC, lowat %dC\n", 309 dimm, temp_hiwat, temp_lowat); 310 } 311 dimm_set_temp_thresh(dimm_sc->dimm_softc, 312 temp_hiwat, temp_lowat); 313 } 314 315 sens = &dimm_sc->dimm_sensor; 316 ksnprintf(sens->desc, sizeof(sens->desc), 317 "node%d chan%d DIMM%d temp", 318 sc->temp_node, sc->temp_chan->chan_ext, dimm); 319 sens->type = SENSOR_TEMP; 320 sensor_set_unknown(sens); 321 dimm_sensor_attach(dimm_sc->dimm_softc, sens); 322 dimm_sc->dimm_senstask = sensor_task_register2(dimm_sc, 323 memtemp_e5_sensor_task, 5, cpuid); 324 325 TAILQ_INSERT_TAIL(&sc->temp_dimm, dimm_sc, dimm_link); 326 } 327 return 0; 328 } 329 330 static int 331 memtemp_e5_detach(device_t dev) 332 { 333 struct memtemp_e5_softc *sc = device_get_softc(dev); 334 struct memtemp_e5_dimm *dimm_sc; 335 336 while ((dimm_sc = TAILQ_FIRST(&sc->temp_dimm)) != NULL) { 337 TAILQ_REMOVE(&sc->temp_dimm, dimm_sc, dimm_link); 338 339 sensor_task_unregister2(dimm_sc->dimm_senstask); 340 dimm_sensor_detach(dimm_sc->dimm_softc, &dimm_sc->dimm_sensor); 341 dimm_destroy(dimm_sc->dimm_softc); 342 343 kfree(dimm_sc, M_DEVBUF); 344 } 345 return 0; 346 } 347 348 static void 349 memtemp_e5_sensor_task(void *xdimm_sc) 350 { 351 struct memtemp_e5_dimm *dimm_sc = xdimm_sc; 352 struct ksensor *sensor = &dimm_sc->dimm_sensor; 353 device_t dev = dimm_sc->dimm_parent->temp_dev; 354 uint32_t val; 355 int temp, reg; 356 357 reg = PCI_E5_IMC_THERMAL_DIMMTEMPSTAT(dimm_sc->dimm_id); 358 359 val = pci_read_config(dev, reg, 4); 360 if (val & (PCI_E5_IMC_THERMAL_DIMMTEMPSTAT_TEMPHI | 361 PCI_E5_IMC_THERMAL_DIMMTEMPSTAT_TEMPMID | 362 PCI_E5_IMC_THERMAL_DIMMTEMPSTAT_TEMPLO)) 363 pci_write_config(dev, reg, val, 4); 364 365 temp = __SHIFTOUT(val, PCI_E5_IMC_THERMAL_DIMMTEMPSTAT_TEMP); 366 if (temp < PCI_E5_IMC_THERMAL_DIMMTEMPSTAT_TEMPMIN || 367 temp >= PCI_E5_IMC_THERMAL_DIMMTEMPSTAT_TEMPMAX) { 368 sensor->status = SENSOR_S_UNSPEC; 369 sensor->flags |= SENSOR_FINVALID; 370 sensor->value = 0; 371 return; 372 } 373 dimm_sensor_temp(dimm_sc->dimm_softc, sensor, temp); 374 } 375