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/pci_cfgreg.h> 48 #include <bus/pci/pcib_private.h> 49 50 #include "pcib_if.h" 51 52 #include <dev/misc/dimm/dimm.h> 53 #include <dev/misc/ecc/e5_imc_reg.h> 54 #include <dev/misc/ecc/e5_imc_var.h> 55 56 #define MEMTEMP_E5_DIMM_TEMP_HIWAT 85 /* spec default TEMPLO */ 57 #define MEMTEMP_E5_DIMM_TEMP_STEP 5 /* spec TEMPLO/MID/HI step */ 58 59 struct memtemp_e5_softc; 60 61 struct memtemp_e5_dimm { 62 TAILQ_ENTRY(memtemp_e5_dimm) dimm_link; 63 struct ksensor dimm_sensor; 64 struct memtemp_e5_softc *dimm_parent; 65 int dimm_id; 66 int dimm_flags; 67 68 struct dimm_softc *dimm_softc; 69 struct sensor_task *dimm_senstask; 70 }; 71 72 #define MEMTEMP_E5_DIMM_FLAG_CRIT 0x1 73 74 struct memtemp_e5_softc { 75 device_t temp_dev; 76 const struct e5_imc_chan *temp_chan; 77 int temp_node; 78 TAILQ_HEAD(, memtemp_e5_dimm) temp_dimm; 79 }; 80 81 static int memtemp_e5_probe(device_t); 82 static int memtemp_e5_attach(device_t); 83 static int memtemp_e5_detach(device_t); 84 85 static int memtemp_e5_tempth_adjust(int); 86 static void memtemp_e5_tempth_str(int, char *, int); 87 static void memtemp_e5_sensor_task(void *); 88 89 #define MEMTEMP_E5_CHAN(v, imc, c, c_ext) \ 90 { \ 91 .did = PCI_E5V##v##_IMC##imc##_THERMAL_CHN##c##_DID_ID, \ 92 .slot = PCISLOT_E5V##v##_IMC##imc##_THERMAL_CHN##c, \ 93 .func = PCIFUNC_E5V##v##_IMC##imc##_THERMAL_CHN##c, \ 94 .desc = "Intel E5 v" #v " memory thermal sensor", \ 95 \ 96 E5_IMC_CHAN_FIELDS(v, imc, c, c_ext) \ 97 } 98 99 #define MEMTEMP_E5_CHAN_V2(c) MEMTEMP_E5_CHAN(2, 0, c, c) 100 #define MEMTEMP_E5_CHAN_IMC0_V3(c) MEMTEMP_E5_CHAN(3, 0, c, c) 101 #define MEMTEMP_E5_CHAN_IMC1_V3(c, c_ext) \ 102 MEMTEMP_E5_CHAN(3, 1, c, c_ext) 103 #define MEMTEMP_E5_CHAN_END E5_IMC_CHAN_END 104 105 static const struct e5_imc_chan memtemp_e5_chans[] = { 106 MEMTEMP_E5_CHAN_V2(0), 107 MEMTEMP_E5_CHAN_V2(1), 108 MEMTEMP_E5_CHAN_V2(2), 109 MEMTEMP_E5_CHAN_V2(3), 110 111 MEMTEMP_E5_CHAN_IMC0_V3(0), 112 MEMTEMP_E5_CHAN_IMC0_V3(1), 113 MEMTEMP_E5_CHAN_IMC0_V3(2), 114 MEMTEMP_E5_CHAN_IMC0_V3(3), 115 MEMTEMP_E5_CHAN_IMC1_V3(0, 2), /* IMC1 chan0 -> channel2 */ 116 MEMTEMP_E5_CHAN_IMC1_V3(1, 3), /* IMC1 chan1 -> channel3 */ 117 118 MEMTEMP_E5_CHAN_END 119 }; 120 121 #undef MEMTEMP_E5_CHAN_END 122 #undef MEMTEMP_E5_CHAN_V2 123 #undef MEMTEMP_E5_CHAN 124 125 static device_method_t memtemp_e5_methods[] = { 126 /* Device interface */ 127 DEVMETHOD(device_probe, memtemp_e5_probe), 128 DEVMETHOD(device_attach, memtemp_e5_attach), 129 DEVMETHOD(device_detach, memtemp_e5_detach), 130 DEVMETHOD(device_shutdown, bus_generic_shutdown), 131 DEVMETHOD(device_suspend, bus_generic_suspend), 132 DEVMETHOD(device_resume, bus_generic_resume), 133 DEVMETHOD_END 134 }; 135 136 static driver_t memtemp_e5_driver = { 137 "memtemp", 138 memtemp_e5_methods, 139 sizeof(struct memtemp_e5_softc) 140 }; 141 static devclass_t memtemp_devclass; 142 DRIVER_MODULE(memtemp_e5, pci, memtemp_e5_driver, memtemp_devclass, NULL, NULL); 143 MODULE_DEPEND(memtemp_e5, pci, 1, 1, 1); 144 MODULE_DEPEND(memtemp_e5, dimm, 1, 1, 1); 145 146 static int 147 memtemp_e5_probe(device_t dev) 148 { 149 const struct e5_imc_chan *c; 150 uint16_t vid, did; 151 int slot, func; 152 153 vid = pci_get_vendor(dev); 154 if (vid != PCI_E5_IMC_VID_ID) 155 return ENXIO; 156 157 did = pci_get_device(dev); 158 slot = pci_get_slot(dev); 159 func = pci_get_function(dev); 160 161 for (c = memtemp_e5_chans; c->desc != NULL; ++c) { 162 if (c->did == did && c->slot == slot && c->func == func) { 163 struct memtemp_e5_softc *sc = device_get_softc(dev); 164 uint32_t cfg; 165 int node; 166 167 node = e5_imc_node_probe(dev, c); 168 if (node < 0) 169 break; 170 171 /* 172 * XXX 173 * It seems that memory thermal sensor is available, 174 * only if CLTT is set (OLTT_EN does not seem matter). 175 */ 176 cfg = pci_read_config(dev, 177 PCI_E5_IMC_THERMAL_CHN_TEMP_CFG, 4); 178 if ((cfg & PCI_E5_IMC_THERMAL_CHN_TEMP_CFG_CLTT) == 0) 179 break; 180 181 device_set_desc(dev, c->desc); 182 183 sc->temp_chan = c; 184 sc->temp_node = node; 185 186 return 0; 187 } 188 } 189 return ENXIO; 190 } 191 192 static int 193 memtemp_e5_tempth_adjust(int temp) 194 { 195 if (temp == PCI_E5_IMC_THERMAL_DIMM_TEMP_TH_DISABLE) 196 return 0; 197 else if (temp < PCI_E5_IMC_THERMAL_DIMM_TEMP_TH_TEMPMIN || 198 temp >= PCI_E5_IMC_THERMAL_DIMM_TEMP_TH_TEMPMAX) 199 return -1; 200 return temp; 201 } 202 203 static void 204 memtemp_e5_tempth_str(int temp, char *temp_str, int temp_strlen) 205 { 206 if (temp < 0) 207 strlcpy(temp_str, "reserved", temp_strlen); 208 else if (temp == 0) 209 strlcpy(temp_str, "disabled", temp_strlen); 210 else 211 ksnprintf(temp_str, temp_strlen, "%dC", temp); 212 } 213 214 static int 215 memtemp_e5_attach(device_t dev) 216 { 217 struct memtemp_e5_softc *sc = device_get_softc(dev); 218 const cpu_node_t *node; 219 int dimm, cpuid = -1; 220 221 sc->temp_dev = dev; 222 TAILQ_INIT(&sc->temp_dimm); 223 224 node = get_cpu_node_by_chipid(sc->temp_node); 225 if (node != NULL && node->child_no > 0) { 226 cpuid = BSRCPUMASK(node->members); 227 if (bootverbose) { 228 device_printf(dev, "node%d chan%d -> cpu%d\n", 229 sc->temp_node, sc->temp_chan->chan_ext, cpuid); 230 } 231 } 232 233 for (dimm = 0; dimm < PCI_E5_IMC_CHN_DIMM_MAX; ++dimm) { 234 char temp_lostr[16], temp_midstr[16], temp_histr[16]; 235 struct memtemp_e5_dimm *dimm_sc; 236 int temp_lo, temp_mid, temp_hi; 237 int temp_hiwat, temp_lowat, has_temp_thresh = 1; 238 uint32_t dimmmtr, temp_th; 239 struct ksensor *sens; 240 241 dimmmtr = IMC_CTAD_READ_4(dev, sc->temp_chan, 242 PCI_E5_IMC_CTAD_DIMMMTR(dimm)); 243 244 if ((dimmmtr & PCI_E5_IMC_CTAD_DIMMMTR_DIMM_POP) == 0) 245 continue; 246 247 dimm_sc = kmalloc(sizeof(*dimm_sc), M_DEVBUF, 248 M_WAITOK | M_ZERO); 249 dimm_sc->dimm_id = dimm; 250 dimm_sc->dimm_parent = sc; 251 252 temp_th = pci_read_config(dev, 253 PCI_E5_IMC_THERMAL_DIMM_TEMP_TH(dimm), 4); 254 255 temp_lo = __SHIFTOUT(temp_th, 256 PCI_E5_IMC_THERMAL_DIMM_TEMP_TH_TEMPLO); 257 temp_lo = memtemp_e5_tempth_adjust(temp_lo); 258 memtemp_e5_tempth_str(temp_lo, temp_lostr, sizeof(temp_lostr)); 259 260 temp_mid = __SHIFTOUT(temp_th, 261 PCI_E5_IMC_THERMAL_DIMM_TEMP_TH_TEMPMID); 262 temp_mid = memtemp_e5_tempth_adjust(temp_mid); 263 memtemp_e5_tempth_str(temp_mid, temp_midstr, 264 sizeof(temp_midstr)); 265 266 temp_hi = __SHIFTOUT(temp_th, 267 PCI_E5_IMC_THERMAL_DIMM_TEMP_TH_TEMPHI); 268 temp_hi = memtemp_e5_tempth_adjust(temp_hi); 269 memtemp_e5_tempth_str(temp_hi, temp_histr, sizeof(temp_histr)); 270 271 /* 272 * NOTE: 273 * - TEMPHI initiates THRTCRIT. 274 * - TEMPMID initiates THRTHI, so it is also taken into 275 * consideration. 276 * - Some BIOSes program temp_lo to a rediculous low value, 277 * so ignore TEMPLO here. 278 */ 279 if (temp_mid <= 0) { 280 if (temp_hi <= 0) { 281 temp_hiwat = MEMTEMP_E5_DIMM_TEMP_HIWAT; 282 has_temp_thresh = 0; 283 } else { 284 temp_hiwat = temp_hi; 285 } 286 } else { 287 temp_hiwat = temp_mid; 288 } 289 if (temp_hiwat < MEMTEMP_E5_DIMM_TEMP_STEP) { 290 temp_hiwat = MEMTEMP_E5_DIMM_TEMP_HIWAT; 291 has_temp_thresh = 0; 292 } 293 temp_lowat = temp_hiwat - MEMTEMP_E5_DIMM_TEMP_STEP; 294 295 if (bootverbose) { 296 device_printf(dev, "DIMM%d " 297 "temp_hi %s, temp_mid %s, temp_lo %s\n", dimm, 298 temp_histr, temp_midstr, temp_lostr); 299 } 300 301 dimm_sc->dimm_softc = dimm_create(sc->temp_node, 302 sc->temp_chan->chan_ext, dimm); 303 304 if (has_temp_thresh) { 305 if (bootverbose) { 306 device_printf(dev, "DIMM%d " 307 "hiwat %dC, lowat %dC\n", 308 dimm, temp_hiwat, temp_lowat); 309 } 310 dimm_set_temp_thresh(dimm_sc->dimm_softc, 311 temp_hiwat, temp_lowat); 312 } 313 314 sens = &dimm_sc->dimm_sensor; 315 ksnprintf(sens->desc, sizeof(sens->desc), 316 "node%d chan%d DIMM%d temp", 317 sc->temp_node, sc->temp_chan->chan_ext, dimm); 318 sens->type = SENSOR_TEMP; 319 sensor_set_unknown(sens); 320 dimm_sensor_attach(dimm_sc->dimm_softc, sens); 321 dimm_sc->dimm_senstask = sensor_task_register2(dimm_sc, 322 memtemp_e5_sensor_task, 5, cpuid); 323 324 TAILQ_INSERT_TAIL(&sc->temp_dimm, dimm_sc, dimm_link); 325 } 326 return 0; 327 } 328 329 static int 330 memtemp_e5_detach(device_t dev) 331 { 332 struct memtemp_e5_softc *sc = device_get_softc(dev); 333 struct memtemp_e5_dimm *dimm_sc; 334 335 while ((dimm_sc = TAILQ_FIRST(&sc->temp_dimm)) != NULL) { 336 TAILQ_REMOVE(&sc->temp_dimm, dimm_sc, dimm_link); 337 338 sensor_task_unregister2(dimm_sc->dimm_senstask); 339 dimm_sensor_detach(dimm_sc->dimm_softc, &dimm_sc->dimm_sensor); 340 dimm_destroy(dimm_sc->dimm_softc); 341 342 kfree(dimm_sc, M_DEVBUF); 343 } 344 return 0; 345 } 346 347 static void 348 memtemp_e5_sensor_task(void *xdimm_sc) 349 { 350 struct memtemp_e5_dimm *dimm_sc = xdimm_sc; 351 struct ksensor *sensor = &dimm_sc->dimm_sensor; 352 device_t dev = dimm_sc->dimm_parent->temp_dev; 353 uint32_t val; 354 int temp, reg; 355 356 reg = PCI_E5_IMC_THERMAL_DIMMTEMPSTAT(dimm_sc->dimm_id); 357 358 val = pci_read_config(dev, reg, 4); 359 if (val & (PCI_E5_IMC_THERMAL_DIMMTEMPSTAT_TEMPHI | 360 PCI_E5_IMC_THERMAL_DIMMTEMPSTAT_TEMPMID | 361 PCI_E5_IMC_THERMAL_DIMMTEMPSTAT_TEMPLO)) 362 pci_write_config(dev, reg, val, 4); 363 364 temp = __SHIFTOUT(val, PCI_E5_IMC_THERMAL_DIMMTEMPSTAT_TEMP); 365 if (temp < PCI_E5_IMC_THERMAL_DIMMTEMPSTAT_TEMPMIN || 366 temp >= PCI_E5_IMC_THERMAL_DIMMTEMPSTAT_TEMPMAX) { 367 sensor->status = SENSOR_S_UNSPEC; 368 sensor->flags |= SENSOR_FINVALID; 369 sensor->value = 0; 370 return; 371 } 372 dimm_sensor_temp(dimm_sc->dimm_softc, sensor, temp); 373 } 374