xref: /dragonfly/sys/dev/misc/ecc/ecc_x3400.c (revision f2a91d31)
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 
120 static int
121 ecc_x3400_probe(device_t dev)
122 {
123 	const struct ecc_x3400_memctrl *mc;
124 	uint16_t vid, did;
125 
126 	vid = pci_get_vendor(dev);
127 	did = pci_get_device(dev);
128 
129 	for (mc = ecc_memctrls; mc->desc != NULL; ++mc) {
130 		if (mc->vid == vid && mc->did == did) {
131 			struct ecc_x3400_softc *sc = device_get_softc(dev);
132 
133 			if (MC_READ_2(PCIR_VENDOR) != PCI_X3400UC_MC_VID_ID ||
134 			    MC_READ_2(PCIR_DEVICE) != PCI_X3400UC_MC_DID_ID)
135 				return ENXIO;
136 			if (MCT2_READ_2(PCIR_VENDOR) !=
137 			    PCI_X3400UC_MCT2_VID_ID ||
138 			    MCT2_READ_2(PCIR_DEVICE) !=
139 			    PCI_X3400UC_MCT2_DID_ID)
140 				return ENXIO;
141 
142 			device_set_desc(dev, mc->desc);
143 			sc->ecc_mydev = dev;
144 			return 0;
145 		}
146 	}
147 	return ENXIO;
148 }
149 
150 static int
151 ecc_x3400_attach(device_t dev)
152 {
153 	struct ecc_x3400_softc *sc = device_get_softc(dev);
154 	uint32_t val, dimms;
155 
156 	callout_init_mp(&sc->ecc_callout);
157 
158 	val = MC_READ_4(PCI_X3400UC_MC_CTRL);
159 	if ((val & PCI_X3400UC_MC_CTRL_ECCEN) == 0) {
160 		device_printf(dev, "ECC checking is not enabled\n");
161 		return 0;
162 	}
163 
164 	val = MC_READ_4(PCI_X3400UC_MC_STS);
165 	if ((val & PCI_X3400UC_MC_STS_ECCEN) == 0) {
166 		device_printf(dev, "ECC is not enabled\n");
167 		return 0;
168 	}
169 
170 	val = MC_READ_4(PCI_X3400UC_MC_MAX_DOD);
171 	dimms = __SHIFTOUT(val, PCI_X3400UC_MC_MAX_DOD_DIMMS);
172 	sc->ecc_dimms = dimms + 1;
173 	device_printf(dev, "max dimms %d\n", sc->ecc_dimms);
174 
175 	callout_reset(&sc->ecc_callout, hz, ecc_x3400_callout, sc);
176 
177 	return 0;
178 }
179 
180 static void
181 ecc_x3400_callout(void *xsc)
182 {
183 	struct ecc_x3400_softc *sc = xsc;
184 
185 	ecc_x3400_status(sc);
186 	callout_reset(&sc->ecc_callout, hz, ecc_x3400_callout, sc);
187 }
188 
189 static void
190 ecc_x3400_status(struct ecc_x3400_softc *sc)
191 {
192 	ecc_x3400_status_ch(sc, PCI_X3400UC_MCT2_COR_ECC_CNT_0, 0);
193 	ecc_x3400_status_ch(sc, PCI_X3400UC_MCT2_COR_ECC_CNT_1, 1);
194 	ecc_x3400_status_ch(sc, PCI_X3400UC_MCT2_COR_ECC_CNT_2, 2);
195 	ecc_x3400_status_ch(sc, PCI_X3400UC_MCT2_COR_ECC_CNT_3, 3);
196 }
197 
198 static void
199 ecc_x3400_status_ch(struct ecc_x3400_softc *sc, int ofs, int idx)
200 {
201 	uint32_t cor, err0, err1;
202 	const char *desc0 = NULL, *desc1 = NULL;
203 
204 	cor = MCT2_READ_4(ofs);
205 	if (cor == 0)
206 		return;
207 
208 	if (sc->ecc_dimms > 2) {
209 		switch (idx) {
210 		case 0:
211 			desc0 = "channel0, DIMM0";
212 			desc1 = "channel0, DIMM1";
213 			break;
214 
215 		case 1:
216 			desc0 = "channel0, DIMM2";
217 			break;
218 
219 		case 2:
220 			desc0 = "channel1, DIMM0";
221 			desc1 = "channel1, DIMM1";
222 			break;
223 
224 		case 3:
225 			desc0 = "channel1, DIMM2";
226 			break;
227 
228 		default:
229 			panic("unsupported index %d", idx);
230 		}
231 	} else {
232 		switch (idx) {
233 		case 0:
234 			desc0 = "channel0, DIMM0 RANK 0/1";
235 			desc1 = "channel0, DIMM0 RANK 2/3";
236 			break;
237 
238 		case 1:
239 			desc0 = "channel0, DIMM1 RANK 0/1";
240 			desc1 = "channel0, DIMM1 RANK 2/3";
241 			break;
242 
243 		case 2:
244 			desc0 = "channel1, DIMM0 RANK 0/1";
245 			desc1 = "channel1, DIMM0 RANK 2/3";
246 			break;
247 
248 		case 3:
249 			desc0 = "channel1, DIMM1 RANK 0/1";
250 			desc1 = "channel1, DIMM1 RANK 2/3";
251 			break;
252 
253 		default:
254 			panic("unsupported index %d", idx);
255 		}
256 	}
257 
258 	err0 = __SHIFTOUT(cor, PCI_X3400UC_MCT2_COR_DIMM0);
259 	if (cor & PCI_X3400UC_MCT2_COR_DIMM0_OV)
260 		ecc_printf(sc, "%s has too many errors\n", desc0);
261 	else if (err0)
262 		ecc_printf(sc, "%s has %d errors", desc0, err0);
263 
264 	if (desc1 != NULL) {
265 		err1 = __SHIFTOUT(cor, PCI_X3400UC_MCT2_COR_DIMM1);
266 		if (cor & PCI_X3400UC_MCT2_COR_DIMM1_OV)
267 			ecc_printf(sc, "%s has too many errors\n", desc1);
268 		else if (err1)
269 			ecc_printf(sc, "%s has %d errors\n", desc1, err1);
270 	}
271 
272 	MCT2_WRITE_4(ofs, 0);
273 }
274 
275 static void
276 ecc_x3400_stop(device_t dev)
277 {
278 	struct ecc_x3400_softc *sc = device_get_softc(dev);
279 
280 	callout_stop_sync(&sc->ecc_callout);
281 }
282 
283 static int
284 ecc_x3400_detach(device_t dev)
285 {
286 	ecc_x3400_stop(dev);
287 	return 0;
288 }
289 
290 static void
291 ecc_x3400_shutdown(device_t dev)
292 {
293 	ecc_x3400_stop(dev);
294 }
295