blob: 0853f450d2c102cdcefee8503e796a811c4a79f6 [file] [log] [blame]
Mauro Carvalho Chehab77c5f5d2013-02-15 06:11:57 -03001/*
2 * GHES/EDAC Linux driver
3 *
4 * This file may be distributed under the terms of the GNU General Public
5 * License version 2.
6 *
7 * Copyright (c) 2013 by Mauro Carvalho Chehab <mchehab@redhat.com>
8 *
9 * Red Hat Inc. http://www.redhat.com
10 */
11
12#include <acpi/ghes.h>
13#include <linux/edac.h>
14#include "edac_core.h"
15
16#define GHES_PFX "ghes_edac: "
17#define GHES_EDAC_REVISION " Ver: 1.0.0"
18
19struct ghes_edac_pvt {
20 struct list_head list;
21 struct ghes *ghes;
22 struct mem_ctl_info *mci;
23};
24
25static LIST_HEAD(ghes_reglist);
26static DEFINE_MUTEX(ghes_edac_lock);
27static int ghes_edac_mc_num;
28
29void ghes_edac_report_mem_error(struct ghes *ghes, int sev,
Mauro Carvalho Chehabf04c62a2013-02-15 06:36:27 -030030 struct cper_sec_mem_err *mem_err)
Mauro Carvalho Chehab77c5f5d2013-02-15 06:11:57 -030031{
Mauro Carvalho Chehabf04c62a2013-02-15 06:36:27 -030032 enum hw_event_mc_err_type type;
33 struct edac_raw_error_desc *e;
34 struct mem_ctl_info *mci;
35 struct ghes_edac_pvt *pvt = NULL;
36
37 list_for_each_entry(pvt, &ghes_reglist, list) {
38 if (ghes == pvt->ghes)
39 break;
40 }
41 if (!pvt) {
42 pr_err("Internal error: Can't find EDAC structure\n");
43 return;
44 }
45 mci = pvt->mci;
46 e = &mci->error_desc;
47
48 /* Cleans the error report buffer */
49 memset(e, 0, sizeof (*e));
50 e->error_count = 1;
51 e->msg = "APEI";
52 strcpy(e->label, "unknown");
53 e->other_detail = "";
54
55 if (mem_err->validation_bits & CPER_MEM_VALID_PHYSICAL_ADDRESS) {
56 e->page_frame_number = mem_err->physical_addr >> PAGE_SHIFT;
57 e->offset_in_page = mem_err->physical_addr & ~PAGE_MASK;
58 e->grain = ~(mem_err->physical_addr_mask & ~PAGE_MASK);
59 }
60
61 switch (sev) {
62 case GHES_SEV_CORRECTED:
63 type = HW_EVENT_ERR_CORRECTED;
64 break;
65 case GHES_SEV_RECOVERABLE:
66 type = HW_EVENT_ERR_UNCORRECTED;
67 break;
68 case GHES_SEV_PANIC:
69 type = HW_EVENT_ERR_FATAL;
70 break;
71 default:
72 case GHES_SEV_NO:
73 type = HW_EVENT_ERR_INFO;
74 }
75
76 sprintf(e->location,
77 "node:%d card:%d module:%d bank:%d device:%d row: %d column:%d bit_pos:%d",
78 mem_err->node, mem_err->card, mem_err->module,
79 mem_err->bank, mem_err->device, mem_err->row, mem_err->column,
80 mem_err->bit_pos);
81 edac_dbg(3, "error at location %s\n", e->location);
82
83 edac_raw_mc_handle_error(type, mci, e);
Mauro Carvalho Chehab77c5f5d2013-02-15 06:11:57 -030084}
85EXPORT_SYMBOL_GPL(ghes_edac_report_mem_error);
86
87int ghes_edac_register(struct ghes *ghes, struct device *dev)
88{
89 int rc;
90 struct mem_ctl_info *mci;
91 struct edac_mc_layer layers[1];
92 struct csrow_info *csrow;
93 struct dimm_info *dimm;
94 struct ghes_edac_pvt *pvt;
95
96 layers[0].type = EDAC_MC_LAYER_ALL_MEM;
97 layers[0].size = 1;
98 layers[0].is_virt_csrow = true;
99
100 /*
101 * We need to serialize edac_mc_alloc() and edac_mc_add_mc(),
102 * to avoid duplicated memory controller numbers
103 */
104 mutex_lock(&ghes_edac_lock);
105 mci = edac_mc_alloc(ghes_edac_mc_num, ARRAY_SIZE(layers), layers,
106 sizeof(*pvt));
107 if (!mci) {
108 pr_info(GHES_PFX "Can't allocate memory for EDAC data\n");
109 mutex_unlock(&ghes_edac_lock);
110 return -ENOMEM;
111 }
112
113 pvt = mci->pvt_info;
114 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehabf04c62a2013-02-15 06:36:27 -0300115 list_add_tail(&pvt->list, &ghes_reglist);
Mauro Carvalho Chehab77c5f5d2013-02-15 06:11:57 -0300116 pvt->ghes = ghes;
117 pvt->mci = mci;
118 mci->pdev = dev;
119
120 mci->mtype_cap = MEM_FLAG_EMPTY;
121 mci->edac_ctl_cap = EDAC_FLAG_NONE;
122 mci->edac_cap = EDAC_FLAG_NONE;
123 mci->mod_name = "ghes_edac.c";
124 mci->mod_ver = GHES_EDAC_REVISION;
125 mci->ctl_name = "ghes_edac";
126 mci->dev_name = "ghes";
127
128 csrow = mci->csrows[0];
129 dimm = csrow->channels[0]->dimm;
130
131 /* FIXME: FAKE DATA */
132 dimm->nr_pages = 1000;
133 dimm->grain = 128;
134 dimm->mtype = MEM_UNKNOWN;
135 dimm->dtype = DEV_UNKNOWN;
136 dimm->edac_mode = EDAC_SECDED;
137
138 rc = edac_mc_add_mc(mci);
139 if (rc < 0) {
140 pr_info(GHES_PFX "Can't register at EDAC core\n");
141 edac_mc_free(mci);
142 mutex_unlock(&ghes_edac_lock);
143 return -ENODEV;
144 }
145
146 ghes_edac_mc_num++;
147 mutex_unlock(&ghes_edac_lock);
148 return 0;
149}
150EXPORT_SYMBOL_GPL(ghes_edac_register);
151
152void ghes_edac_unregister(struct ghes *ghes)
153{
154 struct mem_ctl_info *mci;
155 struct ghes_edac_pvt *pvt;
156
157 list_for_each_entry(pvt, &ghes_reglist, list) {
158 if (ghes == pvt->ghes) {
159 mci = pvt->mci;
160 edac_mc_del_mc(mci->pdev);
161 edac_mc_free(mci);
162 list_del(&pvt->list);
163 }
164 }
165}
166EXPORT_SYMBOL_GPL(ghes_edac_unregister);