Merge branch 'vendor/LIBPCAP'
[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/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 }