diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-16 03:56:45 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-16 03:56:45 +0400 |
commit | 68ffeca4f40dcdfc04e7134b4667e268af6c0683 (patch) | |
tree | e7e9cebac321cbb7074ce665a2de0aa0cd724c0a | |
parent | c9d26423e56ce1ab4d786f92aebecf859d419293 (diff) | |
parent | 50d1bb93672fa2f42cec6e06ce799fbe864f57e9 (diff) | |
download | linux-68ffeca4f40dcdfc04e7134b4667e268af6c0683.tar.xz |
Merge branch 'linux_next' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-edac
Pull EDAC updates from Mauro Carvalho Chehab.
* 'linux_next' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-edac:
sb_edac: add support for Haswell based systems
sb_edac: Fix mix tab/spaces alignments
edac: add DDR4 and RDDR4
sb_edac: remove bogus assumption on mc ordering
sb_edac: make minimal use of channel_mask
sb_edac: fix socket detection on Ivy Bridge controllers
sb_edac: update Kconfig description
sb_edac: search devices using product id
sb_edac: make RIR limit retrieval per model
sb_edac: make node id retrieval per model
sb_edac: make memory type detection per memory controller
-rw-r--r-- | drivers/edac/Kconfig | 4 | ||||
-rw-r--r-- | drivers/edac/edac_mc_sysfs.c | 4 | ||||
-rw-r--r-- | drivers/edac/sb_edac.c | 810 | ||||
-rw-r--r-- | include/linux/edac.h | 5 |
4 files changed, 551 insertions, 272 deletions
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index f8665f9c3e03..fd89ca982748 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -253,12 +253,12 @@ config EDAC_I7300 Clarksboro MCH (Intel 7300 chipset). config EDAC_SBRIDGE - tristate "Intel Sandy-Bridge Integrated MC" + tristate "Intel Sandy-Bridge/Ivy-Bridge/Haswell Integrated MC" depends on EDAC_MM_EDAC && PCI && X86_64 && X86_MCE_INTEL depends on PCI_MMCONFIG help Support for error detection and correction the Intel - Sandy Bridge Integrated Memory Controller. + Sandy Bridge, Ivy Bridge and Haswell Integrated Memory Controllers. config EDAC_MPC85XX tristate "Freescale MPC83xx / MPC85xx" diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 01fae8289cf0..a6cd36100663 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -108,7 +108,9 @@ static const char * const mem_types[] = { [MEM_RDDR2] = "Registered-DDR2", [MEM_XDR] = "XDR", [MEM_DDR3] = "Unbuffered-DDR3", - [MEM_RDDR3] = "Registered-DDR3" + [MEM_RDDR3] = "Registered-DDR3", + [MEM_DDR4] = "Unbuffered-DDR4", + [MEM_RDDR4] = "Registered-DDR4" }; static const char * const dev_types[] = { diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c index deea0dc9999b..0034c4844428 100644 --- a/drivers/edac/sb_edac.c +++ b/drivers/edac/sb_edac.c @@ -99,6 +99,7 @@ static const u32 ibridge_dram_rule[] = { #define DRAM_ATTR(reg) GET_BITFIELD(reg, 2, 3) #define INTERLEAVE_MODE(reg) GET_BITFIELD(reg, 1, 1) #define DRAM_RULE_ENABLE(reg) GET_BITFIELD(reg, 0, 0) +#define A7MODE(reg) GET_BITFIELD(reg, 26, 26) static char *get_dram_attr(u32 reg) { @@ -164,6 +165,8 @@ static inline int sad_pkg(const struct interleave_pkg *table, u32 reg, #define TOLM 0x80 #define TOHM 0x84 +#define HASWELL_TOHM_0 0xd4 +#define HASWELL_TOHM_1 0xd8 #define GET_TOLM(reg) ((GET_BITFIELD(reg, 0, 3) << 28) | 0x3ffffff) #define GET_TOHM(reg) ((GET_BITFIELD(reg, 0, 20) << 25) | 0x3ffffff) @@ -176,8 +179,6 @@ static inline int sad_pkg(const struct interleave_pkg *table, u32 reg, #define SAD_CONTROL 0xf4 -#define NODE_ID(reg) GET_BITFIELD(reg, 0, 2) - /* Device 14 function 0 */ static const u32 tad_dram_rule[] = { @@ -235,7 +236,6 @@ static const u32 rir_way_limit[] = { #define IS_RIR_VALID(reg) GET_BITFIELD(reg, 31, 31) #define RIR_WAY(reg) GET_BITFIELD(reg, 28, 29) -#define RIR_LIMIT(reg) ((GET_BITFIELD(reg, 1, 10) << 29)| 0x1fffffff) #define MAX_RIR_WAY 8 @@ -279,8 +279,6 @@ static const u32 correrrthrsld[] = { #define IB_RANK_CFG_A 0x0320 -#define IS_RDIMM_ENABLED(reg) GET_BITFIELD(reg, 11, 11) - /* * sbridge structs */ @@ -291,6 +289,7 @@ static const u32 correrrthrsld[] = { enum type { SANDY_BRIDGE, IVY_BRIDGE, + HASWELL, }; struct sbridge_pvt; @@ -300,11 +299,15 @@ struct sbridge_info { u32 rankcfgr; u64 (*get_tolm)(struct sbridge_pvt *pvt); u64 (*get_tohm)(struct sbridge_pvt *pvt); + u64 (*rir_limit)(u32 reg); const u32 *dram_rule; const u32 *interleave_list; const struct interleave_pkg *interleave_pkg; u8 max_sad; u8 max_interleave; + u8 (*get_node_id)(struct sbridge_pvt *pvt); + enum mem_type (*get_memory_type)(struct sbridge_pvt *pvt); + struct pci_dev *pci_vtd; }; struct sbridge_channel { @@ -313,9 +316,7 @@ struct sbridge_channel { }; struct pci_id_descr { - int dev; - int func; - int dev_id; + int dev_id; int optional; }; @@ -338,6 +339,7 @@ struct sbridge_pvt { struct pci_dev *pci_sad0, *pci_sad1; struct pci_dev *pci_ha0, *pci_ha1; struct pci_dev *pci_br0, *pci_br1; + struct pci_dev *pci_ha1_ta; struct pci_dev *pci_tad[NUM_CHANNELS]; struct sbridge_dev *sbridge_dev; @@ -362,31 +364,29 @@ struct sbridge_pvt { u64 tolm, tohm; }; -#define PCI_DESCR(device, function, device_id, opt) \ - .dev = (device), \ - .func = (function), \ - .dev_id = (device_id), \ +#define PCI_DESCR(device_id, opt) \ + .dev_id = (device_id), \ .optional = opt static const struct pci_id_descr pci_dev_descr_sbridge[] = { /* Processor Home Agent */ - { PCI_DESCR(14, 0, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_HA0, 0) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_HA0, 0) }, /* Memory controller */ - { PCI_DESCR(15, 0, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TA, 0) }, - { PCI_DESCR(15, 1, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_RAS, 0) }, - { PCI_DESCR(15, 2, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD0, 0) }, - { PCI_DESCR(15, 3, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD1, 0) }, - { PCI_DESCR(15, 4, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD2, 0) }, - { PCI_DESCR(15, 5, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD3, 0) }, - { PCI_DESCR(17, 0, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_DDRIO, 1) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TA, 0) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_RAS, 0) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD0, 0) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD1, 0) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD2, 0) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD3, 0) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_DDRIO, 1) }, /* System Address Decoder */ - { PCI_DESCR(12, 6, PCI_DEVICE_ID_INTEL_SBRIDGE_SAD0, 0) }, - { PCI_DESCR(12, 7, PCI_DEVICE_ID_INTEL_SBRIDGE_SAD1, 0) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_SBRIDGE_SAD0, 0) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_SBRIDGE_SAD1, 0) }, /* Broadcast Registers */ - { PCI_DESCR(13, 6, PCI_DEVICE_ID_INTEL_SBRIDGE_BR, 0) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_SBRIDGE_BR, 0) }, }; #define PCI_ID_TABLE_ENTRY(A) { .descr=A, .n_devs = ARRAY_SIZE(A) } @@ -423,34 +423,34 @@ static const struct pci_id_table pci_dev_descr_sbridge_table[] = { static const struct pci_id_descr pci_dev_descr_ibridge[] = { /* Processor Home Agent */ - { PCI_DESCR(14, 0, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0, 0) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0, 0) }, /* Memory controller */ - { PCI_DESCR(15, 0, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA, 0) }, - { PCI_DESCR(15, 1, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_RAS, 0) }, - { PCI_DESCR(15, 2, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD0, 0) }, - { PCI_DESCR(15, 3, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD1, 0) }, - { PCI_DESCR(15, 4, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD2, 0) }, - { PCI_DESCR(15, 5, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD3, 0) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA, 0) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_RAS, 0) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD0, 0) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD1, 0) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD2, 0) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD3, 0) }, /* System Address Decoder */ - { PCI_DESCR(22, 0, PCI_DEVICE_ID_INTEL_IBRIDGE_SAD, 0) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_SAD, 0) }, /* Broadcast Registers */ - { PCI_DESCR(22, 1, PCI_DEVICE_ID_INTEL_IBRIDGE_BR0, 1) }, - { PCI_DESCR(22, 2, PCI_DEVICE_ID_INTEL_IBRIDGE_BR1, 0) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_BR0, 1) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_BR1, 0) }, /* Optional, mode 2HA */ - { PCI_DESCR(28, 0, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1, 1) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1, 1) }, #if 0 - { PCI_DESCR(29, 0, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TA, 1) }, - { PCI_DESCR(29, 1, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_RAS, 1) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TA, 1) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_RAS, 1) }, #endif - { PCI_DESCR(29, 2, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD0, 1) }, - { PCI_DESCR(29, 3, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD1, 1) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD0, 1) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD1, 1) }, - { PCI_DESCR(17, 0, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_1HA_DDRIO0, 1) }, - { PCI_DESCR(17, 4, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_2HA_DDRIO0, 1) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_1HA_DDRIO0, 1) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_2HA_DDRIO0, 1) }, }; static const struct pci_id_table pci_dev_descr_ibridge_table[] = { @@ -458,12 +458,80 @@ static const struct pci_id_table pci_dev_descr_ibridge_table[] = { {0,} /* 0 terminated list. */ }; +/* Haswell support */ +/* EN processor: + * - 1 IMC + * - 3 DDR3 channels, 2 DPC per channel + * EP processor: + * - 1 or 2 IMC + * - 4 DDR4 channels, 3 DPC per channel + * EP 4S processor: + * - 2 IMC + * - 4 DDR4 channels, 3 DPC per channel + * EX processor: + * - 2 IMC + * - each IMC interfaces with a SMI 2 channel + * - each SMI channel interfaces with a scalable memory buffer + * - each scalable memory buffer supports 4 DDR3/DDR4 channels, 3 DPC + */ +#define HASWELL_DDRCRCLKCONTROLS 0xa10 +#define HASWELL_HASYSDEFEATURE2 0x84 +#define PCI_DEVICE_ID_INTEL_HASWELL_IMC_VTD_MISC 0x2f28 +#define PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0 0x2fa0 +#define PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1 0x2f60 +#define PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_TA 0x2fa8 +#define PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_THERMAL 0x2f71 +#define PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TA 0x2f68 +#define PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_THERMAL 0x2f79 +#define PCI_DEVICE_ID_INTEL_HASWELL_IMC_CBO_SAD0 0x2ffc +#define PCI_DEVICE_ID_INTEL_HASWELL_IMC_CBO_SAD1 0x2ffd +#define PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_TAD0 0x2faa +#define PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_TAD1 0x2fab +#define PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_TAD2 0x2fac +#define PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_TAD3 0x2fad +#define PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TAD0 0x2f6a +#define PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TAD1 0x2f6b +#define PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TAD2 0x2f6c +#define PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TAD3 0x2f6d +#define PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO0 0x2fbd +static const struct pci_id_descr pci_dev_descr_haswell[] = { + /* first item must be the HA */ + { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0, 0) }, + + { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_CBO_SAD0, 0) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_CBO_SAD1, 0) }, + + { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1, 1) }, + + { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_TA, 0) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_THERMAL, 0) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_TAD0, 0) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_TAD1, 0) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_TAD2, 1) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_TAD3, 1) }, + + { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO0, 1) }, + + { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TA, 1) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_THERMAL, 1) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TAD0, 1) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TAD1, 1) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TAD2, 1) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TAD3, 1) }, +}; + +static const struct pci_id_table pci_dev_descr_haswell_table[] = { + PCI_ID_TABLE_ENTRY(pci_dev_descr_haswell), + {0,} /* 0 terminated list. */ +}; + /* * pci_device_id table for which devices we are looking for */ static const struct pci_device_id sbridge_pci_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TA)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0)}, {0,} /* 0 terminated list. */ }; @@ -472,13 +540,17 @@ static const struct pci_device_id sbridge_pci_tbl[] = { Ancillary status routines ****************************************************************************/ -static inline int numrank(u32 mtr) +static inline int numrank(enum type type, u32 mtr) { int ranks = (1 << RANK_CNT_BITS(mtr)); + int max = 4; - if (ranks > 4) { - edac_dbg(0, "Invalid number of ranks: %d (max = 4) raw value = %x (%04x)\n", - ranks, (unsigned int)RANK_CNT_BITS(mtr), mtr); + if (type == HASWELL) + max = 8; + + if (ranks > max) { + edac_dbg(0, "Invalid number of ranks: %d (max = %i) raw value = %x (%04x)\n", + ranks, max, (unsigned int)RANK_CNT_BITS(mtr), mtr); return -EINVAL; } @@ -588,10 +660,107 @@ static u64 ibridge_get_tohm(struct sbridge_pvt *pvt) return GET_TOHM(reg); } +static u64 rir_limit(u32 reg) +{ + return ((u64)GET_BITFIELD(reg, 1, 10) << 29) | 0x1fffffff; +} + +static enum mem_type get_memory_type(struct sbridge_pvt *pvt) +{ + u32 reg; + enum mem_type mtype; + + if (pvt->pci_ddrio) { + pci_read_config_dword(pvt->pci_ddrio, pvt->info.rankcfgr, + ®); + if (GET_BITFIELD(reg, 11, 11)) + /* FIXME: Can also be LRDIMM */ + mtype = MEM_RDDR3; + else + mtype = MEM_DDR3; + } else + mtype = MEM_UNKNOWN; + + return mtype; +} + +static enum mem_type haswell_get_memory_type(struct sbridge_pvt *pvt) +{ + u32 reg; + bool registered = false; + enum mem_type mtype = MEM_UNKNOWN; + + if (!pvt->pci_ddrio) + goto out; + + pci_read_config_dword(pvt->pci_ddrio, + HASWELL_DDRCRCLKCONTROLS, ®); + /* Is_Rdimm */ + if (GET_BITFIELD(reg, 16, 16)) + registered = true; + + pci_read_config_dword(pvt->pci_ta, MCMTR, ®); + if (GET_BITFIELD(reg, 14, 14)) { + if (registered) + mtype = MEM_RDDR4; + else + mtype = MEM_DDR4; + } else { + if (registered) + mtype = MEM_RDDR3; + else + mtype = MEM_DDR3; + } + +out: + return mtype; +} + +static u8 get_node_id(struct sbridge_pvt *pvt) +{ + u32 reg; + pci_read_config_dword(pvt->pci_br0, SAD_CONTROL, ®); + return GET_BITFIELD(reg, 0, 2); +} + +static u8 haswell_get_node_id(struct sbridge_pvt *pvt) +{ + u32 reg; + + pci_read_config_dword(pvt->pci_sad1, SAD_CONTROL, ®); + return GET_BITFIELD(reg, 0, 3); +} + +static u64 haswell_get_tolm(struct sbridge_pvt *pvt) +{ + u32 reg; + + pci_read_config_dword(pvt->info.pci_vtd, TOLM, ®); + return (GET_BITFIELD(reg, 26, 31) << 26) | 0x1ffffff; +} + +static u64 haswell_get_tohm(struct sbridge_pvt *pvt) +{ + u64 rc; + u32 reg; + + pci_read_config_dword(pvt->info.pci_vtd, HASWELL_TOHM_0, ®); + rc = GET_BITFIELD(reg, 26, 31); + pci_read_config_dword(pvt->info.pci_vtd, HASWELL_TOHM_1, ®); + rc = ((reg << 6) | rc) << 26; + + return rc | 0x1ffffff; +} + +static u64 haswell_rir_limit(u32 reg) +{ + return (((u64)GET_BITFIELD(reg, 1, 11) + 1) << 29) - 1; +} + static inline u8 sad_pkg_socket(u8 pkg) { /* on Ivy Bridge, nodeID is SASS, where A is HA and S is node id */ - return (pkg >> 3) | (pkg & 0x3); + return ((pkg >> 3) << 2) | (pkg & 0x3); } static inline u8 sad_pkg_ha(u8 pkg) @@ -602,44 +771,43 @@ static inline u8 sad_pkg_ha(u8 pkg) /**************************************************************************** Memory check routines ****************************************************************************/ -static struct pci_dev *get_pdev_slot_func(u8 bus, unsigned slot, - unsigned func) +static struct pci_dev *get_pdev_same_bus(u8 bus, u32 id) { - struct sbridge_dev *sbridge_dev = get_sbridge_dev(bus); - int i; - - if (!sbridge_dev) - return NULL; - - for (i = 0; i < sbridge_dev->n_devs; i++) { - if (!sbridge_dev->pdev[i]) - continue; + struct pci_dev *pdev = NULL; - if (PCI_SLOT(sbridge_dev->pdev[i]->devfn) == slot && - PCI_FUNC(sbridge_dev->pdev[i]->devfn) == func) { - edac_dbg(1, "Associated %02x.%02x.%d with %p\n", - bus, slot, func, sbridge_dev->pdev[i]); - return sbridge_dev->pdev[i]; - } - } + do { + pdev = pci_get_device(PCI_VENDOR_ID_INTEL, id, pdev); + if (pdev && pdev->bus->number == bus) + break; + } while (pdev); - return NULL; + return pdev; } /** * check_if_ecc_is_active() - Checks if ECC is active - * bus: Device bus + * @bus: Device bus + * @type: Memory controller type + * returns: 0 in case ECC is active, -ENODEV if it can't be determined or + * disabled */ -static int check_if_ecc_is_active(const u8 bus) +static int check_if_ecc_is_active(const u8 bus, enum type type) { struct pci_dev *pdev = NULL; - u32 mcmtr; + u32 mcmtr, id; + + if (type == IVY_BRIDGE) + id = PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA; + else if (type == HASWELL) + id = PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_TA; + else + id = PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TA; - pdev = get_pdev_slot_func(bus, 15, 0); + pdev = get_pdev_same_bus(bus, id); if (!pdev) { sbridge_printk(KERN_ERR, "Couldn't find PCI device " - "%2x.%02d.%d!!!\n", - bus, 15, 0); + "%04x:%04x! on bus %02d\n", + PCI_VENDOR_ID_INTEL, id, bus); return -ENODEV; } @@ -661,11 +829,14 @@ static int get_dimm_config(struct mem_ctl_info *mci) enum edac_type mode; enum mem_type mtype; - pci_read_config_dword(pvt->pci_br0, SAD_TARGET, ®); + if (pvt->info.type == HASWELL) + pci_read_config_dword(pvt->pci_sad1, SAD_TARGET, ®); + else + pci_read_config_dword(pvt->pci_br0, SAD_TARGET, ®); + pvt->sbridge_dev->source_id = SOURCE_ID(reg); - pci_read_config_dword(pvt->pci_br0, SAD_CONTROL, ®); - pvt->sbridge_dev->node_id = NODE_ID(reg); + pvt->sbridge_dev->node_id = pvt->info.get_node_id(pvt); edac_dbg(0, "mc#%d: Node ID: %d, source ID: %d\n", pvt->sbridge_dev->mc, pvt->sbridge_dev->node_id, @@ -698,24 +869,18 @@ static int get_dimm_config(struct mem_ctl_info *mci) pvt->is_close_pg = false; } - if (pvt->pci_ddrio) { - pci_read_config_dword(pvt->pci_ddrio, pvt->info.rankcfgr, - ®); - if (IS_RDIMM_ENABLED(reg)) { - /* FIXME: Can also be LRDIMM */ - edac_dbg(0, "Memory is registered\n"); - mtype = MEM_RDDR3; - } else { - edac_dbg(0, "Memory is unregistered\n"); - mtype = MEM_DDR3; - } - } else { + mtype = pvt->info.get_memory_type(pvt); + if (mtype == MEM_RDDR3 || mtype == MEM_RDDR4) + edac_dbg(0, "Memory is registered\n"); + else if (mtype == MEM_UNKNOWN) edac_dbg(0, "Cannot determine memory type\n"); - mtype = MEM_UNKNOWN; - } + else + edac_dbg(0, "Memory is unregistered\n"); - /* On all supported DDR3 DIMM types, there are 8 banks available */ - banks = 8; + if (mtype == MEM_DDR4 || MEM_RDDR4) + banks = 16; + else + banks = 8; for (i = 0; i < NUM_CHANNELS; i++) { u32 mtr; @@ -729,11 +894,10 @@ static int get_dimm_config(struct mem_ctl_info *mci) if (IS_DIMM_PRESENT(mtr)) { pvt->channel[i].dimms++; - ranks = numrank(mtr); + ranks = numrank(pvt->info.type, mtr); rows = numrow(mtr); cols = numcol(mtr); - /* DDR3 has 8 I/O banks */ size = ((u64)rows * cols * banks * ranks) >> (20 - 3); npages = MiB_TO_PAGES(size); @@ -744,7 +908,17 @@ static int get_dimm_config(struct mem_ctl_info *mci) dimm->nr_pages = npages; dimm->grain = 32; - dimm->dtype = (banks == 8) ? DEV_X8 : DEV_X4; + switch (banks) { + case 16: + dimm->dtype = DEV_X16; + break; + case 8: + dimm->dtype = DEV_X8; + break; + case 4: + dimm->dtype = DEV_X4; + break; + } dimm->mtype = mtype; dimm->edac_mode = mode; snprintf(dimm->label, sizeof(dimm->label), @@ -887,7 +1061,7 @@ static void get_memory_layout(const struct mem_ctl_info *mci) if (!IS_RIR_VALID(reg)) continue; - tmp_mb = RIR_LIMIT(reg) >> 20; + tmp_mb = pvt->info.rir_limit(reg) >> 20; rir_way = 1 << RIR_WAY(reg); mb = div_u64_rem(tmp_mb, 1000, &kb); edac_dbg(0, "CH#%d RIR#%d, limit: %u.%03u GB (0x%016Lx), way: %d, reg=0x%08x\n", @@ -936,11 +1110,11 @@ static int get_memory_error_data(struct mem_ctl_info *mci, struct mem_ctl_info *new_mci; struct sbridge_pvt *pvt = mci->pvt_info; struct pci_dev *pci_ha; - int n_rir, n_sads, n_tads, sad_way, sck_xch; + int n_rir, n_sads, n_tads, sad_way, sck_xch; int sad_interl, idx, base_ch; - int interleave_mode; + int interleave_mode, shiftup = 0; unsigned sad_interleave[pvt->info.max_interleave]; - u32 reg; + u32 reg, dram_rule; u8 ch_way, sck_way, pkg, sad_ha = 0; u32 tad_offset; u32 rir_way; @@ -987,8 +1161,9 @@ static int get_memory_error_data(struct mem_ctl_info *mci, sprintf(msg, "Can't discover the memory socket"); return -EINVAL; } - *area_type = get_dram_attr(reg); - interleave_mode = INTERLEAVE_MODE(reg); + dram_rule = reg; + *area_type = get_dram_attr(dram_rule); + interleave_mode = INTERLEAVE_MODE(dram_rule); pci_read_config_dword(pvt->pci_sad0, pvt->info.interleave_list[n_sads], ®); @@ -1033,6 +1208,36 @@ static int get_memory_error_data(struct mem_ctl_info *mci, *socket = sad_interleave[idx]; edac_dbg(0, "SAD interleave index: %d (wayness %d) = CPU socket %d\n", idx, sad_way, *socket); + } else if (pvt->info.type == HASWELL) { + int bits, a7mode = A7MODE(dram_rule); + + if (a7mode) { + /* A7 mode swaps P9 with P6 */ + bits = GET_BITFIELD(addr, 7, 8) << 1; + bits |= GET_BITFIELD(addr, 9, 9); + } else + bits = GET_BITFIELD(addr, 7, 9); + + if (interleave_mode) { + /* interleave mode will XOR {8,7,6} with {18,17,16} */ + idx = GET_BITFIELD(addr, 16, 18); + idx ^= bits; + } else + idx = bits; + + pkg = sad_pkg(pvt->info.interleave_pkg, reg, idx); + *socket = sad_pkg_socket(pkg); + sad_ha = sad_pkg_ha(pkg); + + if (a7mode) { + /* MCChanShiftUpEnable */ + pci_read_config_dword(pvt->pci_ha0, + HASWELL_HASYSDEFEATURE2, ®); + shiftup = GET_BITFIELD(reg, 22, 22); + } + + edac_dbg(0, "SAD interleave package: %d = CPU socket %d, HA %i, shiftup: %i\n", + idx, *socket, sad_ha, shiftup); } else { /* Ivy Bridge's SAD mode doesn't support XOR interleave mode */ idx = (addr >> 6) & 7; @@ -1090,7 +1295,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci, if (ch_way == 3) idx = addr >> 6; else - idx = addr >> (6 + sck_way); + idx = (addr >> (6 + sck_way + shiftup)) & 0x3; idx = idx % ch_way; /* @@ -1181,7 +1386,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci, if (!IS_RIR_VALID(reg)) continue; - limit = RIR_LIMIT(reg); + limit = pvt->info.rir_limit(reg); mb = div_u64_rem(limit >> 20, 1000, &kb); edac_dbg(0, "RIR#%d, limit: %u.%03u GB (0x%016Lx), way: %d\n", n_rir, @@ -1197,6 +1402,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci, return -EINVAL; } rir_way = RIR_WAY(reg); + if (pvt->is_close_pg) idx = (ch_addr >> 6); else @@ -1259,13 +1465,11 @@ static int sbridge_get_onedevice(struct pci_dev **prev, { struct sbridge_dev *sbridge_dev; const struct pci_id_descr *dev_descr = &table->descr[devno]; - struct pci_dev *pdev = NULL; u8 bus = 0; sbridge_printk(KERN_DEBUG, - "Seeking for: dev %02x.%d PCI ID %04x:%04x\n", - dev_descr->dev, dev_descr->func, + "Seeking for: PCI ID %04x:%04x\n", PCI_VENDOR_ID_INTEL, dev_descr->dev_id); pdev = pci_get_device(PCI_VENDOR_ID_INTEL, @@ -1280,12 +1484,12 @@ static int sbridge_get_onedevice(struct pci_dev **prev, if (dev_descr->optional) return 0; + /* if the HA wasn't found */ if (devno == 0) return -ENODEV; sbridge_printk(KERN_INFO, - "Device not found: dev %02x.%d PCI ID %04x:%04x\n", - dev_descr->dev, dev_descr->func, + "Device not found: %04x:%04x\n", PCI_VENDOR_ID_INTEL, dev_descr->dev_id); /* End of list, leave */ @@ -1305,9 +1509,7 @@ static int sbridge_get_onedevice(struct pci_dev **prev, if (sbridge_dev->pdev[devno]) { sbridge_printk(KERN_ERR, - "Duplicated device for " - "dev %02x:%d.%d PCI ID %04x:%04x\n", - bus, dev_descr->dev, dev_descr->func, + "Duplicated device for %04x:%04x\n", PCI_VENDOR_ID_INTEL, dev_descr->dev_id); pci_dev_put(pdev); return -ENODEV; @@ -1315,30 +1517,15 @@ static int sbridge_get_onedevice(struct pci_dev **prev, sbridge_dev->pdev[devno] = pdev; - /* Sanity check */ - if (unlikely(PCI_SLOT(pdev->devfn) != dev_descr->dev || - PCI_FUNC(pdev->devfn) != dev_descr->func)) { - sbridge_printk(KERN_ERR, - "Device PCI ID %04x:%04x " - "has dev %02x:%d.%d instead of dev %02x:%02x.%d\n", - PCI_VENDOR_ID_INTEL, dev_descr->dev_id, - bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), - bus, dev_descr->dev, dev_descr->func); - return -ENODEV; - } - /* Be sure that the device is enabled */ if (unlikely(pci_enable_device(pdev) < 0)) { sbridge_printk(KERN_ERR, - "Couldn't enable " - "dev %02x:%d.%d PCI ID %04x:%04x\n", - bus, dev_descr->dev, dev_descr->func, + "Couldn't enable %04x:%04x\n", PCI_VENDOR_ID_INTEL, dev_descr->dev_id); return -ENODEV; } - edac_dbg(0, "Detected dev %02x:%d.%d PCI ID %04x:%04x\n", - bus, dev_descr->dev, dev_descr->func, + edac_dbg(0, "Detected %04x:%04x\n", PCI_VENDOR_ID_INTEL, dev_descr->dev_id); /* @@ -1355,10 +1542,9 @@ static int sbridge_get_onedevice(struct pci_dev **prev, /* * sbridge_get_all_devices - Find and perform 'get' operation on the MCH's - * device/functions we want to reference for this driver. - * Need to 'get' device 16 func 1 and func 2. + * devices we want to reference for this driver. * @num_mc: pointer to the memory controllers count, to be incremented in case - * of success. + * of success. * @table: model specific table * * returns 0 in case of success or error code @@ -1396,79 +1582,51 @@ static int sbridge_mci_bind_devs(struct mem_ctl_info *mci, { struct sbridge_pvt *pvt = mci->pvt_info; struct pci_dev *pdev; - int i, func, slot; + int i; for (i = 0; i < sbridge_dev->n_devs; i++) { pdev = sbridge_dev->pdev[i]; if (!pdev) continue; - slot = PCI_SLOT(pdev->devfn); - func = PCI_FUNC(pdev->devfn); - switch (slot) { - case 12: - switch (func) { - case 6: - pvt->pci_sad0 = pdev; - break; - case 7: - pvt->pci_sad1 = pdev; - break; - default: - goto error; - } + + switch (pdev->device) { + case PCI_DEVICE_ID_INTEL_SBRIDGE_SAD0: + pvt->pci_sad0 = pdev; break; - case 13: - switch (func) { - case 6: - pvt->pci_br0 = pdev; - break; - default: - goto error; - } + case PCI_DEVICE_ID_INTEL_SBRIDGE_SAD1: + pvt->pci_sad1 = pdev; break; - case 14: - switch (func) { - case 0: - pvt->pci_ha0 = pdev; - break; - default: - goto error; - } + case PCI_DEVICE_ID_INTEL_SBRIDGE_BR: + pvt->pci_br0 = pdev; break; - case 15: - switch (func) { - case 0: - pvt->pci_ta = pdev; - break; - case 1: - pvt->pci_ras = pdev; - break; - case 2: - case 3: - case 4: - case 5: - pvt->pci_tad[func - 2] = pdev; - break; - default: - goto error; - } + case PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_HA0: + pvt->pci_ha0 = pdev; break; - case 17: - switch (func) { - case 0: - pvt->pci_ddrio = pdev; - break; - default: - goto error; - } + case PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TA: + pvt->pci_ta = pdev; + break; + case PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_RAS: + pvt->pci_ras = pdev; + break; + case PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD0: + case PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD1: + case PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD2: + case PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD3: + { + int id = pdev->device - PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD0; + pvt->pci_tad[id] = pdev; + } + break; + case PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_DDRIO: + pvt->pci_ddrio = pdev; break; default: goto error; } - edac_dbg(0, "Associated PCI %02x.%02d.%d with dev = %p\n", + edac_dbg(0, "Associated PCI %02x:%02x, bus %d with dev = %p\n", + pdev->vendor, pdev->device, sbridge_dev->bus, - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev); } @@ -1488,9 +1646,8 @@ enodev: return -ENODEV; error: - sbridge_printk(KERN_ERR, "Device %d, function %d " - "is out of the expected range\n", - slot, func); + sbridge_printk(KERN_ERR, "Unexpected device %02x:%02x\n", + PCI_VENDOR_ID_INTEL, pdev->device); return -EINVAL; } @@ -1499,7 +1656,7 @@ static int ibridge_mci_bind_devs(struct mem_ctl_info *mci, { struct sbridge_pvt *pvt = mci->pvt_info; struct pci_dev *pdev, *tmp; - int i, func, slot; + int i; bool mode_2ha = false; tmp = pci_get_device(PCI_VENDOR_ID_INTEL, @@ -1513,79 +1670,60 @@ static int ibridge_mci_bind_devs(struct mem_ctl_info *mci, pdev = sbridge_dev->pdev[i]; if (!pdev) continue; - slot = PCI_SLOT(pdev->devfn); - func = PCI_FUNC(pdev->devfn); - switch (slot) { - case 14: - if (func == 0) { - pvt->pci_ha0 = pdev; - break; - } - goto error; - case 15: - switch (func) { - case 0: - pvt->pci_ta = pdev; - break; - case 1: - pvt->pci_ras = pdev; - break; - case 4: - case 5: - /* if we have 2 HAs active, channels 2 and 3 - * are in other device */ - if (mode_2ha) - break; - /* fall through */ - case 2: - case 3: - pvt->pci_tad[func - 2] = pdev; + switch (pdev->device) { + case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0: + pvt->pci_ha0 = pdev; + break; + case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA: + pvt->pci_ta = pdev; + case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_RAS: + pvt->pci_ras = pdev; + break; + case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD2: + case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD3: + /* if we have 2 HAs active, channels 2 and 3 + * are in other device */ + if (mode_2ha) break; - default: - goto error; - } + /* fall through */ + case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD0: + case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD1: + { + int id = pdev->device - PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD0; + pvt->pci_tad[id] = pdev; + } break; - case 17: - if (func == 4) { + case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_2HA_DDRIO0: + pvt->pci_ddrio = pdev; + break; + case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_1HA_DDRIO0: + if (!mode_2ha) pvt->pci_ddrio = pdev; - break; - } else if (func == 0) { - if (!mode_2ha) - pvt->pci_ddrio = pdev; - break; - } - goto error; - case 22: - switch (func) { - case 0: - pvt->pci_sad0 = pdev; - break; - case 1: - pvt->pci_br0 = pdev; - break; - case 2: - pvt->pci_br1 = pdev; - break; - default: - goto error; - } break; - case 28: - if (func == 0) { - pvt->pci_ha1 = pdev; - break; - } - goto error; - case 29: + case PCI_DEVICE_ID_INTEL_IBRIDGE_SAD: + pvt->pci_sad0 = pdev; + break; + case PCI_DEVICE_ID_INTEL_IBRIDGE_BR0: + pvt->pci_br0 = pdev; + break; + case PCI_DEVICE_ID_INTEL_IBRIDGE_BR1: + pvt->pci_br1 = pdev; + break; + case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1: + pvt->pci_ha1 = pdev; + break; + case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD0: + case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD1: + { + int id = pdev->device - PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD0 + 2; + /* we shouldn't have this device if we have just one * HA present */ WARN_ON(!mode_2ha); - if (func == 2 || func == 3) { - pvt->pci_tad[func] = pdev; - break; - } - goto error; + pvt->pci_tad[id] = pdev; + } + break; default: goto error; } @@ -1614,11 +1752,111 @@ enodev: error: sbridge_printk(KERN_ERR, - "Device %d, function %d is out of the expected range\n", - slot, func); + "Unexpected device %02x:%02x\n", PCI_VENDOR_ID_INTEL, + pdev->device); return -EINVAL; } +static int haswell_mci_bind_devs(struct mem_ctl_info *mci, + struct sbridge_dev *sbridge_dev) +{ + struct sbridge_pvt *pvt = mci->pvt_info; + struct pci_dev *pdev, *tmp; + int i; + bool mode_2ha = false; + + tmp = pci_get_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1, NULL); + if (tmp) { + mode_2ha = true; + pci_dev_put(tmp); + } + + /* there's only one device per system; not tied to any bus */ + if (pvt->info.pci_vtd == NULL) + /* result will be checked later */ + pvt->info.pci_vtd = pci_get_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_HASWELL_IMC_VTD_MISC, + NULL); + + for (i = 0; i < sbridge_dev->n_devs; i++) { + pdev = sbridge_dev->pdev[i]; + if (!pdev) + continue; + + switch (pdev->device) { + case PCI_DEVICE_ID_INTEL_HASWELL_IMC_CBO_SAD0: + pvt->pci_sad0 = pdev; + break; + case PCI_DEVICE_ID_INTEL_HASWELL_IMC_CBO_SAD1: + pvt->pci_sad1 = pdev; + break; + case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0: + pvt->pci_ha0 = pdev; + break; + case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_TA: + pvt->pci_ta = pdev; + break; + case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_THERMAL: + pvt->pci_ras = pdev; + break; + case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_TAD0: + pvt->pci_tad[0] = pdev; + break; + case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_TAD1: + pvt->pci_tad[1] = pdev; + break; + case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_TAD2: + if (!mode_2ha) + pvt->pci_tad[2] = pdev; + break; + case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_TAD3: + if (!mode_2ha) + pvt->pci_tad[3] = pdev; + break; + case PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO0: + pvt->pci_ddrio = pdev; + break; + case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1: + pvt->pci_ha1 = pdev; + break; + case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TA: + pvt->pci_ha1_ta = pdev; + break; + case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TAD0: + if (mode_2ha) + pvt->pci_tad[2] = pdev; + break; + case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TAD1: + if (mode_2ha) + pvt->pci_tad[3] = pdev; + break; + default: + break; + } + + edac_dbg(0, "Associated PCI %02x.%02d.%d with dev = %p\n", + sbridge_dev->bus, + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), + pdev); + } + + /* Check if everything were registered */ + if (!pvt->pci_sad0 || !pvt->pci_ha0 || !pvt->pci_sad1 || + !pvt->pci_ras || !pvt->pci_ta || !pvt->info.pci_vtd) + goto enodev; + + for (i = 0; i < NUM_CHANNELS; i++) { + if (!pvt->pci_tad[i]) + goto enodev; + } + return 0; + +enodev: + sbridge_printk(KERN_ERR, "Some needed devices are missing\n"); + return -ENODEV; +} + /**************************************************************************** Error check routines ****************************************************************************/ @@ -1736,6 +1974,9 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci, * EDAC core should be handling the channel mask, in order to point * to the group of dimm's where the error may be happening. */ + if (!pvt->is_lockstep && !pvt->is_mirrored && !pvt->is_close_pg) + channel = first_channel; + snprintf(msg, sizeof(msg), "%s%s area:%s err_code:%04x:%04x socket:%d channel_mask:%ld rank:%d", overflow ? " OVERFLOW" : "", @@ -1865,10 +2106,6 @@ static int sbridge_mce_check_error(struct notifier_block *nb, unsigned long val, "%u APIC %x\n", mce->cpuvendor, mce->cpuid, mce->time, mce->socketid, mce->apicid); - /* Only handle if it is the right mc controller */ - if (cpu_data(mce->cpu).phys_proc_id != pvt->sbridge_dev->mc) - return NOTIFY_DONE; - smp_rmb(); if ((pvt->mce_out + 1) % MCE_LOG_LEN == pvt->mce_in) { smp_wmb(); @@ -1932,7 +2169,7 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) int rc; /* Check the number of active and not disabled channels */ - rc = check_if_ecc_is_active(sbridge_dev->bus); + rc = check_if_ecc_is_active(sbridge_dev->bus, type); if (unlikely(rc < 0)) return rc; @@ -1971,11 +2208,15 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) mci->edac_check = sbridge_check_error; pvt->info.type = type; - if (type == IVY_BRIDGE) { + switch (type) { + case IVY_BRIDGE: pvt->info.rankcfgr = IB_RANK_CFG_A; pvt->info.get_tolm = ibridge_get_tolm; pvt->info.get_tohm = ibridge_get_tohm; pvt->info.dram_rule = ibridge_dram_rule; + pvt->info.get_memory_type = get_memory_type; + pvt->info.get_node_id = get_node_id; + pvt->info.rir_limit = rir_limit; pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule); pvt->info.interleave_list = ibridge_interleave_list; pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list); @@ -1986,11 +2227,15 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) rc = ibridge_mci_bind_devs(mci, sbridge_dev); if (unlikely(rc < 0)) goto fail0; - } else { + break; + case SANDY_BRIDGE: pvt->info.rankcfgr = SB_RANK_CFG_A; pvt->info.get_tolm = sbridge_get_tolm; pvt->info.get_tohm = sbridge_get_tohm; pvt->info.dram_rule = sbridge_dram_rule; + pvt->info.get_memory_type = get_memory_type; + pvt->info.get_node_id = get_node_id; + pvt->info.rir_limit = rir_limit; pvt->info.max_sad = ARRAY_SIZE(sbridge_dram_rule); pvt->info.interleave_list = sbridge_interleave_list; pvt->info.max_interleave = ARRAY_SIZE(sbridge_interleave_list); @@ -2001,8 +2246,27 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) rc = sbridge_mci_bind_devs(mci, sbridge_dev); if (unlikely(rc < 0)) goto fail0; - } + break; + case HASWELL: + /* rankcfgr isn't used */ + pvt->info.get_tolm = haswell_get_tolm; + pvt->info.get_tohm = haswell_get_tohm; + pvt->info.dram_rule = ibridge_dram_rule; + pvt->info.get_memory_type = haswell_get_memory_type; + pvt->info.get_node_id = haswell_get_node_id; + pvt->info.rir_limit = haswell_rir_limit; + pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule); + pvt->info.interleave_list = ibridge_interleave_list; + pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list); + pvt->info.interleave_pkg = ibridge_interleave_pkg; + mci->ctl_name = kasprintf(GFP_KERNEL, "Haswell Socket#%d", mci->mc_idx); + /* Store pci devices at mci for faster access */ + rc = haswell_mci_bind_devs(mci, sbridge_dev); + if (unlikely(rc < 0)) + goto fail0; + break; + } /* Get dimm basic config and the memory layout */ get_dimm_config(mci); @@ -2037,10 +2301,10 @@ fail0: static int sbridge_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - int rc; + int rc = -ENODEV; u8 mc, num_mc = 0; struct sbridge_dev *sbridge_dev; - enum type type; + enum type type = SANDY_BRIDGE; /* get the pci devices we want to reserve for our use */ mutex_lock(&sbridge_edac_lock); @@ -2054,12 +2318,19 @@ static int sbridge_probe(struct pci_dev *pdev, const struct pci_device_id *id) } probed++; - if (pdev->device == PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA) { + switch (pdev->device) { + case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA: rc = sbridge_get_all_devices(&num_mc, pci_dev_descr_ibridge_table); type = IVY_BRIDGE; - } else { + break; + case PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TA: rc = sbridge_get_all_devices(&num_mc, pci_dev_descr_sbridge_table); type = SANDY_BRIDGE; + break; + case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0: + rc = sbridge_get_all_devices(&num_mc, pci_dev_descr_haswell_table); + type = HASWELL; + break; } if (unlikely(rc < 0)) goto fail0; @@ -2068,6 +2339,7 @@ static int sbridge_probe(struct pci_dev *pdev, const struct pci_device_id *id) list_for_each_entry(sbridge_dev, &sbridge_edac_list, list) { edac_dbg(0, "Registering MC#%d (%d of %d)\n", mc, mc + 1, num_mc); + sbridge_dev->mc = mc++; rc = sbridge_register_mci(sbridge_dev, type); if (unlikely(rc < 0)) diff --git a/include/linux/edac.h b/include/linux/edac.h index 8e6c20af11a2..e1e68da6f35c 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -194,6 +194,9 @@ static inline char *mc_event_error_type(const unsigned int err_type) * @MEM_DDR3: DDR3 RAM * @MEM_RDDR3: Registered DDR3 RAM * This is a variant of the DDR3 memories. + * @MEM_DDR4: DDR4 RAM + * @MEM_RDDR4: Registered DDR4 RAM + * This is a variant of the DDR4 memories. */ enum mem_type { MEM_EMPTY = 0, @@ -213,6 +216,8 @@ enum mem_type { MEM_XDR, MEM_DDR3, MEM_RDDR3, + MEM_DDR4, + MEM_RDDR4, }; #define MEM_FLAG_EMPTY BIT(MEM_EMPTY) |