48b38883ddd8e21b459cb1710346c176c9aa2424
[dragonfly.git] / sys / dev / misc / ecc / ecc_x3400.c
1 /*
2  * Copyright (c) 2011 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/pci_cfgreg.h>
45 #include <bus/pci/pcib_private.h>
46
47 #include <vm/pmap.h>
48
49 #include "pcib_if.h"
50
51 #include <dev/misc/ecc/ecc_x3400_reg.h>
52
53 #define MC_READ_2(ofs) \
54         pci_cfgregread(PCIBUS_X3400UC, PCISLOT_X3400UC_MC, \
55             PCIFUNC_X3400UC_MC, (ofs), 2)
56 #define MC_READ_4(ofs) \
57         pci_cfgregread(PCIBUS_X3400UC, PCISLOT_X3400UC_MC, \
58             PCIFUNC_X3400UC_MC, (ofs), 4)
59
60 #define MCT2_READ_2(ofs) \
61         pci_cfgregread(PCIBUS_X3400UC, PCISLOT_X3400UC_MCT2, \
62             PCIFUNC_X3400UC_MCT2, (ofs), 2)
63 #define MCT2_READ_4(ofs) \
64         pci_cfgregread(PCIBUS_X3400UC, PCISLOT_X3400UC_MCT2, \
65             PCIFUNC_X3400UC_MCT2, (ofs), 4)
66 #define MCT2_WRITE_4(ofs, data) \
67         pci_cfgregwrite(PCIBUS_X3400UC, PCISLOT_X3400UC_MCT2, \
68             PCIFUNC_X3400UC_MCT2, (ofs), data, 4)
69
70 struct ecc_x3400_memctrl {
71         uint16_t        vid;
72         uint16_t        did;
73         const char      *desc;
74 };
75
76 struct ecc_x3400_softc {
77         device_t        ecc_mydev;
78         struct callout  ecc_callout;
79         int             ecc_dimms;
80 };
81
82 #define ecc_printf(sc, fmt, arg...) \
83         device_printf((sc)->ecc_mydev, fmt , ##arg)
84
85 static int      ecc_x3400_probe(device_t);
86 static int      ecc_x3400_attach(device_t);
87 static int      ecc_x3400_detach(device_t);
88 static void     ecc_x3400_shutdown(device_t);
89
90 static void     ecc_x3400_status(struct ecc_x3400_softc *);
91 static void     ecc_x3400_status_ch(struct ecc_x3400_softc *, int, int);
92 static void     ecc_x3400_callout(void *);
93 static void     ecc_x3400_stop(device_t);
94
95 static const struct ecc_x3400_memctrl ecc_memctrls[] = {
96         { 0x8086, 0xd130, "Intel X3400 memory controller" },
97         { 0, 0, NULL } /* required last entry */
98 };
99
100 static device_method_t ecc_x3400_methods[] = {
101         /* Device interface */
102         DEVMETHOD(device_probe,         ecc_x3400_probe),
103         DEVMETHOD(device_attach,        ecc_x3400_attach),
104         DEVMETHOD(device_detach,        ecc_x3400_detach),
105         DEVMETHOD(device_shutdown,      ecc_x3400_shutdown),
106         DEVMETHOD(device_suspend,       bus_generic_suspend),
107         DEVMETHOD(device_resume,        bus_generic_resume),
108         DEVMETHOD_END
109 };
110
111 static driver_t ecc_x3400_driver = {
112         "ecc",
113         ecc_x3400_methods,
114         sizeof(struct ecc_x3400_softc)
115 };
116 static devclass_t ecc_devclass;
117 DRIVER_MODULE(ecc_x3400, hostb, ecc_x3400_driver, ecc_devclass, NULL, NULL);
118 MODULE_DEPEND(ecc_x3400, pci, 1, 1, 1);
119 MODULE_VERSION(ecc_x3400, 1);
120
121 static int
122 ecc_x3400_probe(device_t dev)
123 {
124         const struct ecc_x3400_memctrl *mc;
125         uint16_t vid, did;
126
127         vid = pci_get_vendor(dev);
128         did = pci_get_device(dev);
129
130         for (mc = ecc_memctrls; mc->desc != NULL; ++mc) {
131                 if (mc->vid == vid && mc->did == did) {
132                         struct ecc_x3400_softc *sc = device_get_softc(dev);
133
134                         if (MC_READ_2(PCIR_VENDOR) != PCI_X3400UC_MC_VID_ID ||
135                             MC_READ_2(PCIR_DEVICE) != PCI_X3400UC_MC_DID_ID)
136                                 return ENXIO;
137                         if (MCT2_READ_2(PCIR_VENDOR) !=
138                             PCI_X3400UC_MCT2_VID_ID ||
139                             MCT2_READ_2(PCIR_DEVICE) !=
140                             PCI_X3400UC_MCT2_DID_ID)
141                                 return ENXIO;
142
143                         device_set_desc(dev, mc->desc);
144                         sc->ecc_mydev = dev;
145                         return 0;
146                 }
147         }
148         return ENXIO;
149 }
150
151 static int
152 ecc_x3400_attach(device_t dev)
153 {
154         struct ecc_x3400_softc *sc = device_get_softc(dev);
155         uint32_t val, dimms;
156
157         callout_init_mp(&sc->ecc_callout);
158
159         val = MC_READ_4(PCI_X3400UC_MC_CTRL);
160         if ((val & PCI_X3400UC_MC_CTRL_ECCEN) == 0) {
161                 device_printf(dev, "ECC checking is not enabled\n");
162                 return 0;
163         }
164
165         val = MC_READ_4(PCI_X3400UC_MC_STS);
166         if ((val & PCI_X3400UC_MC_STS_ECCEN) == 0) {
167                 device_printf(dev, "ECC is not enabled\n");
168                 return 0;
169         }
170
171         val = MC_READ_4(PCI_X3400UC_MC_MAX_DOD);
172         dimms = __SHIFTOUT(val, PCI_X3400UC_MC_MAX_DOD_DIMMS);
173         sc->ecc_dimms = dimms + 1;
174         device_printf(dev, "max dimms %d\n", sc->ecc_dimms);
175
176         callout_reset(&sc->ecc_callout, hz, ecc_x3400_callout, sc);
177
178         return 0;
179 }
180
181 static void
182 ecc_x3400_callout(void *xsc)
183 {
184         struct ecc_x3400_softc *sc = xsc;
185
186         ecc_x3400_status(sc);
187         callout_reset(&sc->ecc_callout, hz, ecc_x3400_callout, sc);
188 }
189
190 static void
191 ecc_x3400_status(struct ecc_x3400_softc *sc)
192 {
193         ecc_x3400_status_ch(sc, PCI_X3400UC_MCT2_COR_ECC_CNT_0, 0);
194         ecc_x3400_status_ch(sc, PCI_X3400UC_MCT2_COR_ECC_CNT_1, 1);
195         ecc_x3400_status_ch(sc, PCI_X3400UC_MCT2_COR_ECC_CNT_2, 2);
196         ecc_x3400_status_ch(sc, PCI_X3400UC_MCT2_COR_ECC_CNT_3, 3);
197 }
198
199 static void
200 ecc_x3400_status_ch(struct ecc_x3400_softc *sc, int ofs, int idx)
201 {
202         uint32_t cor, err0, err1;
203         const char *desc0 = NULL, *desc1 = NULL;
204
205         cor = MCT2_READ_4(ofs);
206         if (cor == 0)
207                 return;
208
209         if (sc->ecc_dimms > 2) {
210                 switch (idx) {
211                 case 0:
212                         desc0 = "channel0, DIMM0";
213                         desc1 = "channel0, DIMM1";
214                         break;
215
216                 case 1:
217                         desc0 = "channel0, DIMM2";
218                         break;
219
220                 case 2:
221                         desc0 = "channel1, DIMM0";
222                         desc1 = "channel1, DIMM1";
223                         break;
224
225                 case 3:
226                         desc0 = "channel1, DIMM2";
227                         break;
228
229                 default:
230                         panic("unsupported index %d", idx);
231                 }
232         } else {
233                 switch (idx) {
234                 case 0:
235                         desc0 = "channel0, DIMM0 RANK 0/1";
236                         desc1 = "channel0, DIMM0 RANK 2/3";
237                         break;
238
239                 case 1:
240                         desc0 = "channel0, DIMM1 RANK 0/1";
241                         desc1 = "channel0, DIMM1 RANK 2/3";
242                         break;
243
244                 case 2:
245                         desc0 = "channel1, DIMM0 RANK 0/1";
246                         desc1 = "channel1, DIMM0 RANK 2/3";
247                         break;
248
249                 case 3:
250                         desc0 = "channel1, DIMM1 RANK 0/1";
251                         desc1 = "channel1, DIMM1 RANK 2/3";
252                         break;
253
254                 default:
255                         panic("unsupported index %d", idx);
256                 }
257         }
258
259         err0 = __SHIFTOUT(cor, PCI_X3400UC_MCT2_COR_DIMM0);
260         if (cor & PCI_X3400UC_MCT2_COR_DIMM0_OV)
261                 ecc_printf(sc, "%s has too many errors\n", desc0);
262         else if (err0)
263                 ecc_printf(sc, "%s has %d errors", desc0, err0);
264
265         if (desc1 != NULL) {
266                 err1 = __SHIFTOUT(cor, PCI_X3400UC_MCT2_COR_DIMM1);
267                 if (cor & PCI_X3400UC_MCT2_COR_DIMM1_OV)
268                         ecc_printf(sc, "%s has too many errors\n", desc1);
269                 else if (err1)
270                         ecc_printf(sc, "%s has %d errors\n", desc1, err1);
271         }
272
273         MCT2_WRITE_4(ofs, 0);
274 }
275
276 static void
277 ecc_x3400_stop(device_t dev)
278 {
279         struct ecc_x3400_softc *sc = device_get_softc(dev);
280
281         callout_stop_sync(&sc->ecc_callout);
282 }
283
284 static int
285 ecc_x3400_detach(device_t dev)
286 {
287         ecc_x3400_stop(dev);
288         return 0;
289 }
290
291 static void
292 ecc_x3400_shutdown(device_t dev)
293 {
294         ecc_x3400_stop(dev);
295 }