Commit c2df436b authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'edac_for_3.17' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp

Pull EDAC changes from Borislav Petkov:
 "EDAC queue for 3.17:

   - One new edac driver for Intel E3-12xx DRAM controllers.

   - Out-of-subsystem changes are making the non-atomic iomem 64-bit
     accessors' naming explicit to show both exact order of the 32-bit
     accesses and the non-atomicity of the 64-bit access.

     Usage locations are more verbose now as to what access is exactly
     being done vs having a not-very telling "readq" there, for example.

     This is needed by E3-12xx hardware where certain mmapped registers
     cannot be accessed with requests crossing a dword boundary.

     From Jason Baron.

   - Extending AMD MCE signatures to a new model 60h in family 15h, from
     Aravind Gopalakrishnan.

   - An unsigned check cleanup, from Fabian Frederick"

* tag 'edac_for_3.17' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp:
  EDAC, MCE, AMD: Add MCE decoding for F15h M60h
  MAINTAINERS: add ie31200_edac entry
  ie31200_edac: Allocate mci and map mchbar first
  ie31200_edac: Introduce the driver
  x38_edac: make use of lo_hi_readq()
  readq/writeq: Add explicit lo_hi_[read|write]_q and hi_lo_[read|write]_q
  EDAC, edac_module.c: Remove unnecessary test on unsigned value
