Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F154567034
D39663.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
20 KB
Referenced Files
None
Subscribers
None
D39663.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D39663: Pondicherry2 memory controller (ECC) driver
Attached
Detach File
Event Timeline
Log In to Comment