memtemp/e5: Register sensor tasks to the proper cpu package
[dragonfly.git] / sys / dev / powermng / memtemp / memtemp_e5.c
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 }