2 * Copyright (c) 2011 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Sepherosa Ziehau <sepherosa@gmail.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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
35 #include <sys/param.h>
36 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 #include <sys/bitops.h>
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>
51 #include <dev/misc/ecc/ecc_x3400_reg.h>
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)
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)
70 struct ecc_x3400_memctrl
{
76 struct ecc_x3400_softc
{
78 struct callout ecc_callout
;
82 #define ecc_printf(sc, fmt, arg...) \
83 device_printf((sc)->ecc_mydev, fmt , ##arg)
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
);
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
);
95 static const struct ecc_x3400_memctrl ecc_memctrls
[] = {
96 { 0x8086, 0xd130, "Intel X3400 memory controller" },
97 { 0, 0, NULL
} /* required last entry */
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
),
111 static driver_t ecc_x3400_driver
= {
114 sizeof(struct ecc_x3400_softc
)
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);
122 ecc_x3400_probe(device_t dev
)
124 const struct ecc_x3400_memctrl
*mc
;
127 vid
= pci_get_vendor(dev
);
128 did
= pci_get_device(dev
);
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
);
134 if (MC_READ_2(PCIR_VENDOR
) != PCI_X3400UC_MC_VID_ID
||
135 MC_READ_2(PCIR_DEVICE
) != PCI_X3400UC_MC_DID_ID
)
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
)
143 device_set_desc(dev
, mc
->desc
);
152 ecc_x3400_attach(device_t dev
)
154 struct ecc_x3400_softc
*sc
= device_get_softc(dev
);
157 callout_init_mp(&sc
->ecc_callout
);
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");
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");
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
);
176 callout_reset(&sc
->ecc_callout
, hz
, ecc_x3400_callout
, sc
);
182 ecc_x3400_callout(void *xsc
)
184 struct ecc_x3400_softc
*sc
= xsc
;
186 ecc_x3400_status(sc
);
187 callout_reset(&sc
->ecc_callout
, hz
, ecc_x3400_callout
, sc
);
191 ecc_x3400_status(struct ecc_x3400_softc
*sc
)
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);
200 ecc_x3400_status_ch(struct ecc_x3400_softc
*sc
, int ofs
, int idx
)
202 uint32_t cor
, err0
, err1
;
203 const char *desc0
= NULL
, *desc1
= NULL
;
205 cor
= MCT2_READ_4(ofs
);
209 if (sc
->ecc_dimms
> 2) {
212 desc0
= "channel0, DIMM0";
213 desc1
= "channel0, DIMM1";
217 desc0
= "channel0, DIMM2";
221 desc0
= "channel1, DIMM0";
222 desc1
= "channel1, DIMM1";
226 desc0
= "channel1, DIMM2";
230 panic("unsupported index %d", idx
);
235 desc0
= "channel0, DIMM0 RANK 0/1";
236 desc1
= "channel0, DIMM0 RANK 2/3";
240 desc0
= "channel0, DIMM1 RANK 0/1";
241 desc1
= "channel0, DIMM1 RANK 2/3";
245 desc0
= "channel1, DIMM0 RANK 0/1";
246 desc1
= "channel1, DIMM0 RANK 2/3";
250 desc0
= "channel1, DIMM1 RANK 0/1";
251 desc1
= "channel1, DIMM1 RANK 2/3";
255 panic("unsupported index %d", idx
);
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
);
263 ecc_printf(sc
, "%s has %d errors", desc0
, err0
);
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
);
270 ecc_printf(sc
, "%s has %d errors\n", desc1
, err1
);
273 MCT2_WRITE_4(ofs
, 0);
277 ecc_x3400_stop(device_t dev
)
279 struct ecc_x3400_softc
*sc
= device_get_softc(dev
);
281 callout_cancel(&sc
->ecc_callout
);
285 ecc_x3400_detach(device_t dev
)
292 ecc_x3400_shutdown(device_t dev
)