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 } 296