parents f74ad8df eba4bfb3
...@@ -3350,6 +3350,13 @@ W: bluesmoke.sourceforge.net ...@@ -3350,6 +3350,13 @@ W: bluesmoke.sourceforge.net
S: Maintained S: Maintained
F: drivers/edac/i82975x_edac.c F: drivers/edac/i82975x_edac.c
EDAC-IE31200
M: Jason Baron <jbaron@akamai.com>
L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/ie31200_edac.c
EDAC-MPC85XX EDAC-MPC85XX
M: Johannes Thumshirn <johannes.thumshirn@men.de> M: Johannes Thumshirn <johannes.thumshirn@men.de>
L: linux-edac@vger.kernel.org L: linux-edac@vger.kernel.org
......
...@@ -186,6 +186,13 @@ config EDAC_I3200 ...@@ -186,6 +186,13 @@ config EDAC_I3200
Support for error detection and correction on the Intel Support for error detection and correction on the Intel
3200 and 3210 server chipsets. 3200 and 3210 server chipsets.
config EDAC_IE31200
tristate "Intel e312xx"
depends on EDAC_MM_EDAC && PCI && X86
help
Support for error detection and correction on the Intel
E3-1200 based DRAM controllers.
config EDAC_X38 config EDAC_X38
tristate "Intel X38" tristate "Intel X38"
depends on EDAC_MM_EDAC && PCI && X86 depends on EDAC_MM_EDAC && PCI && X86
......
...@@ -37,6 +37,7 @@ obj-$(CONFIG_EDAC_I82875P) += i82875p_edac.o ...@@ -37,6 +37,7 @@ obj-$(CONFIG_EDAC_I82875P) += i82875p_edac.o
obj-$(CONFIG_EDAC_I82975X) += i82975x_edac.o obj-$(CONFIG_EDAC_I82975X) += i82975x_edac.o
obj-$(CONFIG_EDAC_I3000) += i3000_edac.o obj-$(CONFIG_EDAC_I3000) += i3000_edac.o
obj-$(CONFIG_EDAC_I3200) += i3200_edac.o obj-$(CONFIG_EDAC_I3200) += i3200_edac.o
obj-$(CONFIG_EDAC_IE31200) += ie31200_edac.o
obj-$(CONFIG_EDAC_X38) += x38_edac.o obj-$(CONFIG_EDAC_X38) += x38_edac.o
obj-$(CONFIG_EDAC_I82860) += i82860_edac.o obj-$(CONFIG_EDAC_I82860) += i82860_edac.o
obj-$(CONFIG_EDAC_R82600) += r82600_edac.o obj-$(CONFIG_EDAC_R82600) += r82600_edac.o
......
...@@ -28,7 +28,7 @@ static int edac_set_debug_level(const char *buf, struct kernel_param *kp) ...@@ -28,7 +28,7 @@ static int edac_set_debug_level(const char *buf, struct kernel_param *kp)
if (ret) if (ret)
return ret; return ret;
if (val < 0 || val > 4) if (val > 4)
return -EINVAL; return -EINVAL;
return param_set_int(buf, kp); return param_set_int(buf, kp);
......
/*
* Intel E3-1200
* Copyright (C) 2014 Jason Baron <jbaron@akamai.com>
*
* Support for the E3-1200 processor family. Heavily based on previous
* Intel EDAC drivers.
*
* Since the DRAM controller is on the cpu chip, we can use its PCI device
* id to identify these processors.
*
* PCI DRAM controller device ids (Taken from The PCI ID Repository - http://pci-ids.ucw.cz/)
*
* 0108: Xeon E3-1200 Processor Family DRAM Controller
* 010c: Xeon E3-1200/2nd Generation Core Processor Family DRAM Controller
* 0150: Xeon E3-1200 v2/3rd Gen Core processor DRAM Controller
* 0158: Xeon E3-1200 v2/Ivy Bridge DRAM Controller
* 015c: Xeon E3-1200 v2/3rd Gen Core processor DRAM Controller
* 0c04: Xeon E3-1200 v3/4th Gen Core Processor DRAM Controller
* 0c08: Xeon E3-1200 v3 Processor DRAM Controller
*
* Based on Intel specification:
* http://www.intel.com/content/dam/www/public/us/en/documents/datasheets/xeon-e3-1200v3-vol-2-datasheet.pdf
* http://www.intel.com/content/www/us/en/processors/xeon/xeon-e3-1200-family-vol-2-datasheet.html
*
* According to the above datasheet (p.16):
* "
* 6. Software must not access B0/D0/F0 32-bit memory-mapped registers with
* requests that cross a DW boundary.
* "
*
* Thus, we make use of the explicit: lo_hi_readq(), which breaks the readq into
* 2 readl() calls. This restriction may be lifted in subsequent chip releases,
* but lo_hi_readq() ensures that we are safe across all e3-1200 processors.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/edac.h>
#include <asm-generic/io-64-nonatomic-lo-hi.h>
#include "edac_core.h"
#define IE31200_REVISION "1.0"
#define EDAC_MOD_STR "ie31200_edac"
#define ie31200_printk(level, fmt, arg...) \
edac_printk(level, "ie31200", fmt, ##arg)
#define PCI_DEVICE_ID_INTEL_IE31200_HB_1 0x0108
#define PCI_DEVICE_ID_INTEL_IE31200_HB_2 0x010c
#define PCI_DEVICE_ID_INTEL_IE31200_HB_3 0x0150
#define PCI_DEVICE_ID_INTEL_IE31200_HB_4 0x0158
#define PCI_DEVICE_ID_INTEL_IE31200_HB_5 0x015c
#define PCI_DEVICE_ID_INTEL_IE31200_HB_6 0x0c04
#define PCI_DEVICE_ID_INTEL_IE31200_HB_7 0x0c08
#define IE31200_DIMMS 4
#define IE31200_RANKS 8
#define IE31200_RANKS_PER_CHANNEL 4
#define IE31200_DIMMS_PER_CHANNEL 2
#define IE31200_CHANNELS 2
/* Intel IE31200 register addresses - device 0 function 0 - DRAM Controller */
#define IE31200_MCHBAR_LOW 0x48
#define IE31200_MCHBAR_HIGH 0x4c
#define IE31200_MCHBAR_MASK GENMASK_ULL(38, 15)
#define IE31200_MMR_WINDOW_SIZE BIT(15)
/*
* Error Status Register (16b)
*
* 15 reserved
* 14 Isochronous TBWRR Run Behind FIFO Full
* (ITCV)
* 13 Isochronous TBWRR Run Behind FIFO Put
* (ITSTV)
* 12 reserved
* 11 MCH Thermal Sensor Event
* for SMI/SCI/SERR (GTSE)
* 10 reserved
* 9 LOCK to non-DRAM Memory Flag (LCKF)
* 8 reserved
* 7 DRAM Throttle Flag (DTF)
* 6:2 reserved
* 1 Multi-bit DRAM ECC Error Flag (DMERR)
* 0 Single-bit DRAM ECC Error Flag (DSERR)
*/
#define IE31200_ERRSTS 0xc8
#define IE31200_ERRSTS_UE BIT(1)
#define IE31200_ERRSTS_CE BIT(0)
#define IE31200_ERRSTS_BITS (IE31200_ERRSTS_UE | IE31200_ERRSTS_CE)
/*
* Channel 0 ECC Error Log (64b)
*
* 63:48 Error Column Address (ERRCOL)
* 47:32 Error Row Address (ERRROW)
* 31:29 Error Bank Address (ERRBANK)
* 28:27 Error Rank Address (ERRRANK)
* 26:24 reserved
* 23:16 Error Syndrome (ERRSYND)
* 15: 2 reserved
* 1 Multiple Bit Error Status (MERRSTS)
* 0 Correctable Error Status (CERRSTS)
*/
#define IE31200_C0ECCERRLOG 0x40c8
#define IE31200_C1ECCERRLOG 0x44c8
#define IE31200_ECCERRLOG_CE BIT(0)
#define IE31200_ECCERRLOG_UE BIT(1)
#define IE31200_ECCERRLOG_RANK_BITS GENMASK_ULL(28, 27)
#define IE31200_ECCERRLOG_RANK_SHIFT 27
#define IE31200_ECCERRLOG_SYNDROME_BITS GENMASK_ULL(23, 16)
#define IE31200_ECCERRLOG_SYNDROME_SHIFT 16
#define IE31200_ECCERRLOG_SYNDROME(log) \
((log & IE31200_ECCERRLOG_SYNDROME_BITS) >> \
IE31200_ECCERRLOG_SYNDROME_SHIFT)
#define IE31200_CAPID0 0xe4
#define IE31200_CAPID0_PDCD BIT(4)
#define IE31200_CAPID0_DDPCD BIT(6)
#define IE31200_CAPID0_ECC BIT(1)
#define IE31200_MAD_DIMM_0_OFFSET 0x5004
#define IE31200_MAD_DIMM_SIZE GENMASK_ULL(7, 0)
#define IE31200_MAD_DIMM_A_RANK BIT(17)
#define IE31200_MAD_DIMM_A_WIDTH BIT(19)
#define IE31200_PAGES(n) (n << (28 - PAGE_SHIFT))
static int nr_channels;
struct ie31200_priv {
void __iomem *window;
};
enum ie31200_chips {
IE31200 = 0,
};
struct ie31200_dev_info {
const char *ctl_name;
};
struct ie31200_error_info {
u16 errsts;
u16 errsts2;
u64 eccerrlog[IE31200_CHANNELS];
};
static const struct ie31200_dev_info ie31200_devs[] = {
[IE31200] = {
.ctl_name = "IE31200"
},
};
struct dimm_data {
u8 size; /* in 256MB multiples */
u8 dual_rank : 1,
x16_width : 1; /* 0 means x8 width */
};
static int how_many_channels(struct pci_dev *pdev)
{
int n_channels;
unsigned char capid0_2b; /* 2nd byte of CAPID0 */
pci_read_config_byte(pdev, IE31200_CAPID0 + 1, &capid0_2b);
/* check PDCD: Dual Channel Disable */
if (capid0_2b & IE31200_CAPID0_PDCD) {
edac_dbg(0, "In single channel mode\n");
n_channels = 1;
} else {
edac_dbg(0, "In dual channel mode\n");
n_channels = 2;
}
/* check DDPCD - check if both channels are filled */
if (capid0_2b & IE31200_CAPID0_DDPCD)
edac_dbg(0, "2 DIMMS per channel disabled\n");
else
edac_dbg(0, "2 DIMMS per channel enabled\n");
return n_channels;
}
static bool ecc_capable(struct pci_dev *pdev)
{
unsigned char capid0_4b; /* 4th byte of CAPID0 */
pci_read_config_byte(pdev, IE31200_CAPID0 + 3, &capid0_4b);
if (capid0_4b & IE31200_CAPID0_ECC)
return false;
return true;
}
static int eccerrlog_row(int channel, u64 log)
{
int rank = ((log & IE31200_ECCERRLOG_RANK_BITS) >>
IE31200_ECCERRLOG_RANK_SHIFT);
return rank | (channel * IE31200_RANKS_PER_CHANNEL);
}
static void ie31200_clear_error_info(struct mem_ctl_info *mci)
{
/*
* Clear any error bits.
* (Yes, we really clear bits by writing 1 to them.)
*/
pci_write_bits16(to_pci_dev(mci->pdev), IE31200_ERRSTS,
IE31200_ERRSTS_BITS, IE31200_ERRSTS_BITS);
}
static void ie31200_get_and_clear_error_info(struct mem_ctl_info *mci,
struct ie31200_error_info *info)
{
struct pci_dev *pdev;
struct ie31200_priv *priv = mci->pvt_info;
void __iomem *window = priv->window;
pdev = to_pci_dev(mci->pdev);
/*
* This is a mess because there is no atomic way to read all the
* registers at once and the registers can transition from CE being
* overwritten by UE.
*/
pci_read_config_word(pdev, IE31200_ERRSTS, &info->errsts);
if (!(info->errsts & IE31200_ERRSTS_BITS))
return;
info->eccerrlog[0] = lo_hi_readq(window + IE31200_C0ECCERRLOG);
if (nr_channels == 2)
info->eccerrlog[1] = lo_hi_readq(window + IE31200_C1ECCERRLOG);
pci_read_config_word(pdev, IE31200_ERRSTS, &info->errsts2);
/*
* If the error is the same for both reads then the first set
* of reads is valid. If there is a change then there is a CE
* with no info and the second set of reads is valid and
* should be UE info.
*/
if ((info->errsts ^ info->errsts2) & IE31200_ERRSTS_BITS) {
info->eccerrlog[0] = lo_hi_readq(window + IE31200_C0ECCERRLOG);
if (nr_channels == 2)
info->eccerrlog[1] =
lo_hi_readq(window + IE31200_C1ECCERRLOG);
}
ie31200_clear_error_info(mci);
}
static void ie31200_process_error_info(struct mem_ctl_info *mci,
struct ie31200_error_info *info)
{
int channel;
u64 log;
if (!(info->errsts & IE31200_ERRSTS_BITS))
return;
if ((info->errsts ^ info->errsts2) & IE31200_ERRSTS_BITS) {
edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0,
-1, -1, -1, "UE overwrote CE", "");
info->errsts = info->errsts2;
}
for (channel = 0; channel < nr_channels; channel++) {
log = info->eccerrlog[channel];
if (log & IE31200_ECCERRLOG_UE) {
edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
0, 0, 0,
eccerrlog_row(channel, log),
channel, -1,
"ie31200 UE", "");
} else if (log & IE31200_ECCERRLOG_CE) {
edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
0, 0,
IE31200_ECCERRLOG_SYNDROME(log),
eccerrlog_row(channel, log),
channel, -1,
"ie31200 CE", "");
}
}
}
static void ie31200_check(struct mem_ctl_info *mci)
{
struct ie31200_error_info info;
edac_dbg(1, "MC%d\n", mci->mc_idx);
ie31200_get_and_clear_error_info(mci, &info);
ie31200_process_error_info(mci, &info);
}
static void __iomem *ie31200_map_mchbar(struct pci_dev *pdev)
{
union {
u64 mchbar;
struct {
u32 mchbar_low;
u32 mchbar_high;
};
} u;
void __iomem *window;
pci_read_config_dword(pdev, IE31200_MCHBAR_LOW, &u.mchbar_low);
pci_read_config_dword(pdev, IE31200_MCHBAR_HIGH, &u.mchbar_high);
u.mchbar &= IE31200_MCHBAR_MASK;
if (u.mchbar != (resource_size_t)u.mchbar) {
ie31200_printk(KERN_ERR, "mmio space beyond accessible range (0x%llx)\n",
(unsigned long long)u.mchbar);
return NULL;
}
window = ioremap_nocache(u.mchbar, IE31200_MMR_WINDOW_SIZE);
if (!window)
ie31200_printk(KERN_ERR, "Cannot map mmio space at 0x%llx\n",
(unsigned long long)u.mchbar);
return window;
}
static int ie31200_probe1(struct pci_dev *pdev, int dev_idx)
{
int i, j, ret;
struct mem_ctl_info *mci = NULL;
struct edac_mc_layer layers[2];
struct dimm_data dimm_info[IE31200_CHANNELS][IE31200_DIMMS_PER_CHANNEL];
void __iomem *window;
struct ie31200_priv *priv;
u32 addr_decode;
edac_dbg(0, "MC:\n");
if (!ecc_capable(pdev)) {
ie31200_printk(KERN_INFO, "No ECC support\n");
return -ENODEV;
}
nr_channels = how_many_channels(pdev);
layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
layers[0].size = IE31200_DIMMS;
layers[0].is_virt_csrow = true;
layers[1].type = EDAC_MC_LAYER_CHANNEL;
layers[1].size = nr_channels;
layers[1].is_virt_csrow = false;
mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
sizeof(struct ie31200_priv));
if (!mci)
return -ENOMEM;
window = ie31200_map_mchbar(pdev);
if (!window) {
ret = -ENODEV;
goto fail_free;
}
edac_dbg(3, "MC: init mci\n");
mci->pdev = &pdev->dev;
mci->mtype_cap = MEM_FLAG_DDR3;
mci->edac_ctl_cap = EDAC_FLAG_SECDED;
mci->edac_cap = EDAC_FLAG_SECDED;
mci->mod_name = EDAC_MOD_STR;
mci->mod_ver = IE31200_REVISION;
mci->ctl_name = ie31200_devs[dev_idx].ctl_name;
mci->dev_name = pci_name(pdev);
mci->edac_check = ie31200_check;
mci->ctl_page_to_phys = NULL;
priv = mci->pvt_info;
priv->window = window;
/* populate DIMM info */
for (i = 0; i < IE31200_CHANNELS; i++) {
addr_decode = readl(window + IE31200_MAD_DIMM_0_OFFSET +
(i * 4));
edac_dbg(0, "addr_decode: 0x%x\n", addr_decode);
for (j = 0; j < IE31200_DIMMS_PER_CHANNEL; j++) {
dimm_info[i][j].size = (addr_decode >> (j * 8)) &
IE31200_MAD_DIMM_SIZE;
dimm_info[i][j].dual_rank = (addr_decode &
(IE31200_MAD_DIMM_A_RANK << j)) ? 1 : 0;
dimm_info[i][j].x16_width = (addr_decode &
(IE31200_MAD_DIMM_A_WIDTH << j)) ? 1 : 0;
edac_dbg(0, "size: 0x%x, rank: %d, width: %d\n",
dimm_info[i][j].size,
dimm_info[i][j].dual_rank,
dimm_info[i][j].x16_width);
}
}
/*
* The dram rank boundary (DRB) reg values are boundary addresses
* for each DRAM rank with a granularity of 64MB. DRB regs are
* cumulative; the last one will contain the total memory
* contained in all ranks.
*/
for (i = 0; i < IE31200_DIMMS_PER_CHANNEL; i++) {
for (j = 0; j < IE31200_CHANNELS; j++) {
struct dimm_info *dimm;
unsigned long nr_pages;
nr_pages = IE31200_PAGES(dimm_info[j][i].size);
if (nr_pages == 0)
continue;
if (dimm_info[j][i].dual_rank) {
nr_pages = nr_pages / 2;
dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms,
mci->n_layers, (i * 2) + 1,
j, 0);
dimm->nr_pages = nr_pages;
edac_dbg(0, "set nr pages: 0x%lx\n", nr_pages);
dimm->grain = 8; /* just a guess */
dimm->mtype = MEM_DDR3;
dimm->dtype = DEV_UNKNOWN;
dimm->edac_mode = EDAC_UNKNOWN;
}
dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms,
mci->n_layers, i * 2, j, 0);
dimm->nr_pages = nr_pages;
edac_dbg(0, "set nr pages: 0x%lx\n", nr_pages);
dimm->grain = 8; /* same guess */
dimm->mtype = MEM_DDR3;
dimm->dtype = DEV_UNKNOWN;
dimm->edac_mode = EDAC_UNKNOWN;
}
}
ie31200_clear_error_info(mci);
if (edac_mc_add_mc(mci)) {
edac_dbg(3, "MC: failed edac_mc_add_mc()\n");
ret = -ENODEV;
goto fail_unmap;
}
/* get this far and it's successful */
edac_dbg(3, "MC: success\n");
return 0;
fail_unmap:
iounmap(window);
fail_free:
edac_mc_free(mci);
return ret;
}
static int ie31200_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
edac_dbg(0, "MC:\n");
if (pci_enable_device(pdev) < 0)
return -EIO;
return ie31200_probe1(pdev, ent->driver_data);
}
static void ie31200_remove_one(struct pci_dev *pdev)
{
struct mem_ctl_info *mci;
struct ie31200_priv *priv;
edac_dbg(0, "\n");
mci = edac_mc_del_mc(&pdev->dev);
if (!mci)
return;
priv = mci->pvt_info;
iounmap(priv->window);
edac_mc_free(mci);
}
static const struct pci_device_id ie31200_pci_tbl[] = {
{
PCI_VEND_DEV(INTEL, IE31200_HB_1), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
IE31200},
{
PCI_VEND_DEV(INTEL, IE31200_HB_2), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
IE31200},
{
PCI_VEND_DEV(INTEL, IE31200_HB_3), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
IE31200},
{
PCI_VEND_DEV(INTEL, IE31200_HB_4), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
IE31200},
{
PCI_VEND_DEV(INTEL, IE31200_HB_5), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
IE31200},
{
PCI_VEND_DEV(INTEL, IE31200_HB_6), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
IE31200},
{
PCI_VEND_DEV(INTEL, IE31200_HB_7), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
IE31200},
{
0,
} /* 0 terminated list. */
};
MODULE_DEVICE_TABLE(pci, ie31200_pci_tbl);
static struct pci_driver ie31200_driver = {
.name = EDAC_MOD_STR,
.probe = ie31200_init_one,
.remove = ie31200_remove_one,
.id_table = ie31200_pci_tbl,
};
static int __init ie31200_init(void)
{
edac_dbg(3, "MC:\n");
/* Ensure that the OPSTATE is set correctly for POLL or NMI */
opstate_init();
return pci_register_driver(&ie31200_driver);
}
static void __exit ie31200_exit(void)
{
edac_dbg(3, "MC:\n");
pci_unregister_driver(&ie31200_driver);
}
module_init(ie31200_init);
module_exit(ie31200_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jason Baron <jbaron@akamai.com>");
MODULE_DESCRIPTION("MC support for Intel Processor E31200 memory hub controllers");
...@@ -78,7 +78,8 @@ static const char * const f15h_mc1_mce_desc[] = { ...@@ -78,7 +78,8 @@ static const char * const f15h_mc1_mce_desc[] = {
"uop queue", "uop queue",
"insn buffer", "insn buffer",
"predecode buffer", "predecode buffer",
"fetch address FIFO" "fetch address FIFO",
"dispatch uop queue"
}; };
static const char * const f15h_mc2_mce_desc[] = { static const char * const f15h_mc2_mce_desc[] = {
...@@ -267,6 +268,12 @@ static bool f15h_mc0_mce(u16 ec, u8 xec) ...@@ -267,6 +268,12 @@ static bool f15h_mc0_mce(u16 ec, u8 xec)
pr_cont("System Read Data Error.\n"); pr_cont("System Read Data Error.\n");
else else
pr_cont(" Internal error condition type %d.\n", xec); pr_cont(" Internal error condition type %d.\n", xec);
} else if (INT_ERROR(ec)) {
if (xec <= 0x1f)
pr_cont("Hardware Assert.\n");
else
ret = false;
} else } else
ret = false; ret = false;
...@@ -373,7 +380,7 @@ static bool f15h_mc1_mce(u16 ec, u8 xec) ...@@ -373,7 +380,7 @@ static bool f15h_mc1_mce(u16 ec, u8 xec)
pr_cont("%s.\n", f15h_mc1_mce_desc[xec-4]); pr_cont("%s.\n", f15h_mc1_mce_desc[xec-4]);
break; break;
case 0x11 ... 0x14: case 0x11 ... 0x15:
pr_cont("Decoder %s parity error.\n", f15h_mc1_mce_desc[xec-4]); pr_cont("Decoder %s parity error.\n", f15h_mc1_mce_desc[xec-4]);
break; break;
...@@ -397,10 +404,20 @@ static void decode_mc1_mce(struct mce *m) ...@@ -397,10 +404,20 @@ static void decode_mc1_mce(struct mce *m)
bool k8 = (boot_cpu_data.x86 == 0xf && (m->status & BIT_64(58))); bool k8 = (boot_cpu_data.x86 == 0xf && (m->status & BIT_64(58)));
pr_cont("during %s.\n", (k8 ? "system linefill" : "NB data read")); pr_cont("during %s.\n", (k8 ? "system linefill" : "NB data read"));
} else if (INT_ERROR(ec)) {
if (xec <= 0x3f)
pr_cont("Hardware Assert.\n");
else
goto wrong_mc1_mce;
} else if (fam_ops->mc1_mce(ec, xec)) } else if (fam_ops->mc1_mce(ec, xec))
; ;
else else
pr_emerg(HW_ERR "Corrupted MC1 MCE info?\n"); goto wrong_mc1_mce;
return;
wrong_mc1_mce:
pr_emerg(HW_ERR "Corrupted MC1 MCE info?\n");
} }
static bool k8_mc2_mce(u16 ec, u8 xec) static bool k8_mc2_mce(u16 ec, u8 xec)
...@@ -468,6 +485,11 @@ static bool f15h_mc2_mce(u16 ec, u8 xec) ...@@ -468,6 +485,11 @@ static bool f15h_mc2_mce(u16 ec, u8 xec)
default: default:
ret = false; ret = false;
} }
} else if (INT_ERROR(ec)) {
if (xec <= 0x3f)
pr_cont("Hardware Assert.\n");
else
ret = false;
} }
return ret; return ret;
...@@ -615,6 +637,7 @@ static void decode_mc4_mce(struct mce *m) ...@@ -615,6 +637,7 @@ static void decode_mc4_mce(struct mce *m)
static void decode_mc5_mce(struct mce *m) static void decode_mc5_mce(struct mce *m)
{ {
struct cpuinfo_x86 *c = &boot_cpu_data; struct cpuinfo_x86 *c = &boot_cpu_data;
u16 ec = EC(m->status);
u8 xec = XEC(m->status, xec_mask); u8 xec = XEC(m->status, xec_mask);
if (c->x86 == 0xf || c->x86 == 0x11) if (c->x86 == 0xf || c->x86 == 0x11)
...@@ -622,6 +645,14 @@ static void decode_mc5_mce(struct mce *m) ...@@ -622,6 +645,14 @@ static void decode_mc5_mce(struct mce *m)
pr_emerg(HW_ERR "MC5 Error: "); pr_emerg(HW_ERR "MC5 Error: ");
if (INT_ERROR(ec)) {
if (xec <= 0x1f) {
pr_cont("Hardware Assert.\n");
return;
} else
goto wrong_mc5_mce;
}
if (xec == 0x0 || xec == 0xc) if (xec == 0x0 || xec == 0xc)
pr_cont("%s.\n", mc5_mce_desc[xec]); pr_cont("%s.\n", mc5_mce_desc[xec]);
else if (xec <= 0xd) else if (xec <= 0xd)
...@@ -642,6 +673,10 @@ static void decode_mc6_mce(struct mce *m) ...@@ -642,6 +673,10 @@ static void decode_mc6_mce(struct mce *m)
pr_emerg(HW_ERR "MC6 Error: "); pr_emerg(HW_ERR "MC6 Error: ");
switch (xec) { switch (xec) {
case 0x0:
pr_cont("Hardware Assertion");
break;
case 0x1: case 0x1:
pr_cont("Free List"); pr_cont("Free List");
break; break;
...@@ -857,7 +892,8 @@ static int __init mce_amd_init(void) ...@@ -857,7 +892,8 @@ static int __init mce_amd_init(void)
break; break;
case 0x15: case 0x15:
xec_mask = 0x1f; xec_mask = c->x86_model == 0x60 ? 0x3f : 0x1f;
fam_ops->mc0_mce = f15h_mc0_mce; fam_ops->mc0_mce = f15h_mc0_mce;
fam_ops->mc1_mce = f15h_mc1_mce; fam_ops->mc1_mce = f15h_mc1_mce;
fam_ops->mc2_mce = f15h_mc2_mce; fam_ops->mc2_mce = f15h_mc2_mce;
......
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/pci_ids.h> #include <linux/pci_ids.h>
#include <linux/edac.h> #include <linux/edac.h>
#include <asm-generic/io-64-nonatomic-lo-hi.h>
#include "edac_core.h" #include "edac_core.h"
#define X38_REVISION "1.1" #define X38_REVISION "1.1"
...@@ -161,11 +163,6 @@ static void x38_clear_error_info(struct mem_ctl_info *mci) ...@@ -161,11 +163,6 @@ static void x38_clear_error_info(struct mem_ctl_info *mci)
X38_ERRSTS_BITS); X38_ERRSTS_BITS);
} }
static u64 x38_readq(const void __iomem *addr)
{
return readl(addr) | (((u64)readl(addr + 4)) << 32);
}
static void x38_get_and_clear_error_info(struct mem_ctl_info *mci, static void x38_get_and_clear_error_info(struct mem_ctl_info *mci,
struct x38_error_info *info) struct x38_error_info *info)
{ {
...@@ -183,9 +180,9 @@ static void x38_get_and_clear_error_info(struct mem_ctl_info *mci, ...@@ -183,9 +180,9 @@ static void x38_get_and_clear_error_info(struct mem_ctl_info *mci,
if (!(info->errsts & X38_ERRSTS_BITS)) if (!(info->errsts & X38_ERRSTS_BITS))
return; return;
info->eccerrlog[0] = x38_readq(window + X38_C0ECCERRLOG); info->eccerrlog[0] = lo_hi_readq(window + X38_C0ECCERRLOG);
if (x38_channel_num == 2) if (x38_channel_num == 2)
info->eccerrlog[1] = x38_readq(window + X38_C1ECCERRLOG); info->eccerrlog[1] = lo_hi_readq(window + X38_C1ECCERRLOG);
pci_read_config_word(pdev, X38_ERRSTS, &info->errsts2); pci_read_config_word(pdev, X38_ERRSTS, &info->errsts2);
...@@ -196,10 +193,10 @@ static void x38_get_and_clear_error_info(struct mem_ctl_info *mci, ...@@ -196,10 +193,10 @@ static void x38_get_and_clear_error_info(struct mem_ctl_info *mci,
* should be UE info. * should be UE info.
*/ */
if ((info->errsts ^ info->errsts2) & X38_ERRSTS_BITS) { if ((info->errsts ^ info->errsts2) & X38_ERRSTS_BITS) {
info->eccerrlog[0] = x38_readq(window + X38_C0ECCERRLOG); info->eccerrlog[0] = lo_hi_readq(window + X38_C0ECCERRLOG);
if (x38_channel_num == 2) if (x38_channel_num == 2)
info->eccerrlog[1] = info->eccerrlog[1] =
x38_readq(window + X38_C1ECCERRLOG); lo_hi_readq(window + X38_C1ECCERRLOG);
} }
x38_clear_error_info(mci); x38_clear_error_info(mci);
......
...@@ -4,8 +4,7 @@ ...@@ -4,8 +4,7 @@
#include <linux/io.h> #include <linux/io.h>
#include <asm-generic/int-ll64.h> #include <asm-generic/int-ll64.h>
#ifndef readq static inline __u64 hi_lo_readq(const volatile void __iomem *addr)
static inline __u64 readq(const volatile void __iomem *addr)
{ {
const volatile u32 __iomem *p = addr; const volatile u32 __iomem *p = addr;
u32 low, high; u32 low, high;
...@@ -15,14 +14,19 @@ static inline __u64 readq(const volatile void __iomem *addr) ...@@ -15,14 +14,19 @@ static inline __u64 readq(const volatile void __iomem *addr)
return low + ((u64)high << 32); return low + ((u64)high << 32);
} }
#endif
#ifndef writeq static inline void hi_lo_writeq(__u64 val, volatile void __iomem *addr)
static inline void writeq(__u64 val, volatile void __iomem *addr)
{ {
writel(val >> 32, addr + 4); writel(val >> 32, addr + 4);
writel(val, addr); writel(val, addr);
} }
#ifndef readq
#define readq hi_lo_readq
#endif
#ifndef writeq
#define writeq hi_lo_writeq
#endif #endif
#endif /* _ASM_IO_64_NONATOMIC_HI_LO_H_ */ #endif /* _ASM_IO_64_NONATOMIC_HI_LO_H_ */
...@@ -4,8 +4,7 @@ ...@@ -4,8 +4,7 @@
#include <linux/io.h> #include <linux/io.h>
#include <asm-generic/int-ll64.h> #include <asm-generic/int-ll64.h>
#ifndef readq static inline __u64 lo_hi_readq(const volatile void __iomem *addr)
static inline __u64 readq(const volatile void __iomem *addr)
{ {
const volatile u32 __iomem *p = addr; const volatile u32 __iomem *p = addr;
u32 low, high; u32 low, high;
...@@ -15,14 +14,19 @@ static inline __u64 readq(const volatile void __iomem *addr) ...@@ -15,14 +14,19 @@ static inline __u64 readq(const volatile void __iomem *addr)
return low + ((u64)high << 32); return low + ((u64)high << 32);
} }
#endif
#ifndef writeq static inline void lo_hi_writeq(__u64 val, volatile void __iomem *addr)
static inline void writeq(__u64 val, volatile void __iomem *addr)
{ {
writel(val, addr); writel(val, addr);
writel(val >> 32, addr + 4); writel(val >> 32, addr + 4);
} }
#ifndef readq
#define readq lo_hi_readq
#endif
#ifndef writeq
#define writeq lo_hi_writeq
#endif #endif
#endif /* _ASM_IO_64_NONATOMIC_LO_HI_H_ */ #endif /* _ASM_IO_64_NONATOMIC_LO_HI_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment