Page MenuHomeFreeBSD

D39663.diff
No OneTemporary

D39663.diff

diff --git a/sys/amd64/conf/NOTES b/sys/amd64/conf/NOTES
--- a/sys/amd64/conf/NOTES
+++ b/sys/amd64/conf/NOTES
@@ -464,6 +464,10 @@
# PMC-Sierra SAS/SATA controller
device pmspcv
+#
+# Intel Pondicherry2 memory controller
+device pnd2_edac
+
#
# Intel QuickAssist driver with OpenCrypto support
#
diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64
--- a/sys/conf/files.amd64
+++ b/sys/conf/files.amd64
@@ -384,6 +384,7 @@
dev/p2sb/p2sb.c optional p2sb pci
dev/p2sb/lewisburg_gpiocm.c optional lbggpiocm p2sb
dev/p2sb/lewisburg_gpio.c optional lbggpio lbggpiocm
+dev/pnd2_edac/pnd2_edac.c optional pnd2_edac
isa/syscons_isa.c optional sc
isa/vga_isa.c optional vga
kern/imgact_aout.c optional compat_aout
diff --git a/sys/dev/pnd2_edac/pnd2_edac.c b/sys/dev/pnd2_edac/pnd2_edac.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/pnd2_edac/pnd2_edac.c
@@ -0,0 +1,662 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ * Copyright (c) 2020 Semihalf
+ * Copyright (c) 2020 Juniper Networks, Inc.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_extern.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/cpufunc.h>
+#include <machine/cputypes.h>
+#include <machine/md_var.h>
+#include <machine/specialreg.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include <machine/pci_cfgreg.h>
+
+#include <x86/mca.h>
+
+#define BIT(x) (1 << (x))
+#define EXTRACT_BIT(x, y) (((x) >> (y)) & 1)
+
+#define PCICONF_MCHBAR_LOADDR 0x48
+#define PCICONF_MCHBAR_LOADDR_SHIFT 16
+#define PCICONF_MCHBAR_LOADDR_MASK (0xFFFF << PCICONF_MCHBAR_LOADDR_SHIFT)
+#define PCICONF_MCHBAR_HIADDR 0x4C
+#define PCICONF_MCHBAR_HIADDR_MASK 0xDF
+#define PCICONF_TOOUD_LOADDR 0xA8
+#define PCICONF_TOOUD_LOADDR_SHIFT 20
+#define PCICONF_TOOUD_LOADDR_MASK ((BIT(12) - 1) << PCICONF_TOOUD_LOADDR_SHIFT)
+#define PCICONF_TOOUD_HIADDR 0xAC
+#define PCICONF_TOOUD_HIADDR_MASK (BIT(7) - 1)
+#define PCICONF_TOLUD 0xBC
+#define PCICONF_TOLUD_SHIFT 20
+#define PCICONF_TOLUD_MASK ((BIT(12) - 1) << PCICONF_TOLUD_SHIFT)
+#define PCICONF_P2SB_LOADDR 0x10
+#define PCICONF_P2SB_LOADDR_MASK (0xFF << 24)
+#define PCICONF_P2SB_HIADDR 0x14
+
+#define P2SB_DMAP 0xB4
+#define P2SB_DMAP_BANKXOR BIT(6)
+
+#define P2SB_UCELOG 0x64
+#define P2SB_UCEADDR 0x68
+#define P2SB_SBELOG 0x6C
+#define P2SB_SBEADDR 0x70
+
+#define P2SB_SBECNT0 0x74
+#define P2SB_SBECNT(x) (P2SB_SBECNT0 + 4*(x))
+#define P2SB_SBECNT_MASK 0xFFFF
+
+#define P2SB_SBELOG1 0xD0
+
+#define P2SB_DPATROL_SCRUB_CFG 0xB0
+#define P2SB_DPATROL_SCRUB_CFG_PAT_SCRUB_ENABLE BIT(31)
+
+#define P2SB_DERRSTS 0x124
+
+#define P2SB_DERCNTSEL 0x12C
+#define P2SB_DERCNTSEL_SELALL 0x1E
+
+#define P2SB_DERRCNT 0x130
+
+#define P2SB_ERRINJCTL 0x134
+#define P2SB_ERRINJCTL_INJEN BIT(0)
+#define P2SB_ERRINJCTL_INJONCE BIT(1)
+#define P2SB_ERRINJCTL_INJADREN BIT(2)
+#define P2SB_ERRINJCTL_INJSRCEN BIT(3)
+#define P2SB_ERRINJCTL_INJECC BIT(4)
+#define P2SB_ERRINJCTL_INJWRITE BIT(5)
+#define P2SB_ERRINJCTL_INJREAD BIT(6)
+#define P2SB_ERRINJCTL_INJPAT BIT(7)
+#define P2SB_ERRINJCTL_INJECCPAR (0xFF << 24)
+
+#define P2SB_ERRINJADDR 0x138
+#define P2SB_ERRINJAMSK 0x13C
+#define P2SB_ERRINJDATA0 0x140
+#define P2SB_ERRINJDATA1 0x144
+
+#define P2SB_DRP 0x158
+#define P2SB_DRP_RKEN0 BIT(0)
+#define P2SB_DRP_RKEN1 BIT(1)
+#define P2SB_DRP_RKEN2 BIT(2)
+#define P2SB_DRP_RKEN3 BIT(3)
+#define P2SB_DRP_DIMMM0_WID (BIT(4) | BIT(5))
+#define P2SB_DRP_DIMMM1_WID (BIT(8) | BIT(9))
+#define P2SB_DRP_DIMM_FLIP BIT(16)
+
+#define P2SB_DMAP0 0x148
+#define P2SB_DMAP1 0x14C
+#define P2SB_DMAP2 0x150
+#define P2SB_DMAPX_ROW(x, y) (((x) >> (5*(y))) & 0x1F)
+
+#define P2SB_DMAP3 0x154
+#define P2SB_DMAP3_COL(x, y) (((x) >> (4*(y))) & 0xF)
+
+#define P2SB_DMAP4 0x174
+#define P2SB_DMAP4_BNK0 0x1F
+#define P2SB_DMAP4_BNK1_SHIFT 5
+#define P2SB_DMAP4_BNK1 (0x1F << P2SB_DMAP4_BNK1_SHIFT)
+#define P2SB_DMAP4_BNK2_SHIFT 10
+#define P2SB_DMAP4_BNK2 (0x1F << P2SB_DMAP4_BNK2_SHIFT)
+#define P2SB_DMAP4_BNK3_SHIFT 15
+#define P2SB_DMAP4_BNK3 (0x1F << P2SB_DMAP4_BNK3_SHIFT)
+#define P2SB_DMAP4_RK0_SHIFT 20
+#define P2SB_DMAP4_RK0 (0x1F << P2SB_DMAP4_RK0_SHIFT)
+#define P2SB_DMAP4_RK1_SHIFT 25
+#define P2SB_DMAP4_RK1 (0x1F << P2SB_DMAP4_RK1_SHIFT)
+
+#define P2SB_DECCTRL 0x180
+#define P2SB_DECCTRL_ECCEN BIT(0)
+
+#define P2SB_PCI_SLOT 0x1F
+#define P2SB_PCI_FUNC 0x1
+#define P2SB_CTRL 0xE0
+#define P2SB_CTRL_HIDE BIT(8)
+
+#define P2SB_PORT10_OFFSET (0x10 << 16)
+#define P2SB_PORT12_OFFSET (0x12 << 16)
+
+#define MCH_CHASH 0x65C0
+#define MCH_CHASH_SLICE1_DIS BIT(0)
+#define MCH_CHASH_INTLV_SHIFT 1
+#define MCH_CHASH_INTLV_MASK (BIT(1) | BIT(2))
+
+#define P2SB_PORT_SIZE 0x10000
+#define MCHBAR_SIZE 0x8000
+
+#define DECODE_BASE_SHIFT 6
+#define DECODE_RANK_SHIFT 13
+#define ADDR_4GB (1ul << 32)
+
+#define P2SB_NUM_RANKS 4
+
+#define P2SB_ERR_MULTIBIT 2
+#define P2SB_ERR_SINGLEBIT 1
+
+struct pnd2_slice {
+ uint8_t rank0_shift; /* Which bit in pmi corresponds to dram rank. */
+ uint8_t rank1_shift;
+
+ bool dimm_flip;
+
+ uint8_t bank0_shift;
+ uint8_t bank1_shift;
+ uint8_t bank2_shift;
+ uint8_t bank3_shift;
+ bool bank_xor;
+
+ uint8_t row_shifts[18];
+
+ uint8_t col_shifts[7];
+};
+
+struct dram_pos {
+ int channel;
+ int dimm;
+ int rank;
+ int bank;
+ int row;
+ int col;
+};
+
+struct pnd2_softc {
+ device_t dev;
+
+ struct resource *sc_mch_res;
+ struct resource *sc_p2sb_res;
+
+ uint64_t sc_max_low_addr;
+ uint64_t sc_max_high_addr;
+
+ uint8_t sc_slice_bit;
+ bool sc_two_slices;
+
+ struct pnd2_slice sc_slice_desc;
+
+ uint64_t sc_ce_count;
+};
+
+static int pnd2_detect_dimm_geom(struct pnd2_softc *);
+static int pnd2_decode_pmi(struct pnd2_softc *, uint64_t, uint64_t *);
+static void pnd2_decode_geom(struct pnd2_softc *, uint64_t, struct dram_pos *);
+static int pnd2_sysctl_inject(SYSCTL_HANDLER_ARGS);
+static int pnd2_sysctl_scrub(SYSCTL_HANDLER_ARGS);
+
+static void pnd2_identify(driver_t *, device_t);
+static int pnd2_probe(device_t);
+static int pnd2_attach(device_t);
+static int pnd2_detach(device_t);
+
+static int
+pnd2_detect_dimm_geom(struct pnd2_softc *sc)
+{
+ uint64_t reg64;
+ uint32_t reg32;
+ int i;
+
+ /*
+ * We have two windows of DRAM physical addresses:
+ * 1. [0, sc_max_low_addr)
+ * 2. [4GB, sc_max_high_addr)
+ */
+ sc->sc_max_low_addr = pci_cfgregread(0, 0, 0, PCICONF_TOLUD, 4) &
+ PCICONF_TOLUD_MASK;
+ sc->sc_max_high_addr = pci_cfgregread(0, 0, 0, PCICONF_TOOUD_HIADDR, 4) &
+ PCICONF_TOOUD_HIADDR_MASK;
+ sc->sc_max_high_addr <<= 32;
+ sc->sc_max_high_addr |= pci_cfgregread(0, 0, 0, PCICONF_TOOUD_LOADDR, 4) &
+ PCICONF_TOOUD_LOADDR_MASK;
+
+ reg64 = bus_read_8(sc->sc_mch_res, MCH_CHASH);
+ sc->sc_two_slices = ((reg64 & MCH_CHASH_SLICE1_DIS) == 0);
+ if (sc->sc_two_slices) {
+ device_printf(sc->dev,
+ "Two slices DIMM configuration is currently unsupported\n");
+ return (ENXIO);
+ }
+ sc->sc_slice_bit = (reg64 & MCH_CHASH_INTLV_MASK) >> MCH_CHASH_INTLV_SHIFT;
+
+ reg32 = bus_read_4(sc->sc_p2sb_res, P2SB_DECCTRL);
+ if ((reg32 & P2SB_DECCTRL_ECCEN) == 0) {
+ device_printf(sc->dev, "ECC has been disabled in firmware\n");
+ return (ENXIO);
+ }
+
+ reg32 = bus_read_4(sc->sc_p2sb_res, P2SB_DRP);
+ sc->sc_slice_desc.dimm_flip = (reg32 & P2SB_DRP_DIMM_FLIP) != 0;
+
+ reg32 = bus_read_4(sc->sc_p2sb_res, P2SB_DMAP4);
+ sc->sc_slice_desc.rank0_shift = ((reg32 & P2SB_DMAP4_RK0) >>
+ P2SB_DMAP4_RK0_SHIFT) + DECODE_RANK_SHIFT;
+ sc->sc_slice_desc.rank1_shift = ((reg32 & P2SB_DMAP4_RK1) >>
+ P2SB_DMAP4_RK1_SHIFT) + DECODE_RANK_SHIFT;
+ sc->sc_slice_desc.bank0_shift = (reg32 & P2SB_DMAP4_BNK0) +
+ DECODE_BASE_SHIFT;
+ sc->sc_slice_desc.bank1_shift = ((reg32 & P2SB_DMAP4_BNK1) >>
+ P2SB_DMAP4_BNK1_SHIFT) + DECODE_BASE_SHIFT;
+ sc->sc_slice_desc.bank2_shift = ((reg32 & P2SB_DMAP4_BNK2) >>
+ P2SB_DMAP4_BNK2_SHIFT) + DECODE_BASE_SHIFT;
+ sc->sc_slice_desc.bank3_shift = ((reg32 & P2SB_DMAP4_BNK3) >>
+ P2SB_DMAP4_BNK3_SHIFT) + DECODE_BASE_SHIFT;
+
+ reg32 = bus_read_4(sc->sc_p2sb_res, P2SB_DMAP);
+ sc->sc_slice_desc.bank_xor = (reg32 & P2SB_DMAP_BANKXOR) != 0;
+
+ reg32 = bus_read_4(sc->sc_p2sb_res, P2SB_DMAP0);
+ for (i = 0; i < 6; i++)
+ sc->sc_slice_desc.row_shifts[i] = P2SB_DMAPX_ROW(reg32, i) +
+ DECODE_BASE_SHIFT;
+
+ reg32 = bus_read_4(sc->sc_p2sb_res, P2SB_DMAP1);
+ for (i = 0; i < 6; i++)
+ sc->sc_slice_desc.row_shifts[i + 6] = P2SB_DMAPX_ROW(reg32, i) +
+ DECODE_BASE_SHIFT;
+
+ reg32 = bus_read_4(sc->sc_p2sb_res, P2SB_DMAP2);
+ for (i = 0; i < 6; i++)
+ sc->sc_slice_desc.row_shifts[i + 12] = P2SB_DMAPX_ROW(reg32, i) +
+ DECODE_BASE_SHIFT;
+
+ reg32 = bus_read_4(sc->sc_p2sb_res, P2SB_DMAP3);
+ for (i = 0; i < 7; i++)
+ sc->sc_slice_desc.col_shifts[i] = P2SB_DMAP3_COL(reg32, i) +
+ DECODE_BASE_SHIFT;
+
+ return (0);
+}
+
+static void
+pnd2_decode_geom(struct pnd2_softc *sc, uint64_t pmiaddr, struct dram_pos *pos)
+{
+ struct pnd2_slice *desc = &sc->sc_slice_desc;
+ int i;
+
+ pos->channel = 0;
+
+ pos->rank = EXTRACT_BIT(pmiaddr, desc->rank0_shift);
+ pos->rank |= EXTRACT_BIT(pmiaddr, desc->rank1_shift) << 1;
+
+ pos->dimm = (pos->rank >= 2) ^ desc->dimm_flip;
+
+ pos->bank = EXTRACT_BIT(pmiaddr, desc->bank0_shift);
+ pos->bank |= EXTRACT_BIT(pmiaddr, desc->bank1_shift) << 1;
+ pos->bank |= EXTRACT_BIT(pmiaddr, desc->bank2_shift) << 2;
+ pos->bank |= EXTRACT_BIT(pmiaddr, desc->bank3_shift) << 3;
+ if (desc->bank_xor) {
+ pos->bank ^= EXTRACT_BIT(pmiaddr, desc->row_shifts[6]);
+ pos->bank ^= EXTRACT_BIT(pmiaddr, desc->row_shifts[7]) << 1;
+ pos->bank ^= EXTRACT_BIT(pmiaddr, desc->col_shifts[0]) << 2;
+ pos->bank ^= EXTRACT_BIT(pmiaddr, desc->row_shifts[2]) << 3;
+ }
+
+ pos->row = 0;
+ for (i = 0; i < 18; i++) {
+ if (i >= 14 && desc->row_shifts[i] == (31 + DECODE_BASE_SHIFT))
+ continue;
+
+ pos->row |= EXTRACT_BIT(pmiaddr, desc->row_shifts[i]) << i;
+ }
+
+ pos->col = 0;
+ for (i = 0; i < 7; i++)
+ pos->col |= EXTRACT_BIT(pmiaddr, desc->col_shifts[i]) <<
+ (i + 3);
+}
+
+static int
+pnd2_decode_pmi(struct pnd2_softc *sc, uint64_t addr, uint64_t *pmiaddr)
+{
+
+ if ((addr < ADDR_4GB && addr >= sc->sc_max_low_addr) ||
+ addr > sc->sc_max_high_addr)
+ return (EINVAL);
+
+ if (addr >= ADDR_4GB)
+ addr -= (ADDR_4GB - sc->sc_max_low_addr);
+
+ *pmiaddr = addr;
+
+ return (0);
+}
+
+static int
+pnd2_sysctl_inject(SYSCTL_HANDLER_ARGS)
+{
+ struct pnd2_softc *sc = (struct pnd2_softc*) arg1;
+ int type = 0; /* 1 - generate CE, 2 - UE */
+ void *addr;
+ vm_offset_t pmiaddr;
+ uint32_t reg;
+ int error;
+
+ error = sysctl_handle_int(oidp, &type, 0, req);
+ if (error || req->newptr == NULL)
+ return (error);
+
+ if (!type || type > 2)
+ return (EINVAL);
+
+ addr = kmem_alloc_contig(PAGE_SIZE, M_WAITOK | M_ZERO,
+ 0, ~0, PAGE_SIZE, 0, VM_MEMATTR_UNCACHEABLE);
+ error = pnd2_decode_pmi(sc, pmap_kextract((vm_offset_t)addr), &pmiaddr);
+ if (error != 0)
+ goto out;
+
+ /*
+ * Bits 0:4 are specified in another register.
+ * Since we allocated page aligned memory there is no need
+ * to do that in our case.
+ * sc->sc_max_high_addr should guarantee that address fits in 36 bits.
+ * Higher addresses are used for DIMM1, which is not supported right now.
+ */
+ bus_write_4(sc->sc_p2sb_res, P2SB_ERRINJADDR, pmiaddr >> 5);
+ bus_barrier(sc->sc_p2sb_res, P2SB_ERRINJADDR, 4,
+ BUS_SPACE_BARRIER_WRITE | BUS_SPACE_BARRIER_READ);
+ reg = bus_read_4(sc->sc_p2sb_res, P2SB_ERRINJADDR);
+ if (reg == 0) {
+ device_printf(sc->dev, "Error injection has been disabled in firmware.");
+ return (ENXIO);
+ }
+
+ bus_write_4(sc->sc_p2sb_res, P2SB_ERRINJAMSK, 0);
+ if (type == P2SB_ERR_SINGLEBIT)
+ bus_write_4(sc->sc_p2sb_res, P2SB_ERRINJDATA0, 1);
+ else
+ bus_write_4(sc->sc_p2sb_res, P2SB_ERRINJDATA0, 3);
+
+ bus_write_4(sc->sc_p2sb_res, P2SB_ERRINJDATA1, 0);
+
+ bus_write_4(sc->sc_p2sb_res, P2SB_ERRINJCTL,
+ P2SB_ERRINJCTL_INJEN | P2SB_ERRINJCTL_INJONCE |
+ P2SB_ERRINJCTL_INJWRITE | P2SB_ERRINJCTL_INJADREN |
+ P2SB_ERRINJCTL_INJECC);
+ bus_barrier(sc->sc_p2sb_res, P2SB_ERRINJCTL, 4,
+ BUS_SPACE_BARRIER_WRITE | BUS_SPACE_BARRIER_READ);
+
+ DELAY(10000);
+ *((volatile uint32_t*)addr) = 0xdeadbeef;
+ DELAY(10000);
+ if (*((volatile uint32_t*)addr) != 0xdeadbeef)
+ printf("Wrong value read after error injection.\n");
+out:
+ /* Just to be safe. */
+ bus_write_4(sc->sc_p2sb_res, P2SB_ERRINJCTL, 0);
+ bus_write_4(sc->sc_p2sb_res, P2SB_ERRINJADDR, 0);
+ bus_write_4(sc->sc_p2sb_res, P2SB_ERRINJDATA0, 0);
+ kmem_free(addr, PAGE_SIZE);
+ return (error);
+
+}
+
+static int
+pnd2_sysctl_scrub(SYSCTL_HANDLER_ARGS)
+{
+ struct pnd2_softc *sc = (struct pnd2_softc*) arg1;
+ int enable, error;
+ uint32_t reg;
+
+ reg = bus_read_4(sc->sc_p2sb_res, P2SB_DPATROL_SCRUB_CFG);
+ enable = (reg & P2SB_DPATROL_SCRUB_CFG_PAT_SCRUB_ENABLE) ? 1 : 0;
+
+ error = sysctl_handle_int(oidp, &enable, 0, req);
+ if (error || req->newptr == NULL)
+ return (error);
+
+ if (enable > 1)
+ return (EINVAL);
+
+ if (enable)
+ reg |= P2SB_DPATROL_SCRUB_CFG_PAT_SCRUB_ENABLE;
+ else
+ reg &= ~P2SB_DPATROL_SCRUB_CFG_PAT_SCRUB_ENABLE;
+
+ bus_write_4(sc->sc_p2sb_res, P2SB_DPATROL_SCRUB_CFG, reg);
+
+ return (0);
+}
+
+static void
+pnd2_cmc(void *aux, const struct mca_record *rec)
+{
+ struct pnd2_softc *sc = (struct pnd2_softc*)aux;
+ struct dram_pos pos;
+ uint64_t pmi;
+ uint32_t reg;
+ int i, error;
+
+ for (i = 0; i < P2SB_NUM_RANKS; i++) {
+ reg = bus_read_4(sc->sc_p2sb_res, P2SB_SBECNT(i)) &
+ P2SB_SBECNT_MASK;
+ sc->sc_ce_count += reg;
+ bus_write_4(sc->sc_p2sb_res, P2SB_SBECNT(i), P2SB_SBECNT_MASK);
+ }
+
+ if (rec->mr_addr == 0)
+ return;
+
+ error = pnd2_decode_pmi(sc, rec->mr_addr, &pmi);
+ if (error != 0)
+ return;
+ pnd2_decode_geom(sc, pmi, &pos);
+
+ printf("MCA: channel: %d, DIMM: %d, rank: 0x%x, bank: 0x%x,"
+ " col: 0x%x, row: 0x%x\n",
+ pos.channel, pos.dimm, pos.rank, pos.bank, pos.col, pos.row);
+}
+
+static void
+pnd2_identify(driver_t *driver, device_t parent)
+{
+ uint64_t p2sb_base_addr, mch_base_addr;
+ uint32_t p2sb_ctrl;
+ device_t dev;
+
+ if (device_find_child(parent, driver->name, 0) != NULL)
+ return;
+
+ if (cpu_vendor_id != CPU_VENDOR_INTEL ||
+ CPUID_TO_FAMILY(cpu_id) != 0x6 || /* Atom */
+ CPUID_TO_MODEL(cpu_id) != 0x5F) /* Denverton */
+ return;
+
+ /*
+ * Get Primary to Side Band Bridge base address.
+ * If P2SB_CTRL_HIDE is set all reads from cfg space will return ffs.
+ * Setting/clearing is done by setting one byte of CTRL register,
+ * it has to be done that way since we don't know other bits in
+ * this register. It is safe as the adjacent 7 bits are reserved and
+ * writes to them is ignored.
+ */
+ p2sb_ctrl = pci_cfgregread(0, P2SB_PCI_SLOT, P2SB_PCI_FUNC, P2SB_CTRL,
+ sizeof(uint32_t));
+ if (p2sb_ctrl == 0xffffffff)
+ pci_cfgregwrite(0, P2SB_PCI_SLOT, P2SB_PCI_FUNC, P2SB_CTRL + 1,
+ 0, 1);
+
+ p2sb_base_addr = pci_cfgregread(0, P2SB_PCI_SLOT, P2SB_PCI_FUNC,
+ PCICONF_P2SB_HIADDR, 4);
+ p2sb_base_addr <<= 32;
+ p2sb_base_addr |= pci_cfgregread(0, P2SB_PCI_SLOT, P2SB_PCI_FUNC,
+ PCICONF_P2SB_LOADDR, 4) & PCICONF_P2SB_LOADDR_MASK;
+
+ /* Restore previous setting. */
+ if (p2sb_ctrl == 0xffffffff)
+ pci_cfgregwrite(0, P2SB_PCI_SLOT, P2SB_PCI_FUNC, P2SB_CTRL + 1,
+ 1, 1);
+
+ /* Read MCHBAR base address from base pci hub. */
+ mch_base_addr = pci_cfgregread(0, 0, 0, PCICONF_MCHBAR_HIADDR, 4) &
+ PCICONF_MCHBAR_HIADDR_MASK;
+ mch_base_addr <<= 32;
+ mch_base_addr |= pci_cfgregread(0, 0, 0, PCICONF_MCHBAR_LOADDR, 4) &
+ PCICONF_MCHBAR_LOADDR_MASK;
+
+ dev = BUS_ADD_CHILD(parent, 0, driver->name, 0);
+ if (dev == NULL)
+ return;
+
+ bus_set_resource(dev, SYS_RES_MEMORY, 0, mch_base_addr, MCHBAR_SIZE);
+ bus_set_resource(dev, SYS_RES_MEMORY, 1,
+ p2sb_base_addr + P2SB_PORT10_OFFSET, P2SB_PORT_SIZE);
+ bus_set_resource(dev, SYS_RES_MEMORY, 2,
+ p2sb_base_addr + P2SB_PORT12_OFFSET, P2SB_PORT_SIZE);
+}
+
+static void
+pnd2_check_uce(device_t dev)
+{
+ struct pnd2_softc *sc = device_get_softc(dev);
+ uint32_t reg;
+
+ reg = bus_read_4(sc->sc_p2sb_res, P2SB_DERRCNT);
+
+ if (reg != 0) {
+ device_printf(dev, "Uncorrectable ECC error has been detected.\n");
+ /* Write to clear the register. */
+ bus_write_4(sc->sc_p2sb_res, P2SB_DERRCNT, 0xFF);
+ }
+}
+
+static int
+pnd2_probe(device_t dev)
+{
+
+ device_set_desc(dev, "Pondicherry2 memory controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+pnd2_attach(device_t dev)
+{
+ struct pnd2_softc *sc = device_get_softc(dev);
+ int error;
+ int rid;
+
+ sc->dev = dev;
+
+ rid = 0;
+ sc->sc_mch_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->sc_mch_res == NULL) {
+ device_printf(dev, "Failed to allocate MCHBAR\n");
+ return (ENXIO);
+ }
+ rid = 1;
+ sc->sc_p2sb_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->sc_p2sb_res == NULL) {
+ device_printf(dev, "Failed to allocate P2SB PORT10\n");
+ pnd2_detach(dev);
+ return (ENXIO);
+ }
+
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "err_inject", CTLTYPE_INT | CTLFLAG_RW,
+ sc, 0, pnd2_sysctl_inject, "U",
+ "Inject correctable(1), or uncorrectable(2) ECC error");
+
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "patrol_scrub", CTLTYPE_INT | CTLFLAG_RW,
+ sc, 0, pnd2_sysctl_scrub, "U",
+ "Enable, or disable (1/0) DRAM patrol scrubbing.");
+
+ SYSCTL_ADD_ULONG(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "ce_count", CTLFLAG_RD, &sc->sc_ce_count,
+ "Amount of correctable ECC errors.");
+
+ error = pnd2_detect_dimm_geom(sc);
+ if (error != 0) {
+ pnd2_detach(dev);
+ return (error);
+ }
+
+ /* Disable error injection */
+ bus_write_4(sc->sc_p2sb_res, P2SB_ERRINJCTL, 0);
+ bus_write_4(sc->sc_p2sb_res, P2SB_ERRINJADDR, 0);
+ bus_write_4(sc->sc_p2sb_res, P2SB_ERRINJDATA0, 0);
+ /* Unmask logging of uncorrectable errors. */
+ bus_write_4(sc->sc_p2sb_res, P2SB_DERCNTSEL, P2SB_DERCNTSEL_SELALL);
+ pnd2_check_uce(dev);
+
+ mca_register_decode_func(pnd2_cmc, sc);
+
+ return (0);
+}
+
+static int
+pnd2_detach(device_t dev)
+{
+ struct pnd2_softc *sc = device_get_softc(dev);
+
+ mca_deregister_decode_func(pnd2_cmc);
+
+ if (sc->sc_mch_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mch_res);
+
+ if (sc->sc_p2sb_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 1, sc->sc_p2sb_res);
+
+ return (0);
+}
+
+static device_method_t pnd2_edac_methods[] = {
+ DEVMETHOD(device_identify, pnd2_identify),
+ DEVMETHOD(device_probe, pnd2_probe),
+ DEVMETHOD(device_attach, pnd2_attach),
+ DEVMETHOD(device_detach, pnd2_detach),
+ DEVMETHOD_END
+};
+
+static driver_t pnd2_edac_driver = {
+ .name = "pnd2_edac",
+ .methods = pnd2_edac_methods,
+ .size = sizeof(struct pnd2_softc),
+};
+
+DRIVER_MODULE(pnd2_edac, cpu, pnd2_edac_driver, 0, 0);

File Metadata

Mime Type
text/plain
Expires
Wed, Apr 29, 10:30 PM (2 h, 12 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
32405767
Default Alt Text
D39663.diff (20 KB)

Event Timeline