diff options
| author | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-06-22 21:41:15 -0400 |
|---|---|---|
| committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-05-10 10:44:45 -0400 |
| commit | a0c36a1f0fbab42590dab3c13c10fa7d20e6c2cd (patch) | |
| tree | 1aba0a7bd6f50cf394e747d02c624f456a24fdb8 | |
| parent | 66f41d4c5c8a5deed66fdcc84509376c9a0bf9d8 (diff) | |
i7core_edac: Add an EDAC memory controller driver for Nehalem chipsets
This driver is meant to support i7 core/i7core extreme desktop
processors and Xeon 35xx/55xx series with integrated memory controller.
It is likely that it can be expanded in the future to work with other
processor series based at the same Memory Controller design.
For now, it has just a few MCH status reads.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
| -rw-r--r-- | drivers/edac/Kconfig | 7 | ||||
| -rw-r--r-- | drivers/edac/Makefile | 1 | ||||
| -rw-r--r-- | drivers/edac/i7core_edac.c | 462 | ||||
| -rw-r--r-- | include/linux/pci_ids.h | 16 |
4 files changed, 486 insertions, 0 deletions
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 55c9c59b3f71..391ddbfb2a34 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig | |||
| @@ -166,6 +166,13 @@ config EDAC_I5400 | |||
| 166 | Support for error detection and correction the Intel | 166 | Support for error detection and correction the Intel |
| 167 | i5400 MCH chipset (Seaburg). | 167 | i5400 MCH chipset (Seaburg). |
| 168 | 168 | ||
| 169 | config EDAC_I7CORE | ||
| 170 | tristate "Intel i7 Core (Nehalem) processors" | ||
| 171 | depends on EDAC_MM_EDAC && PCI && X86 | ||
| 172 | help | ||
| 173 | Support for error detection and correction the Intel | ||
| 174 | i7 Core (Nehalem) Integrated Memory Controller | ||
| 175 | |||
| 169 | config EDAC_I82860 | 176 | config EDAC_I82860 |
| 170 | tristate "Intel 82860" | 177 | tristate "Intel 82860" |
| 171 | depends on EDAC_MM_EDAC && PCI && X86_32 | 178 | depends on EDAC_MM_EDAC && PCI && X86_32 |
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index bc5dc232a0fb..b9996195b233 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile | |||
| @@ -23,6 +23,7 @@ obj-$(CONFIG_EDAC_CPC925) += cpc925_edac.o | |||
| 23 | obj-$(CONFIG_EDAC_I5000) += i5000_edac.o | 23 | obj-$(CONFIG_EDAC_I5000) += i5000_edac.o |
| 24 | obj-$(CONFIG_EDAC_I5100) += i5100_edac.o | 24 | obj-$(CONFIG_EDAC_I5100) += i5100_edac.o |
| 25 | obj-$(CONFIG_EDAC_I5400) += i5400_edac.o | 25 | obj-$(CONFIG_EDAC_I5400) += i5400_edac.o |
| 26 | obj-$(CONFIG_EDAC_I7CORE) += i7core_edac.o | ||
| 26 | obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o | 27 | obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o |
| 27 | obj-$(CONFIG_EDAC_E752X) += e752x_edac.o | 28 | obj-$(CONFIG_EDAC_E752X) += e752x_edac.o |
| 28 | obj-$(CONFIG_EDAC_I82443BXGX) += i82443bxgx_edac.o | 29 | obj-$(CONFIG_EDAC_I82443BXGX) += i82443bxgx_edac.o |
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c new file mode 100644 index 000000000000..7ecf15e66a3f --- /dev/null +++ b/drivers/edac/i7core_edac.c | |||
| @@ -0,0 +1,462 @@ | |||
| 1 | /* Intel 7 core Memory Controller kernel module (Nehalem) | ||
| 2 | * | ||
| 3 | * This file may be distributed under the terms of the | ||
| 4 | * GNU General Public License version 2 only. | ||
| 5 | * | ||
| 6 | * Copyright (c) 2009 by: | ||
| 7 | * Mauro Carvalho Chehab <mchehab@redhat.com> | ||
| 8 | * | ||
| 9 | * Red Hat Inc. http://www.redhat.com | ||
| 10 | * | ||
| 11 | * Forked and adapted from the i5400_edac driver | ||
| 12 | * | ||
| 13 | * Based on the following public Intel datasheets: | ||
| 14 | * Intel Core i7 Processor Extreme Edition and Intel Core i7 Processor | ||
| 15 | * Datasheet, Volume 2: | ||
| 16 | * http://download.intel.com/design/processor/datashts/320835.pdf | ||
| 17 | * Intel Xeon Processor 5500 Series Datasheet Volume 2 | ||
| 18 | * http://www.intel.com/Assets/PDF/datasheet/321322.pdf | ||
| 19 | * also available at: | ||
| 20 | * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf | ||
| 21 | */ | ||
| 22 | |||
| 23 | |||
| 24 | #include <linux/module.h> | ||
| 25 | #include <linux/init.h> | ||
| 26 | #include <linux/pci.h> | ||
| 27 | #include <linux/pci_ids.h> | ||
| 28 | #include <linux/slab.h> | ||
| 29 | #include <linux/edac.h> | ||
| 30 | #include <linux/mmzone.h> | ||
| 31 | |||
| 32 | #include "edac_core.h" | ||
| 33 | |||
| 34 | |||
| 35 | /* | ||
| 36 | * Alter this version for the module when modifications are made | ||
| 37 | */ | ||
| 38 | #define I7CORE_REVISION " Ver: 1.0.0 " __DATE__ | ||
| 39 | #define EDAC_MOD_STR "i7core_edac" | ||
| 40 | |||
| 41 | /* HACK: temporary, just to enable all logs, for now */ | ||
| 42 | #undef debugf0 | ||
| 43 | #define debugf0(fmt, arg...) edac_printk(KERN_INFO, "i7core", fmt, ##arg) | ||
| 44 | |||
| 45 | /* | ||
| 46 | * Debug macros | ||
| 47 | */ | ||
| 48 | #define i7core_printk(level, fmt, arg...) \ | ||
| 49 | edac_printk(level, "i7core", fmt, ##arg) | ||
| 50 | |||
| 51 | #define i7core_mc_printk(mci, level, fmt, arg...) \ | ||
| 52 | edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg) | ||
| 53 | |||
| 54 | /* | ||
| 55 | * i7core Memory Controller Registers | ||
| 56 | */ | ||
| 57 | |||
| 58 | /* OFFSETS for Device 3 Function 0 */ | ||
| 59 | |||
| 60 | #define MC_CONTROL 0x48 | ||
| 61 | #define MC_STATUS 0x4c | ||
| 62 | #define MC_MAX_DOD 0x64 | ||
| 63 | |||
| 64 | /* OFFSETS for Devices 4,5 and 6 Function 0 */ | ||
| 65 | |||
| 66 | #define MC_CHANNEL_ADDR_MATCH 0xf0 | ||
| 67 | |||
| 68 | #define MC_MASK_DIMM (1 << 41) | ||
| 69 | #define MC_MASK_RANK (1 << 40) | ||
| 70 | #define MC_MASK_BANK (1 << 39) | ||
| 71 | #define MC_MASK_PAGE (1 << 38) | ||
| 72 | #define MC_MASK_COL (1 << 37) | ||
| 73 | |||
| 74 | /* | ||
| 75 | * i7core structs | ||
| 76 | */ | ||
| 77 | |||
| 78 | #define NUM_CHANS 3 | ||
| 79 | #define NUM_FUNCS 1 | ||
| 80 | |||
| 81 | struct i7core_info { | ||
| 82 | u32 mc_control; | ||
| 83 | u32 mc_status; | ||
| 84 | u32 max_dod; | ||
| 85 | }; | ||
| 86 | |||
| 87 | struct i7core_pvt { | ||
| 88 | struct pci_dev *pci_mcr; /* Dev 3:0 */ | ||
| 89 | struct pci_dev *pci_ch[NUM_CHANS][NUM_FUNCS]; | ||
| 90 | struct i7core_info info; | ||
| 91 | }; | ||
| 92 | |||
| 93 | /* Device name and register DID (Device ID) */ | ||
| 94 | struct i7core_dev_info { | ||
| 95 | const char *ctl_name; /* name for this device */ | ||
| 96 | u16 fsb_mapping_errors; /* DID for the branchmap,control */ | ||
| 97 | }; | ||
| 98 | |||
| 99 | static int chan_pci_ids[NUM_CHANS] = { | ||
| 100 | PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL, /* Dev 4 */ | ||
| 101 | PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL, /* Dev 5 */ | ||
| 102 | PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL, /* Dev 6 */ | ||
| 103 | }; | ||
| 104 | |||
| 105 | /* Table of devices attributes supported by this driver */ | ||
| 106 | static const struct i7core_dev_info i7core_devs[] = { | ||
| 107 | { | ||
| 108 | .ctl_name = "i7 Core", | ||
| 109 | .fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7_MCR, | ||
| 110 | }, | ||
| 111 | }; | ||
| 112 | |||
| 113 | static struct edac_pci_ctl_info *i7core_pci; | ||
| 114 | |||
| 115 | /**************************************************************************** | ||
| 116 | Anciliary status routines | ||
| 117 | ****************************************************************************/ | ||
| 118 | |||
| 119 | /* MC_CONTROL bits */ | ||
| 120 | #define CH2_ACTIVE(pvt) ((pvt)->info.mc_control & 1 << 10) | ||
| 121 | #define CH1_ACTIVE(pvt) ((pvt)->info.mc_control & 1 << 9) | ||
| 122 | #define CH0_ACTIVE(pvt) ((pvt)->info.mc_control & 1 << 8) | ||
| 123 | #define ECCx8(pvt) ((pvt)->info.mc_control & 1 << 1) | ||
| 124 | |||
| 125 | /* MC_STATUS bits */ | ||
| 126 | #define ECC_ENABLED(pvt) ((pvt)->info.mc_status & 1 << 3) | ||
| 127 | #define CH2_DISABLED(pvt) ((pvt)->info.mc_status & 1 << 2) | ||
| 128 | #define CH1_DISABLED(pvt) ((pvt)->info.mc_status & 1 << 1) | ||
| 129 | #define CH0_DISABLED(pvt) ((pvt)->info.mc_status & 1 << 0) | ||
| 130 | |||
| 131 | /* MC_MAX_DOD read functions */ | ||
| 132 | static inline int maxnumdimms(struct i7core_pvt *pvt) | ||
| 133 | { | ||
| 134 | return (pvt->info.max_dod & 0x3) + 1; | ||
| 135 | } | ||
| 136 | |||
| 137 | static inline int maxnumrank(struct i7core_pvt *pvt) | ||
| 138 | { | ||
| 139 | static int ranks[4] = { 1, 2, 4, -EINVAL }; | ||
| 140 | |||
| 141 | return ranks[(pvt->info.max_dod >> 2) & 0x3]; | ||
| 142 | } | ||
| 143 | |||
| 144 | static inline int maxnumbank(struct i7core_pvt *pvt) | ||
| 145 | { | ||
| 146 | static int banks[4] = { 4, 8, 16, -EINVAL }; | ||
| 147 | |||
| 148 | return banks[(pvt->info.max_dod >> 4) & 0x3]; | ||
| 149 | } | ||
| 150 | |||
| 151 | static inline int maxnumrow(struct i7core_pvt *pvt) | ||
| 152 | { | ||
| 153 | static int rows[8] = { | ||
| 154 | 1 << 12, 1 << 13, 1 << 14, 1 << 15, | ||
| 155 | 1 << 16, -EINVAL, -EINVAL, -EINVAL, | ||
| 156 | }; | ||
| 157 | |||
| 158 | return rows[((pvt->info.max_dod >> 6) & 0x7)]; | ||
| 159 | } | ||
| 160 | |||
| 161 | static inline int maxnumcol(struct i7core_pvt *pvt) | ||
