From: Sepherosa Ziehau Date: Wed, 11 Feb 2015 09:06:17 +0000 (+0800) Subject: ecc/e3: Split it into two drivers, coremctl(4) and ecc(4) X-Git-Tag: v4.2.0rc~843 X-Git-Url: https://gitweb.dragonflybsd.org/~tuxillo/dragonfly.git/commitdiff_plain/f7409137935ee81167bddbeb7a340e4c086f6290 ecc/e3: Split it into two drivers, coremctl(4) and ecc(4) coremctl(4), which maps the MCHBAR, is now the parent of ecc(4) for Intel E3 cpus. This paves way to add Intel Core/E3 support to memtemp(4). Tested-by: dillon@ on i3/Haswell and E3/v3 --- diff --git a/sys/conf/files b/sys/conf/files index b1e38e929a..253ce068b5 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1753,8 +1753,10 @@ bus/isa/isa_common.c optional isa bus/isa/isahint.c optional isa bus/isa/pnpeat.c optional isa dev/misc/amdsbwd/amdsbwd.c optional amdsbwd +dev/misc/coremctl/coremctl.c optional coremctl +dev/misc/coremctl/coremctl_if.m optional coremctl dev/misc/ecc/ecc_amd8000.c optional ecc -dev/misc/ecc/ecc_e31200.c optional ecc +dev/misc/ecc/ecc_e3.c optional ecc dev/misc/ecc/ecc_e5.c optional ecc # not ready yet #dev/misc/ecc/ecc_x3400.c optional ecc diff --git a/sys/conf/kmod.mk b/sys/conf/kmod.mk index a862dfe2e1..04fa1713c3 100644 --- a/sys/conf/kmod.mk +++ b/sys/conf/kmod.mk @@ -316,7 +316,8 @@ MFILES?= kern/bus_if.m kern/device_if.m bus/iicbus/iicbb_if.m \ libiconv/iconv_converter_if.m dev/agp/agp_if.m opencrypto/cryptodev_if.m \ bus/mmc/mmcbus_if.m bus/mmc/mmcbr_if.m \ dev/virtual/virtio/virtio/virtio_bus_if.m \ - dev/virtual/virtio/virtio/virtio_if.m + dev/virtual/virtio/virtio/virtio_if.m \ + dev/misc/coremctl/coremctl_if.m .for _srcsrc in ${MFILES} .for _ext in c h diff --git a/sys/config/LINT64 b/sys/config/LINT64 index 4cf1e1112e..c91bcca93b 100644 --- a/sys/config/LINT64 +++ b/sys/config/LINT64 @@ -1273,6 +1273,7 @@ options SND_OLDSTEREO # Miscellaneous hardware: # # bktr: Brooktree bt848/848a/849a/878/879 video capture and TV Tuner board +# coremctl: Intel Core/E3 memory controller (required by ecc(4)) # ecc: ECC memory controller # ipmi: Intelligent Platform Management Interface # joy: joystick @@ -1288,6 +1289,7 @@ options SND_OLDSTEREO # The Rev 2 host cards use a 32K chunk, on a 32K boundary. # The cards can use an IRQ of 11, 12 or 15. +device coremctl device ecc device joy0 at isa? port IO_GAME device nrp diff --git a/sys/config/X86_64_GENERIC b/sys/config/X86_64_GENERIC index 1b1359f805..c3cda37ddd 100644 --- a/sys/config/X86_64_GENERIC +++ b/sys/config/X86_64_GENERIC @@ -185,6 +185,9 @@ device wbsio1 at isa? port 0x4e device lm#3 at wbsio? # Intel Core and newer CPUs on-die digital thermal sensor support device coretemp +device coremctl # support Intel Core and E3 memory controller +device ecc # support AMD8000, Intel E3 and Intel E5 ECC + # requires coremctl. # PCCARD (PCMCIA) support device pccard diff --git a/sys/dev/misc/Makefile b/sys/dev/misc/Makefile index 66b5a6dc1b..c041cc24d2 100644 --- a/sys/dev/misc/Makefile +++ b/sys/dev/misc/Makefile @@ -1,4 +1,4 @@ SUBDIR= amdsbwd cmx cpuctl dcons ecc ichwd ipmi joy kbdmux lpbb \ - nmdm pcfclock putter snp syscons tbridge + nmdm pcfclock putter snp syscons tbridge coremctl .include diff --git a/sys/dev/misc/coremctl/Makefile b/sys/dev/misc/coremctl/Makefile new file mode 100644 index 0000000000..c25040904b --- /dev/null +++ b/sys/dev/misc/coremctl/Makefile @@ -0,0 +1,5 @@ +KMOD = coremctl +SRCS = coremctl.c coremctl_if.c +SRCS += device_if.h bus_if.h pci_if.h pcib_if.h coremctl_if.h + +.include diff --git a/sys/dev/misc/coremctl/coremctl.c b/sys/dev/misc/coremctl/coremctl.c new file mode 100644 index 0000000000..db28e1eac4 --- /dev/null +++ b/sys/dev/misc/coremctl/coremctl.c @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2015 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Sepherosa Ziehau + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "coremctl_if.h" +#include "pcib_if.h" + +#include + +#define COREMCTL_VER_1 1 /* Sandy Bridge */ +#define COREMCTL_VER_2 2 /* Ivy Bridge */ +#define COREMCTL_VER_3 3 /* Haswell */ + +struct coremctl_type { + uint16_t did; + const char *desc; + int ver; /* COREMCTL_VER_ */ +}; + +struct coremctl_softc { + device_t sc_dev; + int sc_ver; /* COREMCTL_VER_ */ + device_t sc_ecc; + volatile uint8_t *sc_mch; +}; + +#define CSR_READ_4(sc, ofs) (*(volatile uint32_t *)((sc)->sc_mch + (ofs))) + +static void coremctl_identify(driver_t *, device_t); +static int coremctl_probe(device_t); +static int coremctl_attach(device_t); +static int coremctl_detach(device_t); +static int coremctl_mch_readreg(device_t, int, uint32_t *); +static int coremctl_pci_read_ivar(device_t, device_t, int, uintptr_t *); +static uint32_t coremctl_pci_read_config(device_t, device_t, int, int); +static void coremctl_pci_write_config(device_t, device_t, int, uint32_t, + int); + +static void coremctl_chaninfo(struct coremctl_softc *, uint32_t, + const char *); + +static const struct coremctl_type coremctl_types[] = { + { PCI_E3V1_MEMCTL_DID, "Intel E3 memory controller", + COREMCTL_VER_1 }, + + { PCI_E3V2_MEMCTL_DID, "Intel E3 v2 memory controller", + COREMCTL_VER_2 }, + + { PCI_E3V3_MEMCTL_DID, "Intel E3 v3 memory controller", + COREMCTL_VER_3 }, + + { PCI_COREV3_MEMCTL_DID, "Intel i3/i5/i7 Haswell memory controller", + COREMCTL_VER_3 }, + + { 0, NULL, 0 } /* required last entry */ +}; + +static device_method_t coremctl_methods[] = { + /* Device interface */ + DEVMETHOD(device_identify, coremctl_identify), + DEVMETHOD(device_probe, coremctl_probe), + DEVMETHOD(device_attach, coremctl_attach), + DEVMETHOD(device_detach, coremctl_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* Bus interface */ + DEVMETHOD(bus_read_ivar, coremctl_pci_read_ivar), + + /* PCI interface */ + DEVMETHOD(pci_read_config, coremctl_pci_read_config), + DEVMETHOD(pci_write_config, coremctl_pci_write_config), + + /* Core memory controller interface */ + DEVMETHOD(coremctl_mch_read, coremctl_mch_readreg), + + DEVMETHOD_END +}; + +static driver_t coremctl_driver = { + "coremctl", + coremctl_methods, + sizeof(struct coremctl_softc) +}; +static devclass_t coremctl_devclass; + +DRIVER_MODULE(coremctl, hostb, coremctl_driver, coremctl_devclass, NULL, NULL); +MODULE_VERSION(coremctl, 1); +MODULE_DEPEND(coremctl, pci, 1, 1, 1); + +static void +coremctl_identify(driver_t *driver, device_t parent) +{ + const struct coremctl_type *t; + uint16_t did; + + /* Already identified */ + if (device_find_child(parent, "coremctl", -1) != NULL) + return; + + if (pci_get_vendor(parent) != PCI_CORE_MEMCTL_VID) + return; + + did = pci_get_device(parent); + for (t = coremctl_types; t->desc != NULL; ++t) { + if (t->did == did) { + if (device_add_child(parent, "coremctl", -1) == NULL) + device_printf(parent, "add coremctl failed\n"); + return; + } + } +} + +static int +coremctl_probe(device_t dev) +{ + const struct coremctl_type *t; + uint16_t did; + + if (pci_get_vendor(dev) != PCI_CORE_MEMCTL_VID) + return ENXIO; + + did = pci_get_device(dev); + for (t = coremctl_types; t->desc != NULL; ++t) { + if (t->did == did) { + struct coremctl_softc *sc = device_get_softc(dev); + + device_set_desc(dev, t->desc); + sc->sc_ver = t->ver; + return 0; + } + } + return ENXIO; +} + +static int +coremctl_attach(device_t dev) +{ + struct coremctl_softc *sc = device_get_softc(dev); + uint32_t capa, dmfc, mch_barlo, mch_barhi; + uint64_t mch_bar; + int dmfc_parsed = 1; + + sc->sc_dev = dev; + + capa = pci_read_config(dev, PCI_CORE_CAPID0_A, 4); + + if (sc->sc_ver == COREMCTL_VER_1) { + dmfc = __SHIFTOUT(capa, PCI_CORE_CAPID0_A_DMFC); + } else { /* v2/v3 */ + uint32_t capb; + + capb = pci_read_config(dev, PCI_CORE_CAPID0_B, 4); + dmfc = __SHIFTOUT(capb, PCI_CORE_CAPID0_B_DMFC); + } + + if (dmfc == PCI_CORE_CAPID0_DMFC_1067) { + device_printf(dev, "CAP DDR3 1067 "); + } else if (dmfc == PCI_CORE_CAPID0_DMFC_1333) { + device_printf(dev, "CAP DDR3 1333 "); + } else { + if (sc->sc_ver == COREMCTL_VER_1) { + if (dmfc == PCI_CORE_CAPID0_DMFC_V1_ALL) + device_printf(dev, "no CAP "); + else + dmfc_parsed = 0; + } else { /* v2/v3 */ + if (dmfc == PCI_CORE_CAPID0_DMFC_1600) + device_printf(dev, "CAP DDR3 1600 "); + else if (dmfc == PCI_CORE_CAPID0_DMFC_1867) + device_printf(dev, "CAP DDR3 1867 "); + else if (dmfc == PCI_CORE_CAPID0_DMFC_2133) + device_printf(dev, "CAP DDR3 2133 "); + else if (dmfc == PCI_CORE_CAPID0_DMFC_2400) + device_printf(dev, "CAP DDR3 2400 "); + else if (dmfc == PCI_CORE_CAPID0_DMFC_2667) + device_printf(dev, "CAP DDR3 2667 "); + else if (dmfc == PCI_CORE_CAPID0_DMFC_2933) + device_printf(dev, "CAP DDR3 2933 "); + else + dmfc_parsed = 0; + } + } + if (!dmfc_parsed) { + device_printf(dev, "unknown DMFC %#x\n", dmfc); + return 0; + } + + if (capa & PCI_CORE_CAPID0_A_ECCDIS) { + kprintf("NON-ECC\n"); + } else { + kprintf("ECC\n"); + sc->sc_ecc = device_add_child(dev, "ecc", -1); + if (sc->sc_ecc == NULL) + device_printf(dev, "add ecc failed\n"); + } + + mch_barlo = pci_read_config(dev, PCI_CORE_MCHBAR_LO, 4); + mch_barhi = pci_read_config(dev, PCI_CORE_MCHBAR_HI, 4); + + mch_bar = (uint64_t)mch_barlo | (((uint64_t)mch_barhi) << 32); + if (bootverbose) + device_printf(dev, "MCHBAR 0x%jx\n", (uintmax_t)mch_bar); + + if (mch_bar & PCI_CORE_MCHBAR_LO_EN) { + uint64_t map_addr = mch_bar & PCI_CORE_MCHBAR_ADDRMASK; + + sc->sc_mch = pmap_mapdev_uncacheable(map_addr, MCH_CORE_SIZE); + + if (bootverbose) { + uint32_t dimm_ch0, dimm_ch1; + + dimm_ch0 = CSR_READ_4(sc, MCH_CORE_DIMM_CH0); + dimm_ch1 = CSR_READ_4(sc, MCH_CORE_DIMM_CH1); + + coremctl_chaninfo(sc, dimm_ch0, "channel0"); + coremctl_chaninfo(sc, dimm_ch1, "channel1"); + } + } else { + device_printf(dev, "MCHBAR is not enabled\n"); + } + + bus_generic_attach(dev); + + return 0; +} + +static void +coremctl_chaninfo(struct coremctl_softc *sc, uint32_t dimm_ch, + const char *desc) +{ + int size_a, size_b; + int dimma_id, dimmb_id; + + dimma_id = 0; + dimmb_id = 1; + if (dimm_ch & MCH_CORE_DIMM_A_SELECT) { + dimma_id = 1; + dimmb_id = 0; + } + + size_a = __SHIFTOUT(dimm_ch, MCH_CORE_DIMM_A_SIZE); + if (size_a != 0) { + device_printf(sc->sc_dev, "%s, DIMM%d %dMB %dx%d\n", desc, + dimma_id, size_a * MCH_CORE_DIMM_SIZE_UNIT, + (dimm_ch & MCH_CORE_DIMM_A_DUAL_RANK) ? 2 : 1, + (dimm_ch & MCH_CORE_DIMM_A_X16) ? 16 : 8); + } + + size_b = __SHIFTOUT(dimm_ch, MCH_CORE_DIMM_B_SIZE); + if (size_b != 0) { + device_printf(sc->sc_dev, "%s, DIMM%d %dMB %dx%d\n", desc, + dimmb_id, size_b * MCH_CORE_DIMM_SIZE_UNIT, + (dimm_ch & MCH_CORE_DIMM_B_DUAL_RANK) ? 2 : 1, + (dimm_ch & MCH_CORE_DIMM_B_X16) ? 16 : 8); + } + + if (size_a == 0 && size_b == 0) + return; + + if (sc->sc_ver == COREMCTL_VER_1 || sc->sc_ver == COREMCTL_VER_2) { + /* This bit is v3 only */ + dimm_ch &= ~MCH_CORE_DIMM_HORI; + } + if (dimm_ch & (MCH_CORE_DIMM_ENHI | MCH_CORE_DIMM_RI | + MCH_CORE_DIMM_HORI)) { + device_printf(sc->sc_dev, "%s", desc); + if (dimm_ch & MCH_CORE_DIMM_RI) + kprintf(", rank interleave"); + if (dimm_ch & MCH_CORE_DIMM_ENHI) + kprintf(", enhanced interleave"); + if (dimm_ch & MCH_CORE_DIMM_HORI) + kprintf(", high order rank interleave"); + kprintf("\n"); + } +} + +static int +coremctl_detach(device_t dev) +{ + struct coremctl_softc *sc = device_get_softc(dev); + + if (sc->sc_ecc != NULL) + device_delete_child(dev, sc->sc_ecc); + bus_generic_detach(dev); + + if (sc->sc_mch != NULL) + pmap_unmapdev((vm_offset_t)sc->sc_mch, MCH_CORE_SIZE); + return 0; +} + +static int +coremctl_mch_readreg(device_t dev, int reg, uint32_t *val) +{ + struct coremctl_softc *sc = device_get_softc(dev); + + if (sc->sc_mch == NULL) + return EOPNOTSUPP; + + *val = CSR_READ_4(sc, reg); + return 0; +} + +static int +coremctl_pci_read_ivar(device_t dev, device_t child, int which, + uintptr_t *result) +{ + return BUS_READ_IVAR(device_get_parent(dev), dev, which, result); +} + +static uint32_t +coremctl_pci_read_config(device_t dev, device_t child, int reg, int width) +{ + return pci_read_config(dev, reg, width); +} + +static void +coremctl_pci_write_config(device_t dev, device_t child, int reg, uint32_t val, + int width) +{ + pci_write_config(dev, reg, val, width); +} diff --git a/sys/dev/misc/coremctl/coremctl_if.m b/sys/dev/misc/coremctl/coremctl_if.m new file mode 100644 index 0000000000..b9fa26301d --- /dev/null +++ b/sys/dev/misc/coremctl/coremctl_if.m @@ -0,0 +1,9 @@ +#include + +INTERFACE coremctl; + +METHOD int mch_read { + device_t dev; + int reg; + uint32_t *val; +}; diff --git a/sys/dev/misc/coremctl/coremctl_reg.h b/sys/dev/misc/coremctl/coremctl_reg.h new file mode 100644 index 0000000000..bea78a6097 --- /dev/null +++ b/sys/dev/misc/coremctl/coremctl_reg.h @@ -0,0 +1,83 @@ +#ifndef _COREMCTL_REG_H_ +#define _COREMCTL_REG_H_ + +#ifndef _SYS_BITOPS_H_ +#include +#endif + +#define PCI_CORE_MEMCTL_VID 0x8086 +#define PCI_E3V1_MEMCTL_DID 0x0108 +#define PCI_E3V2_MEMCTL_DID 0x0158 +#define PCI_E3V3_MEMCTL_DID 0x0c08 +#define PCI_COREV3_MEMCTL_DID 0x0c00 + +#define PCI_CORE_MCHBAR_LO 0x48 +#define PCI_CORE_MCHBAR_LO_EN 0x1 +#define PCI_CORE_MCHBAR_HI 0x4c + +#define PCI_E3_ERRSTS 0xc8 +#define PCI_E3_ERRSTS_DMERR __BIT(1) +#define PCI_E3_ERRSTS_DSERR __BIT(0) + +#define PCI_CORE_CAPID0_A 0xe4 +#define PCI_CORE_CAPID0_A_DMFC __BITS(0, 2) /* v1 */ +#define PCI_CORE_CAPID0_A_ECCDIS __BIT(25) + +#define PCI_CORE_CAPID0_B 0xe8 +#define PCI_CORE_CAPID0_B_DMFC __BITS(4, 6) /* v2/v3 */ + +#define PCI_CORE_CAPID0_DMFC_V1_ALL 0x0 /* v1 */ +#define PCI_CORE_CAPID0_DMFC_2933 0x0 /* v2/v3 */ +#define PCI_CORE_CAPID0_DMFC_2667 0x1 /* v2/v3 */ +#define PCI_CORE_CAPID0_DMFC_2400 0x2 /* v2/v3 */ +#define PCI_CORE_CAPID0_DMFC_2133 0x3 /* v2/v3 */ +#define PCI_CORE_CAPID0_DMFC_1867 0x4 /* v2/v3 */ +#define PCI_CORE_CAPID0_DMFC_1600 0x5 /* v2/v3 */ +#define PCI_CORE_CAPID0_DMFC_1333 0x6 +#define PCI_CORE_CAPID0_DMFC_1067 0x7 + +#define PCI_CORE_MCHBAR_ADDRMASK __BITS64(15, 38) + +#define MCH_CORE_SIZE (32 * 1024) + +#define MCH_E3_ERRLOG0_C0 0x40c8 +#define MCH_E3_ERRLOG1_C0 0x40cc + +#define MCH_E3_ERRLOG0_C1 0x44c8 +#define MCH_E3_ERRLOG1_C1 0x44cc + +#define MCH_E3_ERRLOG0_CERRSTS __BIT(0) +#define MCH_E3_ERRLOG0_MERRSTS __BIT(1) +#define MCH_E3_ERRLOG0_ERRSYND __BITS(16, 23) +#define MCH_E3_ERRLOG0_ERRCHUNK __BITS(24, 26) +#define MCH_E3_ERRLOG0_ERRRANK __BITS(27, 28) +#define MCH_E3_ERRLOG0_ERRBANK __BITS(29, 31) + +#define MCH_E3_ERRLOG1_ERRROW __BITS(0, 15) +#define MCH_E3_ERRLOG1_ERRCOL __BITS(16, 31) + +#define MCH_CORE_DIMM_CH0 0x5004 +#define MCH_CORE_DIMM_CH1 0x5008 + +#define MCH_CORE_DIMM_SIZE_UNIT 256 /* MB */ +#define MCH_CORE_DIMM_A_SIZE __BITS(0, 7) +#define MCH_CORE_DIMM_B_SIZE __BITS(8, 15) +#define MCH_CORE_DIMM_A_SELECT __BIT(16) +#define MCH_CORE_DIMM_A_DUAL_RANK __BIT(17) +#define MCH_CORE_DIMM_B_DUAL_RANK __BIT(18) +#define MCH_CORE_DIMM_A_X16 __BIT(19) +#define MCH_CORE_DIMM_B_X16 __BIT(20) +#define MCH_CORE_DIMM_RI __BIT(21) /* rank interleave */ +/* enchanced interleave */ +#define MCH_CORE_DIMM_ENHI __BIT(22) +#define MCH_E3_DIMM_ECC __BITS(24, 25) +#define MCH_E3_DIMM_ECC_NONE 0x0 +#define MCH_E3_DIMM_ECC_IO 0x1 +#define MCH_E3_DIMM_ECC_LOGIC 0x2 +#define MCH_E3_DIMM_ECC_ALL 0x3 +/* high order rank interleave */ +#define MCH_CORE_DIMM_HORI __BIT(26) /* v3 */ +/* high order rank interleave address (addr bits [20,27]) */ +#define MCH_CORE_DIMM_HORIADDR __BITS(27, 29) /* v3 */ + +#endif /* !_COREMCTL_REG_H_ */ diff --git a/sys/dev/misc/ecc/Makefile b/sys/dev/misc/ecc/Makefile index dff2e5f91d..d0cf2746d4 100644 --- a/sys/dev/misc/ecc/Makefile +++ b/sys/dev/misc/ecc/Makefile @@ -1,6 +1,6 @@ KMOD = ecc -SRCS = ecc_amd8000.c ecc_e31200.c ecc_e5.c +SRCS = ecc_amd8000.c ecc_e3.c ecc_e5.c #ecc_x3400.c -SRCS += device_if.h bus_if.h pci_if.h pcib_if.h +SRCS += device_if.h bus_if.h pci_if.h pcib_if.h coremctl_if.h .include diff --git a/sys/dev/misc/ecc/ecc_e3.c b/sys/dev/misc/ecc/ecc_e3.c new file mode 100644 index 0000000000..1cfba7098c --- /dev/null +++ b/sys/dev/misc/ecc/ecc_e3.c @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2011 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Sepherosa Ziehau + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "coremctl_if.h" +#include "pcib_if.h" + +#include + +#define ECC_E3_VER_1 1 /* Sandy Bridge */ +#define ECC_E3_VER_2 2 /* Ivy Bridge */ +#define ECC_E3_VER_3 3 /* Haswell */ + +struct ecc_e3_type { + uint16_t did; + const char *desc; + int ver; /* ECC_E3_VER_ */ +}; + +struct ecc_e3_softc { + device_t ecc_dev; + device_t ecc_parent; /* non-NULL if parent has MCHBAR */ + struct callout ecc_callout; + int ecc_ver; /* ECC_E3_VER_ */ +}; + +#define ecc_printf(sc, fmt, arg...) \ + device_printf((sc)->ecc_dev, fmt , ##arg) + +static int ecc_e3_probe(device_t); +static int ecc_e3_attach(device_t); +static int ecc_e3_detach(device_t); +static void ecc_e3_shutdown(device_t); + +static void ecc_e3_chaninfo(struct ecc_e3_softc *, uint32_t, const char *); +static void ecc_e3_status(struct ecc_e3_softc *); +static void ecc_e3_callout(void *); +static void ecc_e3_errlog(struct ecc_e3_softc *); +static void ecc_e3_errlog_ch(struct ecc_e3_softc *, int, int, const char *); + +static const struct ecc_e3_type ecc_e3_types[] = { + { PCI_E3V1_MEMCTL_DID, "Intel E3 ECC", ECC_E3_VER_1 }, + { PCI_E3V2_MEMCTL_DID, "Intel E3 v2 ECC", ECC_E3_VER_2 }, + { PCI_E3V3_MEMCTL_DID, "Intel E3 v3 ECC", ECC_E3_VER_3 }, + { 0, NULL, 0 } /* required last entry */ +}; + +static device_method_t ecc_e3_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ecc_e3_probe), + DEVMETHOD(device_attach, ecc_e3_attach), + DEVMETHOD(device_detach, ecc_e3_detach), + DEVMETHOD(device_shutdown, ecc_e3_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD_END +}; + +static driver_t ecc_e3_driver = { + "ecc", + ecc_e3_methods, + sizeof(struct ecc_e3_softc) +}; +static devclass_t ecc_devclass; +DRIVER_MODULE(ecc_e3, coremctl, ecc_e3_driver, ecc_devclass, NULL, NULL); +MODULE_DEPEND(ecc_e3, pci, 1, 1, 1); +MODULE_DEPEND(ecc_e3, coremctl, 1, 1, 1); + +static __inline uint32_t +CSR_READ_4(struct ecc_e3_softc *sc, int ofs) +{ + uint32_t val; + int error; + + error = COREMCTL_MCH_READ(sc->ecc_parent, ofs, &val); + KASSERT(!error, ("mch read failed")); + + return val; +} + +static int +ecc_e3_probe(device_t dev) +{ + const struct ecc_e3_type *t; + uint16_t did; + + if (pci_get_vendor(dev) != PCI_CORE_MEMCTL_VID) + return ENXIO; + + did = pci_get_device(dev); + for (t = ecc_e3_types; t->desc != NULL; ++t) { + if (t->did == did) { + struct ecc_e3_softc *sc = device_get_softc(dev); + + device_set_desc(dev, t->desc); + sc->ecc_ver = t->ver; + return 0; + } + } + return ENXIO; +} + +static int +ecc_e3_attach(device_t dev) +{ + struct ecc_e3_softc *sc = device_get_softc(dev); + uint32_t val; + int error; + + callout_init_mp(&sc->ecc_callout); + sc->ecc_dev = dev; + + /* Probe the existance of MCHBAR */ + error = COREMCTL_MCH_READ(device_get_parent(dev), MCH_CORE_DIMM_CH0, + &val); + if (!error) + sc->ecc_parent = device_get_parent(dev); + + if (sc->ecc_parent != NULL) { + uint32_t dimm_ch0, dimm_ch1; + int ecc_active; + + if (bootverbose) { + ecc_printf(sc, "LOG0_C0 %#x\n", + CSR_READ_4(sc, MCH_E3_ERRLOG0_C0)); + ecc_printf(sc, "LOG0_C1 %#x\n", + CSR_READ_4(sc, MCH_E3_ERRLOG0_C1)); + } + + dimm_ch0 = CSR_READ_4(sc, MCH_CORE_DIMM_CH0); + dimm_ch1 = CSR_READ_4(sc, MCH_CORE_DIMM_CH1); + + if (bootverbose) { + ecc_e3_chaninfo(sc, dimm_ch0, "channel0"); + ecc_e3_chaninfo(sc, dimm_ch1, "channel1"); + } + + ecc_active = 1; + if (sc->ecc_ver == ECC_E3_VER_1 || + sc->ecc_ver == ECC_E3_VER_2) { + if (((dimm_ch0 | dimm_ch1) & MCH_E3_DIMM_ECC) == + MCH_E3_DIMM_ECC_NONE) { + ecc_active = 0; + ecc_printf(sc, "No ECC active\n"); + } + } else { /* v3 */ + uint32_t ecc_mode0, ecc_mode1; + + ecc_mode0 = __SHIFTOUT(dimm_ch0, MCH_E3_DIMM_ECC); + ecc_mode1 = __SHIFTOUT(dimm_ch1, MCH_E3_DIMM_ECC); + + /* + * Only active ALL/NONE is supported + */ + + if (ecc_mode0 != MCH_E3_DIMM_ECC_NONE && + ecc_mode0 != MCH_E3_DIMM_ECC_ALL) { + ecc_active = 0; + ecc_printf(sc, "channel0, invalid ECC " + "active 0x%x\n", ecc_mode0); + } + if (ecc_mode1 != MCH_E3_DIMM_ECC_NONE && + ecc_mode1 != MCH_E3_DIMM_ECC_ALL) { + ecc_active = 0; + ecc_printf(sc, "channel1, invalid ECC " + "active 0x%x\n", ecc_mode1); + } + + if (ecc_mode0 == MCH_E3_DIMM_ECC_NONE && + ecc_mode1 == MCH_E3_DIMM_ECC_NONE) { + ecc_active = 0; + ecc_printf(sc, "No ECC active\n"); + } + } + + if (!ecc_active) + return 0; + } else { + ecc_printf(sc, "MCHBAR is not enabled\n"); + } + + ecc_e3_status(sc); + callout_reset(&sc->ecc_callout, hz, ecc_e3_callout, sc); + + return 0; +} + +static void +ecc_e3_callout(void *xsc) +{ + struct ecc_e3_softc *sc = xsc; + + ecc_e3_status(sc); + callout_reset(&sc->ecc_callout, hz, ecc_e3_callout, sc); +} + +static void +ecc_e3_status(struct ecc_e3_softc *sc) +{ + device_t dev = sc->ecc_dev; + uint16_t errsts; + + errsts = pci_read_config(dev, PCI_E3_ERRSTS, 2); + if (errsts & PCI_E3_ERRSTS_DMERR) + ecc_printf(sc, "Uncorrectable multilple-bit ECC error\n"); + else if (errsts & PCI_E3_ERRSTS_DSERR) + ecc_printf(sc, "Correctable single-bit ECC error\n"); + + if (errsts & (PCI_E3_ERRSTS_DSERR | PCI_E3_ERRSTS_DMERR)) { + if (sc->ecc_parent != NULL) + ecc_e3_errlog(sc); + + /* Clear pending errors */ + pci_write_config(dev, PCI_E3_ERRSTS, errsts, 2); + } +} + +static void +ecc_e3_chaninfo(struct ecc_e3_softc *sc, uint32_t dimm_ch, const char *desc) +{ + int size_a, size_b, ecc; + + size_a = __SHIFTOUT(dimm_ch, MCH_CORE_DIMM_A_SIZE); + size_b = __SHIFTOUT(dimm_ch, MCH_CORE_DIMM_B_SIZE); + if (size_a == 0 && size_b == 0) + return; + + ecc = __SHIFTOUT(dimm_ch, MCH_E3_DIMM_ECC); + if (ecc == MCH_E3_DIMM_ECC_NONE) { + ecc_printf(sc, "%s, no ECC active\n", desc); + } else if (ecc == MCH_E3_DIMM_ECC_ALL) { + ecc_printf(sc, "%s, ECC active IO/logic\n", desc); + } else { + if (sc->ecc_ver == ECC_E3_VER_1 || + sc->ecc_ver == ECC_E3_VER_2) { + if (ecc == MCH_E3_DIMM_ECC_IO) + ecc_printf(sc, "%s, ECC active IO\n", desc); + else + ecc_printf(sc, "%s, ECC active logic\n", desc); + } else { /* v3 */ + ecc_printf(sc, "%s, invalid ECC active 0x%x\n", + desc, ecc); + } + } +} + +static void +ecc_e3_errlog(struct ecc_e3_softc *sc) +{ + ecc_e3_errlog_ch(sc, MCH_E3_ERRLOG0_C0, MCH_E3_ERRLOG1_C0, + "channel0"); + ecc_e3_errlog_ch(sc, MCH_E3_ERRLOG0_C1, MCH_E3_ERRLOG1_C1, + "channel1"); +} + +static void +ecc_e3_errlog_ch(struct ecc_e3_softc *sc, int err0_ofs, int err1_ofs, + const char *desc) +{ + uint32_t err0, err1; + + err0 = CSR_READ_4(sc, err0_ofs); + if ((err0 & (MCH_E3_ERRLOG0_CERRSTS | MCH_E3_ERRLOG0_MERRSTS)) == 0) + return; + + err1 = CSR_READ_4(sc, err1_ofs); + + ecc_printf(sc, "%s error @bank %d, rank %d, chunk %d, syndrome %d, " + "row %d, col %d\n", desc, + __SHIFTOUT(err0, MCH_E3_ERRLOG0_ERRBANK), + __SHIFTOUT(err0, MCH_E3_ERRLOG0_ERRRANK), + __SHIFTOUT(err0, MCH_E3_ERRLOG0_ERRCHUNK), + __SHIFTOUT(err0, MCH_E3_ERRLOG0_ERRSYND), + __SHIFTOUT(err1, MCH_E3_ERRLOG1_ERRROW), + __SHIFTOUT(err1, MCH_E3_ERRLOG1_ERRCOL)); +} + +static int +ecc_e3_detach(device_t dev) +{ + struct ecc_e3_softc *sc = device_get_softc(dev); + + callout_stop_sync(&sc->ecc_callout); + return 0; +} + +static void +ecc_e3_shutdown(device_t dev) +{ + struct ecc_e3_softc *sc = device_get_softc(dev); + + callout_stop_sync(&sc->ecc_callout); +} diff --git a/sys/dev/misc/ecc/ecc_e31200.c b/sys/dev/misc/ecc/ecc_e31200.c deleted file mode 100644 index 1485691fe7..0000000000 --- a/sys/dev/misc/ecc/ecc_e31200.c +++ /dev/null @@ -1,451 +0,0 @@ -/* - * Copyright (c) 2011 The DragonFly Project. All rights reserved. - * - * This code is derived from software contributed to The DragonFly Project - * by Sepherosa Ziehau - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * 3. Neither the name of The DragonFly Project nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific, prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include "pcib_if.h" - -#include - -#define ECC_E31200_VER_1 1 /* Sandy Bridge */ -#define ECC_E31200_VER_2 2 /* Ivy Bridge */ -#define ECC_E31200_VER_3 3 /* Haswell */ - -struct ecc_e31200_memctrl { - uint16_t vid; - uint16_t did; - const char *desc; - int ver; /* ECC_E31200_VER_ */ -}; - -struct ecc_e31200_softc { - device_t ecc_dev; - struct callout ecc_callout; - int ecc_ver; /* ECC_E31200_VER_ */ - volatile uint8_t *ecc_addr; -}; - -#define CSR_READ_4(sc, ofs) (*(volatile uint32_t *)((sc)->ecc_addr + (ofs))) - -#define ecc_printf(sc, fmt, arg...) \ - device_printf((sc)->ecc_dev, fmt , ##arg) - -static void ecc_e31200_identify(driver_t *, device_t); -static int ecc_e31200_probe(device_t); -static int ecc_e31200_attach(device_t); -static int ecc_e31200_detach(device_t); -static void ecc_e31200_shutdown(device_t); - -static void ecc_e31200_chaninfo(struct ecc_e31200_softc *, uint32_t, - const char *); -static void ecc_e31200_status(struct ecc_e31200_softc *); -static void ecc_e31200_callout(void *); -static void ecc_e31200_errlog(struct ecc_e31200_softc *); -static void ecc_e31200_errlog_ch(struct ecc_e31200_softc *, int, int, - const char *); - -static const struct ecc_e31200_memctrl ecc_memctrls[] = { - { 0x8086, 0x0108, "Intel E3-1200 memory controller", - ECC_E31200_VER_1 }, - { 0x8086, 0x0158, "Intel E3-1200 v2 memory controller", - ECC_E31200_VER_2 }, - { 0x8086, 0x0c08, "Intel E3-1200 v3 memory controller", - ECC_E31200_VER_3 }, - { 0, 0, NULL } /* required last entry */ -}; - -static device_method_t ecc_e31200_methods[] = { - /* Device interface */ - DEVMETHOD(device_identify, ecc_e31200_identify), - DEVMETHOD(device_probe, ecc_e31200_probe), - DEVMETHOD(device_attach, ecc_e31200_attach), - DEVMETHOD(device_detach, ecc_e31200_detach), - DEVMETHOD(device_shutdown, ecc_e31200_shutdown), - DEVMETHOD(device_suspend, bus_generic_suspend), - DEVMETHOD(device_resume, bus_generic_resume), - DEVMETHOD_END -}; - -static driver_t ecc_e31200_driver = { - "ecc", - ecc_e31200_methods, - sizeof(struct ecc_e31200_softc) -}; -static devclass_t ecc_devclass; -DRIVER_MODULE(ecc_e31200, hostb, ecc_e31200_driver, ecc_devclass, NULL, NULL); -MODULE_DEPEND(ecc_e31200, pci, 1, 1, 1); - -static void -ecc_e31200_identify(driver_t *driver, device_t parent) -{ - const struct ecc_e31200_memctrl *mc; - uint16_t vid, did; - - /* Already identified */ - if (device_find_child(parent, "ecc", -1) != NULL) - return; - - vid = pci_get_vendor(parent); - did = pci_get_device(parent); - - for (mc = ecc_memctrls; mc->desc != NULL; ++mc) { - if (mc->vid == vid && mc->did == did) { - if (device_add_child(parent, "ecc", -1) == NULL) - device_printf(parent, "add ecc child failed\n"); - return; - } - } -} - -static int -ecc_e31200_probe(device_t dev) -{ - const struct ecc_e31200_memctrl *mc; - uint16_t vid, did; - - vid = pci_get_vendor(dev); - did = pci_get_device(dev); - - for (mc = ecc_memctrls; mc->desc != NULL; ++mc) { - if (mc->vid == vid && mc->did == did) { - struct ecc_e31200_softc *sc = device_get_softc(dev); - - device_set_desc(dev, mc->desc); - sc->ecc_ver = mc->ver; - return (0); - } - } - return (ENXIO); -} - -static int -ecc_e31200_attach(device_t dev) -{ - struct ecc_e31200_softc *sc = device_get_softc(dev); - uint32_t capa, dmfc, mch_barlo, mch_barhi; - uint64_t mch_bar; - int dmfc_parsed = 1; - - callout_init_mp(&sc->ecc_callout); - sc->ecc_dev = dev; - - capa = pci_read_config(dev, PCI_E31200_CAPID0_A, 4); - - if (sc->ecc_ver == ECC_E31200_VER_1) { - dmfc = __SHIFTOUT(capa, PCI_E31200_CAPID0_A_DMFC); - } else { /* V2/V3 */ - uint32_t capb; - - capb = pci_read_config(dev, PCI_E31200_CAPID0_B, 4); - dmfc = __SHIFTOUT(capb, PCI_E31200_CAPID0_B_DMFC); - } - - if (dmfc == PCI_E31200_CAPID0_DMFC_1067) { - ecc_printf(sc, "CAP DDR3 1067 "); - } else if (dmfc == PCI_E31200_CAPID0_DMFC_1333) { - ecc_printf(sc, "CAP DDR3 1333 "); - } else { - if (sc->ecc_ver == ECC_E31200_VER_1) { - if (dmfc == PCI_E31200_CAPID0_DMFC_V1_ALL) - ecc_printf(sc, "no CAP "); - else - dmfc_parsed = 0; - } else { /* V2/V3 */ - if (dmfc == PCI_E31200_CAPID0_DMFC_1600) - ecc_printf(sc, "CAP DDR3 1600 "); - else if (dmfc == PCI_E31200_CAPID0_DMFC_1867) - ecc_printf(sc, "CAP DDR3 1867 "); - else if (dmfc == PCI_E31200_CAPID0_DMFC_2133) - ecc_printf(sc, "CAP DDR3 2133 "); - else if (dmfc == PCI_E31200_CAPID0_DMFC_2400) - ecc_printf(sc, "CAP DDR3 2400 "); - else if (dmfc == PCI_E31200_CAPID0_DMFC_2667) - ecc_printf(sc, "CAP DDR3 2667 "); - else if (dmfc == PCI_E31200_CAPID0_DMFC_2933) - ecc_printf(sc, "CAP DDR3 2933 "); - else - dmfc_parsed = 0; - } - } - if (!dmfc_parsed) { - ecc_printf(sc, "unknown DMFC %#x\n", dmfc); - return 0; - } - - if (capa & PCI_E31200_CAPID0_A_ECCDIS) { - kprintf("NON-ECC\n"); - return 0; - } else { - kprintf("ECC\n"); - } - - mch_barlo = pci_read_config(dev, PCI_E31200_MCHBAR_LO, 4); - mch_barhi = pci_read_config(dev, PCI_E31200_MCHBAR_HI, 4); - - mch_bar = (uint64_t)mch_barlo | (((uint64_t)mch_barhi) << 32); - if (bootverbose) - ecc_printf(sc, "MCHBAR %jx\n", (uintmax_t)mch_bar); - - if (mch_bar & PCI_E31200_MCHBAR_LO_EN) { - uint64_t map_addr = mch_bar & PCI_E31200_MCHBAR_ADDRMASK; - uint32_t dimm_ch0, dimm_ch1; - int ecc_active; - - sc->ecc_addr = pmap_mapdev_uncacheable(map_addr, - MCH_E31200_SIZE); - - if (bootverbose) { - ecc_printf(sc, "LOG0_C0 %#x\n", - CSR_READ_4(sc, MCH_E31200_ERRLOG0_C0)); - ecc_printf(sc, "LOG0_C1 %#x\n", - CSR_READ_4(sc, MCH_E31200_ERRLOG0_C1)); - } - - dimm_ch0 = CSR_READ_4(sc, MCH_E31200_DIMM_CH0); - dimm_ch1 = CSR_READ_4(sc, MCH_E31200_DIMM_CH1); - - if (bootverbose) { - ecc_e31200_chaninfo(sc, dimm_ch0, "channel0"); - ecc_e31200_chaninfo(sc, dimm_ch1, "channel1"); - } - - ecc_active = 1; - if (sc->ecc_ver == ECC_E31200_VER_1 || - sc->ecc_ver == ECC_E31200_VER_2) { - if (((dimm_ch0 | dimm_ch1) & MCH_E31200_DIMM_ECC) == - MCH_E31200_DIMM_ECC_NONE) { - ecc_active = 0; - ecc_printf(sc, "No ECC active\n"); - } - } else { /* V3 */ - uint32_t ecc_mode0, ecc_mode1; - - ecc_mode0 = __SHIFTOUT(dimm_ch0, MCH_E31200_DIMM_ECC); - ecc_mode1 = __SHIFTOUT(dimm_ch1, MCH_E31200_DIMM_ECC); - - /* - * Only active ALL/NONE is supported - */ - - if (ecc_mode0 != MCH_E31200_DIMM_ECC_NONE && - ecc_mode0 != MCH_E31200_DIMM_ECC_ALL) { - ecc_active = 0; - ecc_printf(sc, "channel0, invalid ECC " - "active 0x%x\n", ecc_mode0); - } - if (ecc_mode1 != MCH_E31200_DIMM_ECC_NONE && - ecc_mode1 != MCH_E31200_DIMM_ECC_ALL) { - ecc_active = 0; - ecc_printf(sc, "channel1, invalid ECC " - "active 0x%x\n", ecc_mode1); - } - - if (ecc_mode0 == MCH_E31200_DIMM_ECC_NONE && - ecc_mode1 == MCH_E31200_DIMM_ECC_NONE) { - ecc_active = 0; - ecc_printf(sc, "No ECC active\n"); - } - } - - if (!ecc_active) { - pmap_unmapdev((vm_offset_t)sc->ecc_addr, - MCH_E31200_SIZE); - sc->ecc_addr = NULL; - return 0; - } - } else { - ecc_printf(sc, "MCHBAR is not enabled\n"); - } - - ecc_e31200_status(sc); - callout_reset(&sc->ecc_callout, hz, ecc_e31200_callout, sc); - - return 0; -} - -static void -ecc_e31200_callout(void *xsc) -{ - struct ecc_e31200_softc *sc = xsc; - - ecc_e31200_status(sc); - callout_reset(&sc->ecc_callout, hz, ecc_e31200_callout, sc); -} - -static void -ecc_e31200_status(struct ecc_e31200_softc *sc) -{ - device_t dev = sc->ecc_dev; - uint16_t errsts; - - errsts = pci_read_config(dev, PCI_E31200_ERRSTS, 2); - if (errsts & PCI_E31200_ERRSTS_DMERR) - ecc_printf(sc, "Uncorrectable multilple-bit ECC error\n"); - else if (errsts & PCI_E31200_ERRSTS_DSERR) - ecc_printf(sc, "Correctable single-bit ECC error\n"); - - if (errsts & (PCI_E31200_ERRSTS_DSERR | PCI_E31200_ERRSTS_DMERR)) { - if (sc->ecc_addr != NULL) - ecc_e31200_errlog(sc); - - /* Clear pending errors */ - pci_write_config(dev, PCI_E31200_ERRSTS, errsts, 2); - } -} - -static void -ecc_e31200_chaninfo(struct ecc_e31200_softc *sc, uint32_t dimm_ch, - const char *desc) -{ - int size_a, size_b, ecc; - - size_a = __SHIFTOUT(dimm_ch, MCH_E31200_DIMM_A_SIZE); - if (size_a != 0) { - ecc_printf(sc, "%s, DIMM A %dMB %s %s\n", desc, - size_a * MCH_E31200_DIMM_SIZE_UNIT, - (dimm_ch & MCH_E31200_DIMM_A_X16) ? "X16" : "X8", - (dimm_ch & MCH_E31200_DIMM_A_DUAL_RANK) ? - "DUAL" : "SINGLE"); - } - - size_b = __SHIFTOUT(dimm_ch, MCH_E31200_DIMM_B_SIZE); - if (size_b != 0) { - ecc_printf(sc, "%s, DIMM B %dMB %s %s\n", desc, - size_b * MCH_E31200_DIMM_SIZE_UNIT, - (dimm_ch & MCH_E31200_DIMM_B_X16) ? "X16" : "X8", - (dimm_ch & MCH_E31200_DIMM_B_DUAL_RANK) ? - "DUAL" : "SINGLE"); - } - - if (size_a == 0 && size_b == 0) - return; - - ecc = __SHIFTOUT(dimm_ch, MCH_E31200_DIMM_ECC); - if (ecc == MCH_E31200_DIMM_ECC_NONE) { - ecc_printf(sc, "%s, no ECC active\n", desc); - } else if (ecc == MCH_E31200_DIMM_ECC_ALL) { - ecc_printf(sc, "%s, ECC active IO/logic\n", desc); - } else { - if (sc->ecc_ver == ECC_E31200_VER_1 || - sc->ecc_ver == ECC_E31200_VER_2) { - if (ecc == MCH_E31200_DIMM_ECC_IO) - ecc_printf(sc, "%s, ECC active IO\n", desc); - else - ecc_printf(sc, "%s, ECC active logic\n", desc); - } else { /* V3 */ - ecc_printf(sc, "%s, invalid ECC active 0x%x\n", - desc, ecc); - } - } - - if (sc->ecc_ver == ECC_E31200_VER_1 || - sc->ecc_ver == ECC_E31200_VER_2) { - /* This bit is V3 only */ - dimm_ch &= ~MCH_E31200_DIMM_HORI; - } - if (dimm_ch & (MCH_E31200_DIMM_ENHI | MCH_E31200_DIMM_RI | - MCH_E31200_DIMM_HORI)) { - ecc_printf(sc, "%s", desc); - if (dimm_ch & MCH_E31200_DIMM_RI) - kprintf(", rank interleave"); - if (dimm_ch & MCH_E31200_DIMM_ENHI) - kprintf(", enhanced interleave"); - if (dimm_ch & MCH_E31200_DIMM_HORI) - kprintf(", high order rank interleave"); - kprintf("\n"); - } -} - -static void -ecc_e31200_errlog(struct ecc_e31200_softc *sc) -{ - ecc_e31200_errlog_ch(sc, MCH_E31200_ERRLOG0_C0, MCH_E31200_ERRLOG1_C0, - "channel0"); - ecc_e31200_errlog_ch(sc, MCH_E31200_ERRLOG0_C1, MCH_E31200_ERRLOG1_C1, - "channel1"); -} - -static void -ecc_e31200_errlog_ch(struct ecc_e31200_softc *sc, - int err0_ofs, int err1_ofs, const char *desc) -{ - uint32_t err0, err1; - - err0 = CSR_READ_4(sc, err0_ofs); - if ((err0 & (MCH_E31200_ERRLOG0_CERRSTS | MCH_E31200_ERRLOG0_MERRSTS)) - == 0) - return; - - err1 = CSR_READ_4(sc, err1_ofs); - - ecc_printf(sc, "%s error @bank %d, rank %d, chunk %d, syndrome %d, " - "row %d, col %d\n", desc, - __SHIFTOUT(err0, MCH_E31200_ERRLOG0_ERRBANK), - __SHIFTOUT(err0, MCH_E31200_ERRLOG0_ERRRANK), - __SHIFTOUT(err0, MCH_E31200_ERRLOG0_ERRCHUNK), - __SHIFTOUT(err0, MCH_E31200_ERRLOG0_ERRSYND), - __SHIFTOUT(err1, MCH_E31200_ERRLOG1_ERRROW), - __SHIFTOUT(err1, MCH_E31200_ERRLOG1_ERRCOL)); -} - -static int -ecc_e31200_detach(device_t dev) -{ - struct ecc_e31200_softc *sc = device_get_softc(dev); - - callout_stop_sync(&sc->ecc_callout); - if (sc->ecc_addr != NULL) - pmap_unmapdev((vm_offset_t)sc->ecc_addr, MCH_E31200_SIZE); - return 0; -} - -static void -ecc_e31200_shutdown(device_t dev) -{ - struct ecc_e31200_softc *sc = device_get_softc(dev); - - callout_stop_sync(&sc->ecc_callout); -} diff --git a/sys/dev/misc/ecc/ecc_e31200_reg.h b/sys/dev/misc/ecc/ecc_e31200_reg.h deleted file mode 100644 index 69a0766c01..0000000000 --- a/sys/dev/misc/ecc/ecc_e31200_reg.h +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef _ECC_E31200_REG_H_ -#define _ECC_E31200_REG_H_ - -#ifndef _SYS_BITOPS_H_ -#include -#endif - -#define PCI_E31200_MCHBAR_LO 0x48 -#define PCI_E31200_MCHBAR_LO_EN 0x1 -#define PCI_E31200_MCHBAR_HI 0x4c - -#define PCI_E31200_ERRSTS 0xc8 -#define PCI_E31200_ERRSTS_DMERR __BIT(1) -#define PCI_E31200_ERRSTS_DSERR __BIT(0) - -#define PCI_E31200_CAPID0_A 0xe4 -#define PCI_E31200_CAPID0_A_DMFC __BITS(0, 2) /* V1 */ -#define PCI_E31200_CAPID0_A_ECCDIS __BIT(25) - -#define PCI_E31200_CAPID0_B 0xe8 -#define PCI_E31200_CAPID0_B_DMFC __BITS(4, 6) /* V2/V3 */ - -#define PCI_E31200_CAPID0_DMFC_V1_ALL 0x0 /* V1 */ -#define PCI_E31200_CAPID0_DMFC_2933 0x0 /* V2/V3 */ -#define PCI_E31200_CAPID0_DMFC_2667 0x1 /* V2/V3 */ -#define PCI_E31200_CAPID0_DMFC_2400 0x2 /* V2/V3 */ -#define PCI_E31200_CAPID0_DMFC_2133 0x3 /* V2/V3 */ -#define PCI_E31200_CAPID0_DMFC_1867 0x4 /* V2/V3 */ -#define PCI_E31200_CAPID0_DMFC_1600 0x5 /* V2/V3 */ -#define PCI_E31200_CAPID0_DMFC_1333 0x6 -#define PCI_E31200_CAPID0_DMFC_1067 0x7 - -#define PCI_E31200_MCHBAR_ADDRMASK __BITS64(15, 38) - -#define MCH_E31200_SIZE (32 * 1024) - -#define MCH_E31200_ERRLOG0_C0 0x40c8 -#define MCH_E31200_ERRLOG1_C0 0x40cc - -#define MCH_E31200_ERRLOG0_C1 0x44c8 -#define MCH_E31200_ERRLOG1_C1 0x44cc - -#define MCH_E31200_ERRLOG0_CERRSTS __BIT(0) -#define MCH_E31200_ERRLOG0_MERRSTS __BIT(1) -#define MCH_E31200_ERRLOG0_ERRSYND __BITS(16, 23) -#define MCH_E31200_ERRLOG0_ERRCHUNK __BITS(24, 26) -#define MCH_E31200_ERRLOG0_ERRRANK __BITS(27, 28) -#define MCH_E31200_ERRLOG0_ERRBANK __BITS(29, 31) - -#define MCH_E31200_ERRLOG1_ERRROW __BITS(0, 15) -#define MCH_E31200_ERRLOG1_ERRCOL __BITS(16, 31) - -#define MCH_E31200_DIMM_CH0 0x5004 -#define MCH_E31200_DIMM_CH1 0x5008 - -#define MCH_E31200_DIMM_SIZE_UNIT 256 /* MB */ -#define MCH_E31200_DIMM_A_SIZE __BITS(0, 7) -#define MCH_E31200_DIMM_B_SIZE __BITS(8, 15) -#define MCH_E31200_DIMM_A_DUAL_RANK __BIT(17) -#define MCH_E31200_DIMM_B_DUAL_RANK __BIT(18) -#define MCH_E31200_DIMM_A_X16 __BIT(19) -#define MCH_E31200_DIMM_B_X16 __BIT(20) -#define MCH_E31200_DIMM_RI __BIT(21) /* rank interleave */ -/* enchanced interleave */ -#define MCH_E31200_DIMM_ENHI __BIT(22) -#define MCH_E31200_DIMM_ECC __BITS(24, 25) -#define MCH_E31200_DIMM_ECC_NONE 0x0 -#define MCH_E31200_DIMM_ECC_IO 0x1 -#define MCH_E31200_DIMM_ECC_LOGIC 0x2 -#define MCH_E31200_DIMM_ECC_ALL 0x3 -/* high order rank interleave */ -#define MCH_E31200_DIMM_HORI __BIT(26) /* V3 */ -/* high order rank interleave address (addr bits [20,27]) */ -#define MCH_E31200_DIMM_HORIADDR __BITS(27, 29) /* V3 */ - -#endif /* !_ECC_E31200_REG_H_ */