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