#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <bus/pci/pcivar.h>
#include <bus/pci/pcireg.h>
#include <bus/pci/pci_cfgreg.h>
#include <bus/pci/pcib_private.h>
#include "pcib_if.h"
struct ecc_amd8000_memctrl {
uint16_t vid;
uint16_t did;
const char *desc;
};
struct ecc_amd8000_softc {
device_t ecc_dev;
struct callout ecc_callout;
};
#define ecc_printf(sc, fmt, arg...) \
device_printf((sc)->ecc_dev, fmt , ##arg)
static void ecc_amd8000_callout(void *);
static void ecc_amd8000_stop(device_t);
static int ecc_amd8000_probe(device_t);
static int ecc_amd8000_attach(device_t);
static int ecc_amd8000_detach(device_t);
static void ecc_amd8000_shutdown(device_t);
static const struct ecc_amd8000_memctrl ecc_memctrls[] = {
{ 0x1022, 0x1100, "AMD 8000 memory controller" },
{ 0x1022, 0x7454, "AMD 8151 memory controller" },
{ 0, 0, NULL }
};
static device_method_t ecc_amd8000_methods[] = {
DEVMETHOD(device_probe, ecc_amd8000_probe),
DEVMETHOD(device_attach, ecc_amd8000_attach),
DEVMETHOD(device_detach, ecc_amd8000_detach),
DEVMETHOD(device_shutdown, ecc_amd8000_shutdown),
DEVMETHOD(device_suspend, bus_generic_suspend),
DEVMETHOD(device_resume, bus_generic_resume),
DEVMETHOD_END
};
static driver_t ecc_amd8000_driver = {
"ecc",
ecc_amd8000_methods,
sizeof(struct ecc_amd8000_softc)
};
static devclass_t ecc_devclass;
DRIVER_MODULE(ecc_amd8000, pci, ecc_amd8000_driver, ecc_devclass, NULL, NULL);
MODULE_DEPEND(ecc_amd8000, pci, 1, 1, 1);
MODULE_VERSION(ecc_amd8000, 1);
static int
ecc_amd8000_probe(device_t dev)
{
const struct ecc_amd8000_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) {
device_set_desc(dev, mc->desc);
return (0);
}
}
return (ENXIO);
}
static int
ecc_amd8000_attach(device_t dev)
{
struct ecc_amd8000_softc *sc = device_get_softc(dev);
uint32_t draminfo, eccinfo;
int bus, slot, poll = 0;
callout_init_mp(&sc->ecc_callout);
sc->ecc_dev = dev;
bus = pci_get_bus(dev);
slot = pci_get_slot(dev);
draminfo = pcib_read_config(dev, bus, slot, 2, 0x90, 4);
eccinfo = pcib_read_config(dev, bus, slot, 3, 0x44, 4);
if ((draminfo >> 17) & 1)
ecc_printf(sc, "memory type: ECC\n");
else
ecc_printf(sc, "memory type: NON-ECC\n");
switch((eccinfo >> 22) & 3) {
case 0:
ecc_printf(sc, "ecc mode: DISABLED\n");
break;
case 1:
ecc_printf(sc, "ecc mode: ENABLED/CORRECT-MODE\n");
poll = 1;
break;
case 2:
ecc_printf(sc, "ecc mode: ENABLED/RESERVED (disabled)\n");
break;
case 3:
ecc_printf(sc, "ecc mode: ENABLED/CHIPKILL-MODE\n");
poll = 1;
break;
}
if (poll) {
uint64_t v64;
uint32_t v32;
v64 = rdmsr(0x017B);
wrmsr(0x17B, (v64 & ~0xFFFFFFFFLL) | 0x00000010LL);
v32 = pcib_read_config(dev, bus, slot, 3, 0x4C, 4);
v32 &= 0x7F801EFC;
pcib_write_config(dev, bus, slot, 3, 0x4C, v32, 4);
callout_reset(&sc->ecc_callout, hz, ecc_amd8000_callout, sc);
}
return (0);
}
static void
ecc_amd8000_callout(void *xsc)
{
struct ecc_amd8000_softc *sc = xsc;
device_t dev = sc->ecc_dev;
uint32_t v32, addr;
int bus, slot;
bus = pci_get_bus(dev);
slot = pci_get_slot(dev);
v32 = pcib_read_config(dev, bus, slot, 3, 0x4C, 4);
if ((v32 & 0x80004000) == 0x80004000) {
addr = pcib_read_config(dev, bus, slot, 3, 0x50, 4);
ecc_printf(sc, "Correctable ECC error at %08x\n", addr);
pcib_write_config(dev, bus, slot, 3, 0x4C, v32 & 0x7F801EFC, 4);
} else if ((v32 & 0x80002000) == 0x80002000) {
addr = pcib_read_config(dev, bus ,slot, 3, 0x50, 4);
ecc_printf(sc, "Uncorrectable ECC error at %08x\n", addr);
pcib_write_config(dev, bus, slot, 3, 0x4C, v32 & 0x7F801EFC, 4);
}
callout_reset(&sc->ecc_callout, hz, ecc_amd8000_callout, sc);
}
static void
ecc_amd8000_stop(device_t dev)
{
struct ecc_amd8000_softc *sc = device_get_softc(dev);
callout_cancel(&sc->ecc_callout);
}
static int
ecc_amd8000_detach(device_t dev)
{
ecc_amd8000_stop(dev);
return 0;
}
static void
ecc_amd8000_shutdown(device_t dev)
{
ecc_amd8000_stop(dev);
}