ecc/memtemp/e5: Prepare for E5 v3 support
[dragonfly.git] / sys / dev / misc / ecc / ecc_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/bus.h>
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 #include <sys/bitops.h>
41
42 #include <bus/pci/pcivar.h>
43 #include <bus/pci/pcireg.h>
44 #include <bus/pci/pcibus.h>
45 #include <bus/pci/pci_cfgreg.h>
46 #include <bus/pci/pcib_private.h>
47
48 #include "pcib_if.h"
49
50 #include <dev/misc/ecc/e5_imc_reg.h>
51 #include <dev/misc/ecc/e5_imc_var.h>
52
53 struct ecc_e5_rank {
54         int             rank_dimm;      /* owner dimm */
55         int             rank_dimm_rank; /* rank within the owner dimm */
56 };
57
58 struct ecc_e5_softc {
59         device_t                ecc_dev;
60         const struct e5_imc_chan *ecc_chan;
61         int                     ecc_node;
62         int                     ecc_rank_cnt;
63         struct ecc_e5_rank      ecc_rank[PCI_E5_IMC_ERROR_RANK_MAX];
64         struct callout          ecc_callout;
65 };
66
67 #define ecc_printf(sc, fmt, arg...) \
68         device_printf((sc)->ecc_dev, fmt , ##arg)
69
70 static int      ecc_e5_probe(device_t);
71 static int      ecc_e5_attach(device_t);
72 static int      ecc_e5_detach(device_t);
73 static void     ecc_e5_shutdown(device_t);
74
75 static void     ecc_e5_callout(void *);
76
77 #define ECC_E5_CHAN(v, imc, c, c_ext)                           \
78 {                                                               \
79         .did            = PCI_E5V##v##_IMC##imc##_ERROR_CHN##c##_DID_ID, \
80         .slot           = PCISLOT_E5V##v##_IMC##imc##_ERROR_CHN##c, \
81         .func           = PCIFUNC_E5V##v##_IMC##imc##_ERROR_CHN##c, \
82         .desc           = "Intel E5 v" #v " ECC",               \
83                                                                 \
84         E5_IMC_CHAN_FIELDS(v, imc, c, c_ext)                    \
85 }
86
87 #define ECC_E5_CHAN_V2(c)       ECC_E5_CHAN(2, 0, c, c)
88 #define ECC_E5_CHAN_END         E5_IMC_CHAN_END
89
90 static const struct e5_imc_chan ecc_e5_chans[] = {
91         ECC_E5_CHAN_V2(0),
92         ECC_E5_CHAN_V2(1),
93         ECC_E5_CHAN_V2(2),
94         ECC_E5_CHAN_V2(3),
95
96         ECC_E5_CHAN_END
97 };
98
99 #undef ECC_E5_CHAN_END
100 #undef ECC_E5_CHAN_V2
101 #undef ECC_E5_CHAN
102
103 static device_method_t ecc_e5_methods[] = {
104         /* Device interface */
105         DEVMETHOD(device_probe,         ecc_e5_probe),
106         DEVMETHOD(device_attach,        ecc_e5_attach),
107         DEVMETHOD(device_detach,        ecc_e5_detach),
108         DEVMETHOD(device_shutdown,      ecc_e5_shutdown),
109         DEVMETHOD(device_suspend,       bus_generic_suspend),
110         DEVMETHOD(device_resume,        bus_generic_resume),
111         DEVMETHOD_END
112 };
113
114 static driver_t ecc_e5_driver = {
115         "ecc",
116         ecc_e5_methods,
117         sizeof(struct ecc_e5_softc)
118 };
119 static devclass_t ecc_devclass;
120 DRIVER_MODULE(ecc_e5, pci, ecc_e5_driver, ecc_devclass, NULL, NULL);
121 MODULE_DEPEND(ecc_e5, pci, 1, 1, 1);
122
123 static int
124 ecc_e5_probe(device_t dev)
125 {
126         const struct e5_imc_chan *c;
127         uint16_t vid, did;
128         int slot, func;
129
130         vid = pci_get_vendor(dev);
131         if (vid != PCI_E5_IMC_VID_ID)
132                 return ENXIO;
133
134         did = pci_get_device(dev);
135         slot = pci_get_slot(dev);
136         func = pci_get_function(dev);
137
138         for (c = ecc_e5_chans; c->desc != NULL; ++c) {
139                 if (c->did == did && c->slot == slot && c->func == func) {
140                         struct ecc_e5_softc *sc = device_get_softc(dev);
141                         char desc[32];
142                         int node;
143
144                         node = e5_imc_node_probe(dev, c);
145                         if (node < 0)
146                                 break;
147
148                         ksnprintf(desc, sizeof(desc), "%s node%d channel%d",
149                             c->desc, node, c->chan_ext);
150                         device_set_desc_copy(dev, desc);
151
152                         sc->ecc_chan = c;
153                         sc->ecc_node = node;
154                         return 0;
155                 }
156         }
157         return ENXIO;
158 }
159
160 static int
161 ecc_e5_attach(device_t dev)
162 {
163         struct ecc_e5_softc *sc = device_get_softc(dev);
164         uint32_t mcmtr;
165         int dimm, rank;
166
167         callout_init_mp(&sc->ecc_callout);
168         sc->ecc_dev = dev;
169
170         mcmtr = IMC_CPGC_READ_4(sc->ecc_dev, sc->ecc_chan,
171             PCI_E5_IMC_CPGC_MCMTR);
172         if (bootverbose) {
173                 if (sc->ecc_chan->ver == E5_IMC_CHAN_VER3 &&
174                     (mcmtr & PCI_E5V3_IMC_CPGC_MCMTR_DDR4))
175                         ecc_printf(sc, "DDR4 ");
176                 if (__SHIFTOUT(mcmtr, PCI_E5_IMC_CPGC_MCMTR_IMC_MODE) ==
177                     PCI_E5_IMC_CPGC_MCMTR_IMC_MODE_DDR3) {
178                         ecc_printf(sc, "native %s",
179                             sc->ecc_chan->ver == E5_IMC_CHAN_VER2 ?
180                             "DDR3" : "DDR");
181                 }
182                 kprintf("\n");
183         }
184
185         rank = 0;
186         for (dimm = 0; dimm < PCI_E5_IMC_CHN_DIMM_MAX; ++dimm) {
187                 const char *width;
188                 uint32_t dimmmtr;
189                 int rank_cnt, r;
190                 int density;
191                 int val;
192
193                 dimmmtr = IMC_CTAD_READ_4(sc->ecc_dev, sc->ecc_chan,
194                     PCI_E5_IMC_CTAD_DIMMMTR(dimm));
195
196                 if ((dimmmtr & PCI_E5_IMC_CTAD_DIMMMTR_DIMM_POP) == 0)
197                         continue;
198
199                 val = __SHIFTOUT(dimmmtr, PCI_E5_IMC_CTAD_DIMMMTR_RANK_CNT);
200                 switch (val) {
201                 case PCI_E5_IMC_CTAD_DIMMMTR_RANK_CNT_SR:
202                         rank_cnt = 1;
203                         break;
204                 case PCI_E5_IMC_CTAD_DIMMMTR_RANK_CNT_DR:
205                         rank_cnt = 2;
206                         break;
207                 case PCI_E5_IMC_CTAD_DIMMMTR_RANK_CNT_QR:
208                         rank_cnt = 4;
209                         break;
210                 case PCI_E5V3_IMC_CTAD_DIMMMTR_RANK_CNT_8R:
211                         if (sc->ecc_chan->ver >= E5_IMC_CHAN_VER3) {
212                                 rank_cnt = 8;
213                                 break;
214                         }
215                         /* FALL THROUGH */
216                 default:
217                         ecc_printf(sc, "unknown rank count 0x%x\n", val);
218                         return ENXIO;
219                 }
220
221                 val = __SHIFTOUT(dimmmtr, PCI_E5_IMC_CTAD_DIMMMTR_DDR3_WIDTH);
222                 switch (val) {
223                 case PCI_E5_IMC_CTAD_DIMMMTR_DDR3_WIDTH_4:
224                         width = "x4";
225                         break;
226                 case PCI_E5_IMC_CTAD_DIMMMTR_DDR3_WIDTH_8:
227                         width = "x8";
228                         break;
229                 case PCI_E5_IMC_CTAD_DIMMMTR_DDR3_WIDTH_16:
230                         width = "x16";
231                         break;
232                 default:
233                         ecc_printf(sc, "unknown ddr3 width 0x%x\n", val);
234                         return ENXIO;
235                 }
236
237                 val = __SHIFTOUT(dimmmtr, PCI_E5_IMC_CTAD_DIMMMTR_DDR3_DNSTY);
238                 switch (val) {
239                 case PCI_E5_IMC_CTAD_DIMMMTR_DDR3_DNSTY_2G:
240                         density = 2;
241                         break;
242                 case PCI_E5_IMC_CTAD_DIMMMTR_DDR3_DNSTY_4G:
243                         density = 4;
244                         break;
245                 case PCI_E5_IMC_CTAD_DIMMMTR_DDR3_DNSTY_8G:
246                         density = 8;
247                         break;
248                 case PCI_E5_IMC_CTAD_DIMMMTR_DDR3_DNSTY_1G:
249                         if (sc->ecc_chan->ver < E5_IMC_CHAN_VER3) {
250                                 density = 1;
251                                 break;
252                         }
253                         /* FALL THROUGH */
254                 default:
255                         ecc_printf(sc, "unknown ddr3 density 0x%x\n", val);
256                         return ENXIO;
257                 }
258
259                 if (bootverbose) {
260                         ecc_printf(sc, "DIMM%d %dGB, %d%s, density %dGB\n",
261                             dimm, density * rank_cnt * 2,
262                             rank_cnt, width, density);
263                 }
264
265                 for (r = 0; r < rank_cnt; ++r) {
266                         struct ecc_e5_rank *rk;
267
268                         if (rank >= PCI_E5_IMC_ERROR_RANK_MAX) {
269                                 ecc_printf(sc, "too many ranks\n");
270                                 return ENXIO;
271                         }
272                         rk = &sc->ecc_rank[rank];
273
274                         rk->rank_dimm = dimm;
275                         rk->rank_dimm_rank = r;
276
277                         ++rank;
278                 }
279         }
280         sc->ecc_rank_cnt = rank;
281
282         if ((mcmtr & PCI_E5_IMC_CPGC_MCMTR_ECC_EN) == 0) {
283                 ecc_printf(sc, "ECC is not enabled\n");
284                 return 0;
285         }
286
287         if (bootverbose) {
288                 for (rank = 0; rank < sc->ecc_rank_cnt; ++rank) {
289                         const struct ecc_e5_rank *rk = &sc->ecc_rank[rank];
290                         uint32_t thr, mask;
291                         int ofs;
292
293                         ofs = PCI_E5_IMC_ERROR_COR_ERR_TH(rank / 2);
294                         if (rank & 1)
295                                 mask = PCI_E5_IMC_ERROR_COR_ERR_TH_HI;
296                         else
297                                 mask = PCI_E5_IMC_ERROR_COR_ERR_TH_LO;
298
299                         thr = pci_read_config(sc->ecc_dev, ofs, 4);
300                         ecc_printf(sc, "DIMM%d rank%d, "
301                             "corrected error threshold %d\n",
302                             rk->rank_dimm, rk->rank_dimm_rank,
303                             __SHIFTOUT(thr, mask));
304                 }
305         }
306
307         callout_reset(&sc->ecc_callout, hz, ecc_e5_callout, sc);
308         return 0;
309 }
310
311 static void
312 ecc_e5_callout(void *xsc)
313 {
314         struct ecc_e5_softc *sc = xsc;
315         uint32_t err_ranks, val;
316
317         val = pci_read_config(sc->ecc_dev, PCI_E5_IMC_ERROR_COR_ERR_STAT, 4);
318
319         err_ranks = (val & PCI_E5_IMC_ERROR_COR_ERR_STAT_RANKS);
320         while (err_ranks != 0) {
321                 int rank;
322
323                 rank = ffs(err_ranks) - 1;
324                 err_ranks &= ~(1 << rank);
325
326                 if (rank < sc->ecc_rank_cnt) {
327                         const struct ecc_e5_rank *rk = &sc->ecc_rank[rank];
328                         uint32_t err, mask;
329                         int ofs;
330
331                         ofs = PCI_E5_IMC_ERROR_COR_ERR_CNT(rank / 2);
332                         if (rank & 1)
333                                 mask = PCI_E5_IMC_ERROR_COR_ERR_CNT_HI;
334                         else
335                                 mask = PCI_E5_IMC_ERROR_COR_ERR_CNT_LO;
336
337                         err = pci_read_config(sc->ecc_dev, ofs, 4);
338                         ecc_printf(sc, "node%d channel%d DIMM%d rank%d, "
339                             "too many errors %d",
340                             sc->ecc_node, sc->ecc_chan->chan_ext,
341                             rk->rank_dimm, rk->rank_dimm_rank,
342                             __SHIFTOUT(err, mask));
343                 }
344         }
345
346         if (val & PCI_E5_IMC_ERROR_COR_ERR_STAT_RANKS) {
347                 pci_write_config(sc->ecc_dev, PCI_E5_IMC_ERROR_COR_ERR_STAT,
348                     val, 4);
349         }
350         callout_reset(&sc->ecc_callout, hz, ecc_e5_callout, sc);
351 }
352
353 static void
354 ecc_e5_stop(device_t dev)
355 {
356         struct ecc_e5_softc *sc = device_get_softc(dev);
357
358         callout_stop_sync(&sc->ecc_callout);
359 }
360
361 static int
362 ecc_e5_detach(device_t dev)
363 {
364         ecc_e5_stop(dev);
365         return 0;
366 }
367
368 static void
369 ecc_e5_shutdown(device_t dev)
370 {
371         ecc_e5_stop(dev);
372 }