2 * Copyright (c) 2015 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Sepherosa Ziehau <sepherosa@gmail.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/bitops.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>
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>
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>
57 #define MEMTEMP_E5_DIMM_TEMP_HIWAT 85 /* spec default TEMPLO */
58 #define MEMTEMP_E5_DIMM_TEMP_STEP 5 /* spec TEMPLO/MID/HI step */
60 struct memtemp_e5_softc;
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;
69 struct dimm_softc *dimm_softc;
70 struct sensor_task *dimm_senstask;
73 #define MEMTEMP_E5_DIMM_FLAG_CRIT 0x1
75 struct memtemp_e5_softc {
77 const struct e5_imc_chan *temp_chan;
79 TAILQ_HEAD(, memtemp_e5_dimm) temp_dimm;
82 static int memtemp_e5_probe(device_t);
83 static int memtemp_e5_attach(device_t);
84 static int memtemp_e5_detach(device_t);
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 *);
90 #define MEMTEMP_E5_CHAN(v, imc, c, c_ext) \
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", \
97 E5_IMC_CHAN_FIELDS(v, imc, c, c_ext) \
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
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),
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 */
122 #undef MEMTEMP_E5_CHAN_END
123 #undef MEMTEMP_E5_CHAN_V2
124 #undef MEMTEMP_E5_CHAN
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),
137 static driver_t memtemp_e5_driver = {
140 sizeof(struct memtemp_e5_softc)
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);
148 memtemp_e5_probe(device_t dev)
150 const struct e5_imc_chan *c;
154 vid = pci_get_vendor(dev);
155 if (vid != PCI_E5_IMC_VID_ID)
158 did = pci_get_device(dev);
159 slot = pci_get_slot(dev);
160 func = pci_get_function(dev);
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);
168 node = e5_imc_node_probe(dev, c);
174 * It seems that memory thermal sensor is available,
175 * only if CLTT is set (OLTT_EN does not seem matter).
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)
182 device_set_desc(dev, c->desc);
185 sc->temp_node = node;
194 memtemp_e5_tempth_adjust(int temp)
196 if (temp == PCI_E5_IMC_THERMAL_DIMM_TEMP_TH_DISABLE)
198 else if (temp < PCI_E5_IMC_THERMAL_DIMM_TEMP_TH_TEMPMIN ||
199 temp >= PCI_E5_IMC_THERMAL_DIMM_TEMP_TH_TEMPMAX)
205 memtemp_e5_tempth_str(int temp, char *temp_str, int temp_strlen)
208 strlcpy(temp_str, "reserved", temp_strlen);
210 strlcpy(temp_str, "disabled", temp_strlen);
212 ksnprintf(temp_str, temp_strlen, "%dC", temp);
216 memtemp_e5_attach(device_t dev)
218 struct memtemp_e5_softc *sc = device_get_softc(dev);
219 const cpu_node_t *node;
220 int dimm, cpuid = -1;
223 TAILQ_INIT(&sc->temp_dimm);
225 node = get_cpu_node_by_chipid(sc->temp_node);
226 if (node != NULL && node->child_no > 0) {
227 cpuid = BSRCPUMASK(node->members);
229 device_printf(dev, "node%d chan%d -> cpu%d\n",
230 sc->temp_node, sc->temp_chan->chan_ext, cpuid);
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;
242 dimmmtr = IMC_CTAD_READ_4(dev, sc->temp_chan,
243 PCI_E5_IMC_CTAD_DIMMMTR(dimm));
245 if ((dimmmtr & PCI_E5_IMC_CTAD_DIMMMTR_DIMM_POP) == 0)
248 dimm_sc = kmalloc(sizeof(*dimm_sc), M_DEVBUF,
250 dimm_sc->dimm_id = dimm;
251 dimm_sc->dimm_parent = sc;
253 temp_th = pci_read_config(dev,
254 PCI_E5_IMC_THERMAL_DIMM_TEMP_TH(dimm), 4);
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));
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));
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));
274 * - TEMPHI initiates THRTCRIT.
275 * - TEMPMID initiates THRTHI, so it is also taken into
277 * - Some BIOSes program temp_lo to a rediculous low value,
278 * so ignore TEMPLO here.
282 temp_hiwat = MEMTEMP_E5_DIMM_TEMP_HIWAT;
285 temp_hiwat = temp_hi;
288 temp_hiwat = temp_mid;
290 if (temp_hiwat < MEMTEMP_E5_DIMM_TEMP_STEP) {
291 temp_hiwat = MEMTEMP_E5_DIMM_TEMP_HIWAT;
294 temp_lowat = temp_hiwat - MEMTEMP_E5_DIMM_TEMP_STEP;
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);
302 dimm_sc->dimm_softc = dimm_create(sc->temp_node,
303 sc->temp_chan->chan_ext, dimm);
305 if (has_temp_thresh) {
307 device_printf(dev, "DIMM%d "
308 "hiwat %dC, lowat %dC\n",
309 dimm, temp_hiwat, temp_lowat);
311 dimm_set_temp_thresh(dimm_sc->dimm_softc,
312 temp_hiwat, temp_lowat);
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);
325 TAILQ_INSERT_TAIL(&sc->temp_dimm, dimm_sc, dimm_link);
331 memtemp_e5_detach(device_t dev)
333 struct memtemp_e5_softc *sc = device_get_softc(dev);
334 struct memtemp_e5_dimm *dimm_sc;
336 while ((dimm_sc = TAILQ_FIRST(&sc->temp_dimm)) != NULL) {
337 TAILQ_REMOVE(&sc->temp_dimm, dimm_sc, dimm_link);
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);
343 kfree(dimm_sc, M_DEVBUF);
349 memtemp_e5_sensor_task(void *xdimm_sc)
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;
357 reg = PCI_E5_IMC_THERMAL_DIMMTEMPSTAT(dimm_sc->dimm_id);
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);
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;
373 dimm_sensor_temp(dimm_sc->dimm_softc, sensor, temp);