aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@redhat.com>2013-02-14 07:11:08 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2013-02-25 17:42:13 -0500
commit32fa1f53c2daf9f55f17ff883b4297f86095b09c (patch)
tree3843fb442b7dfb5016ed837a65c12600b2669ecc
parentf04c62a7036a4b8490b224a9ad1be4378a3acf4d (diff)
ghes_edac: do a better job of filling EDAC DIMM info
Instead of just faking a random value for the DIMM data, get the information that it is available via DMI table. Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r--drivers/edac/ghes_edac.c192
1 files changed, 180 insertions, 12 deletions
diff --git a/drivers/edac/ghes_edac.c b/drivers/edac/ghes_edac.c
index 0853f450d2c1..22ac29e4733f 100644
--- a/drivers/edac/ghes_edac.c
+++ b/drivers/edac/ghes_edac.c
@@ -11,6 +11,7 @@
11 11
12#include <acpi/ghes.h> 12#include <acpi/ghes.h>
13#include <linux/edac.h> 13#include <linux/edac.h>
14#include <linux/dmi.h>
14#include "edac_core.h" 15#include "edac_core.h"
15 16
16#define GHES_PFX "ghes_edac: " 17#define GHES_PFX "ghes_edac: "
@@ -26,6 +27,155 @@ static LIST_HEAD(ghes_reglist);
26static DEFINE_MUTEX(ghes_edac_lock); 27static DEFINE_MUTEX(ghes_edac_lock);
27static int ghes_edac_mc_num; 28static int ghes_edac_mc_num;
28 29
30/* Memory Device - Type 17 of SMBIOS spec */
31struct memdev_dmi_entry {
32 u8 type;
33 u8 length;
34 u16 handle;
35 u16 phys_mem_array_handle;
36 u16 mem_err_info_handle;
37 u16 total_width;
38 u16 data_width;
39 u16 size;
40 u8 form_factor;
41 u8 device_set;
42 u8 device_locator;
43 u8 bank_locator;
44 u8 memory_type;
45 u16 type_detail;
46 u16 speed;
47 u8 manufacturer;
48 u8 serial_number;
49 u8 asset_tag;
50 u8 part_number;
51 u8 attributes;
52 u32 extended_size;
53 u16 conf_mem_clk_speed;
54} __attribute__((__packed__));
55
56struct ghes_edac_dimm_fill {
57 struct mem_ctl_info *mci;
58 unsigned count;
59};
60
61char *memory_type[] = {
62 [MEM_EMPTY] = "EMPTY",
63 [MEM_RESERVED] = "RESERVED",
64 [MEM_UNKNOWN] = "UNKNOWN",
65 [MEM_FPM] = "FPM",
66 [MEM_EDO] = "EDO",
67 [MEM_BEDO] = "BEDO",
68 [MEM_SDR] = "SDR",
69 [MEM_RDR] = "RDR",
70 [MEM_DDR] = "DDR",
71 [MEM_RDDR] = "RDDR",
72 [MEM_RMBS] = "RMBS",
73 [MEM_DDR2] = "DDR2",
74 [MEM_FB_DDR2] = "FB_DDR2",
75 [MEM_RDDR2] = "RDDR2",
76 [MEM_XDR] = "XDR",
77 [MEM_DDR3] = "DDR3",
78 [MEM_RDDR3] = "RDDR3",
79};
80
81static void ghes_edac_count_dimms(const struct dmi_header *dh, void *arg)
82{
83 int *num_dimm = arg;
84
85 if (dh->type == DMI_ENTRY_MEM_DEVICE)
86 (*num_dimm)++;
87}
88
89static void ghes_edac_dmidecode(const struct dmi_header *dh, void *arg)
90{
91 struct ghes_edac_dimm_fill *dimm_fill = arg;
92 struct mem_ctl_info *mci = dimm_fill->mci;
93
94 if (dh->type == DMI_ENTRY_MEM_DEVICE) {
95 struct memdev_dmi_entry *entry = (struct memdev_dmi_entry *)dh;
96 struct dimm_info *dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms,
97 mci->n_layers,
98 dimm_fill->count, 0, 0);
99
100 if (entry->size == 0xffff) {
101 pr_info(GHES_PFX "Can't get dimm size\n");
102 dimm->nr_pages = MiB_TO_PAGES(32);/* Unknown */
103 } else if (entry->size == 0x7fff) {
104 dimm->nr_pages = MiB_TO_PAGES(entry->extended_size);
105 } else {
106 if (entry->size & 1 << 15)
107 dimm->nr_pages = MiB_TO_PAGES((entry->size &
108 0x7fff) << 10);
109 else
110 dimm->nr_pages = MiB_TO_PAGES(entry->size);
111 }
112
113 switch (entry->memory_type) {
114 case 0x12:
115 if (entry->type_detail & 1 << 13)
116 dimm->mtype = MEM_RDDR;
117 else
118 dimm->mtype = MEM_DDR;
119 break;
120 case 0x13:
121 if (entry->type_detail & 1 << 13)
122 dimm->mtype = MEM_RDDR2;
123 else
124 dimm->mtype = MEM_DDR2;
125 break;
126 case 0x14:
127 dimm->mtype = MEM_FB_DDR2;
128 break;
129 case 0x18:
130 if (entry->type_detail & 1 << 13)
131 dimm->mtype = MEM_RDDR3;
132 else
133 dimm->mtype = MEM_DDR3;
134 break;
135 default:
136 if (entry->type_detail & 1 << 6)
137 dimm->mtype = MEM_RMBS;
138 else if ((entry->type_detail & ((1 << 7) | (1 << 13)))
139 == ((1 << 7) | (1 << 13)))
140 dimm->mtype = MEM_RDR;
141 else if (entry->type_detail & 1 << 7)
142 dimm->mtype = MEM_SDR;
143 else if (entry->type_detail & 1 << 9)
144 dimm->mtype = MEM_EDO;
145 else
146 dimm->mtype = MEM_UNKNOWN;
147 }
148
149 /*
150 * Actually, we can only detect if the memory has bits for
151 * checksum or not
152 */
153 if (entry->total_width == entry->data_width)
154 dimm->edac_mode = EDAC_NONE;
155 else
156 dimm->edac_mode = EDAC_SECDED;
157
158 dimm->dtype = DEV_UNKNOWN;
159 dimm->grain = 128; /* Likely, worse case */
160
161 /*
162 * FIXME: It shouldn't be hard to also fill the DIMM labels
163 */
164
165 if (dimm->nr_pages) {
166 pr_info(GHES_PFX "DIMM%i: %s size = %d MB%s\n",
167 dimm_fill->count, memory_type[dimm->mtype],
168 PAGES_TO_MiB(dimm->nr_pages),
169 (dimm->edac_mode != EDAC_NONE) ? "(ECC)" : "");
170 pr_info(GHES_PFX "\ttype %d, detail 0x%02x, width %d(total %d)\n",
171 entry->memory_type, entry->type_detail,
172 entry->total_width, entry->data_width);
173 }
174
175 dimm_fill->count++;
176 }
177}
178
29void ghes_edac_report_mem_error(struct ghes *ghes, int sev, 179void ghes_edac_report_mem_error(struct ghes *ghes, int sev,
30 struct cper_sec_mem_err *mem_err) 180 struct cper_sec_mem_err *mem_err)
31{ 181{
@@ -86,15 +236,24 @@ EXPORT_SYMBOL_GPL(ghes_edac_report_mem_error);
86 236
87int ghes_edac_register(struct ghes *ghes, struct device *dev) 237int ghes_edac_register(struct ghes *ghes, struct device *dev)
88{ 238{
89 int rc; 239 bool fake = false;
240 int rc, num_dimm = 0;
90 struct mem_ctl_info *mci; 241 struct mem_ctl_info *mci;
91 struct edac_mc_layer layers[1]; 242 struct edac_mc_layer layers[1];
92 struct csrow_info *csrow;
93 struct dimm_info *dimm;
94 struct ghes_edac_pvt *pvt; 243 struct ghes_edac_pvt *pvt;
244 struct ghes_edac_dimm_fill dimm_fill;
245
246 /* Get the number of DIMMs */
247 dmi_walk(ghes_edac_count_dimms, &num_dimm);
248
249 /* Check if we've got a bogus BIOS */
250 if (num_dimm == 0) {
251 fake = true;
252 num_dimm = 1;
253 }
95 254
96 layers[0].type = EDAC_MC_LAYER_ALL_MEM; 255 layers[0].type = EDAC_MC_LAYER_ALL_MEM;
97 layers[0].size = 1; 256 layers[0].size = num_dimm;
98 layers[0].is_virt_csrow = true; 257 layers[0].is_virt_csrow = true;
99 258
100 /* 259 /*
@@ -102,6 +261,8 @@ int ghes_edac_register(struct ghes *ghes, struct device *dev)
102 * to avoid duplicated memory controller numbers 261 * to avoid duplicated memory controller numbers
103 */ 262 */
104 mutex_lock(&ghes_edac_lock); 263 mutex_lock(&ghes_edac_lock);
264 pr_info("ghes_edac#%d: allocating space for %d dimms\n",
265 ghes_edac_mc_num, num_dimm);
105 mci = edac_mc_alloc(ghes_edac_mc_num, ARRAY_SIZE(layers), layers, 266 mci = edac_mc_alloc(ghes_edac_mc_num, ARRAY_SIZE(layers), layers,
106 sizeof(*pvt)); 267 sizeof(*pvt));
107 if (!mci) { 268 if (!mci) {
@@ -125,15 +286,22 @@ int ghes_edac_register(struct ghes *ghes, struct device *dev)
125 mci->ctl_name = "ghes_edac"; 286 mci->ctl_name = "ghes_edac";
126 mci->dev_name = "ghes"; 287 mci->dev_name = "ghes";
127 288
128 csrow = mci->csrows[0]; 289 if (!fake) {
129 dimm = csrow->channels[0]->dimm; 290 /* Fill DIMM info from DMI */
291 dimm_fill.count = 0;
292 dimm_fill.mci = mci;
293 dmi_walk(ghes_edac_dmidecode, &dimm_fill);
294 } else {
295 struct dimm_info *dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms,
296 mci->n_layers, 0, 0, 0);
130 297
131 /* FIXME: FAKE DATA */ 298 pr_info(GHES_PFX "Crappy BIOS detected. Faking DIMM EDAC data\n");
132 dimm->nr_pages = 1000; 299 dimm->nr_pages = 1000;
133 dimm->grain = 128; 300 dimm->grain = 128;
134 dimm->mtype = MEM_UNKNOWN; 301 dimm->mtype = MEM_UNKNOWN;
135 dimm->dtype = DEV_UNKNOWN; 302 dimm->dtype = DEV_UNKNOWN;
136 dimm->edac_mode = EDAC_SECDED; 303 dimm->edac_mode = EDAC_SECDED;
304 }
137 305
138 rc = edac_mc_add_mc(mci); 306 rc = edac_mc_add_mc(mci);
139 if (rc < 0) { 307 if (rc < 0) {