Blame view

drivers/edac/edac_pci.c 7.62 KB
91b99041c   Dave Jiang   drivers/edac: upd...
1
2
3
4
5
6
7
8
9
10
11
  /*
   * EDAC PCI component
   *
   * Author: Dave Jiang <djiang@mvista.com>
   *
   * 2007 (c) MontaVista Software, Inc. This file is licensed under
   * the terms of the GNU General Public License version 2. This program
   * is licensed "as is" without any warranty of any kind, whether express
   * or implied.
   *
   */
0b892c717   Mauro Carvalho Chehab   edac: move EDAC P...
12
  #include <asm/page.h>
7c0f6ba68   Linus Torvalds   Replace <asm/uacc...
13
  #include <linux/uaccess.h>
0b892c717   Mauro Carvalho Chehab   edac: move EDAC P...
14
15
16
  #include <linux/ctype.h>
  #include <linux/highmem.h>
  #include <linux/init.h>
91b99041c   Dave Jiang   drivers/edac: upd...
17
  #include <linux/module.h>
0b892c717   Mauro Carvalho Chehab   edac: move EDAC P...
18
  #include <linux/slab.h>
91b99041c   Dave Jiang   drivers/edac: upd...
19
  #include <linux/smp.h>
0b892c717   Mauro Carvalho Chehab   edac: move EDAC P...
20
  #include <linux/spinlock.h>
91b99041c   Dave Jiang   drivers/edac: upd...
21
  #include <linux/sysctl.h>
91b99041c   Dave Jiang   drivers/edac: upd...
22
  #include <linux/timer.h>
91b99041c   Dave Jiang   drivers/edac: upd...
23

0b892c717   Mauro Carvalho Chehab   edac: move EDAC P...
24
  #include "edac_pci.h"
91b99041c   Dave Jiang   drivers/edac: upd...
25
26
27
  #include "edac_module.h"
  
  static DEFINE_MUTEX(edac_pci_ctls_mutex);
ff6ac2a61   Robert P. J. Day   edac: use the sho...
28
  static LIST_HEAD(edac_pci_list);
8641a3845   Harry Ciao   edac: Add edac_pc...
29
  static atomic_t pci_indexes = ATOMIC_INIT(0);
91b99041c   Dave Jiang   drivers/edac: upd...
30

079708b91   Douglas Thompson   drivers/edac: cor...
31
  struct edac_pci_ctl_info *edac_pci_alloc_ctl_info(unsigned int sz_pvt,
052dfb45c   Douglas Thompson   drivers/edac: cle...
32
  						const char *edac_pci_name)
91b99041c   Dave Jiang   drivers/edac: upd...
33
34
  {
  	struct edac_pci_ctl_info *pci;
93e4fe64e   Mauro Carvalho Chehab   edac: rewrite eda...
35
  	void *p = NULL, *pvt;
91b99041c   Dave Jiang   drivers/edac: upd...
36
  	unsigned int size;
956b9ba15   Joe Perches   edac: Convert deb...
37
38
  	edac_dbg(1, "
  ");
d4c1465b7   Doug Thompson   drivers/edac: fix...
39

93e4fe64e   Mauro Carvalho Chehab   edac: rewrite eda...
40
41
  	pci = edac_align_ptr(&p, sizeof(*pci), 1);
  	pvt = edac_align_ptr(&p, 1, sz_pvt);
91b99041c   Dave Jiang   drivers/edac: upd...
42
  	size = ((unsigned long)pvt) + sz_pvt;
d4c1465b7   Doug Thompson   drivers/edac: fix...
43
44
45
  	/* Alloc the needed control struct memory */
  	pci = kzalloc(size, GFP_KERNEL);
  	if (pci  == NULL)
91b99041c   Dave Jiang   drivers/edac: upd...
46
  		return NULL;
d4c1465b7   Doug Thompson   drivers/edac: fix...
47
  	/* Now much private space */
91b99041c   Dave Jiang   drivers/edac: upd...
48
49
50
  	pvt = sz_pvt ? ((char *)pci) + ((unsigned long)pvt) : NULL;
  
  	pci->pvt_info = pvt;
91b99041c   Dave Jiang   drivers/edac: upd...
51
  	pci->op_state = OP_ALLOC;
079708b91   Douglas Thompson   drivers/edac: cor...
52
  	snprintf(pci->name, strlen(edac_pci_name) + 1, "%s", edac_pci_name);
91b99041c   Dave Jiang   drivers/edac: upd...
53
54
55
56
  
  	return pci;
  }
  EXPORT_SYMBOL_GPL(edac_pci_alloc_ctl_info);
91b99041c   Dave Jiang   drivers/edac: upd...
57
58
  void edac_pci_free_ctl_info(struct edac_pci_ctl_info *pci)
  {
956b9ba15   Joe Perches   edac: Convert deb...
59
60
  	edac_dbg(1, "
  ");
079708b91   Douglas Thompson   drivers/edac: cor...
61

d4c1465b7   Doug Thompson   drivers/edac: fix...
62
63
  	edac_pci_remove_sysfs(pci);
  }
91b99041c   Dave Jiang   drivers/edac: upd...
64
65
66
67
68
  EXPORT_SYMBOL_GPL(edac_pci_free_ctl_info);
  
  /*
   * find_edac_pci_by_dev()
   * 	scans the edac_pci list for a specific 'struct device *'
d4c1465b7   Doug Thompson   drivers/edac: fix...
69
70
   *
   *	return NULL if not found, or return control struct pointer
91b99041c   Dave Jiang   drivers/edac: upd...
71
   */
079708b91   Douglas Thompson   drivers/edac: cor...
72
  static struct edac_pci_ctl_info *find_edac_pci_by_dev(struct device *dev)
91b99041c   Dave Jiang   drivers/edac: upd...
73
74
75
  {
  	struct edac_pci_ctl_info *pci;
  	struct list_head *item;
956b9ba15   Joe Perches   edac: Convert deb...
76
77
  	edac_dbg(1, "
  ");
91b99041c   Dave Jiang   drivers/edac: upd...
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
  
  	list_for_each(item, &edac_pci_list) {
  		pci = list_entry(item, struct edac_pci_ctl_info, link);
  
  		if (pci->dev == dev)
  			return pci;
  	}
  
  	return NULL;
  }
  
  /*
   * add_edac_pci_to_global_list
   * 	Before calling this function, caller must assign a unique value to
   * 	edac_dev->pci_idx.
   * 	Return:
   * 		0 on success
   * 		1 on failure
   */
  static int add_edac_pci_to_global_list(struct edac_pci_ctl_info *pci)
  {
  	struct list_head *item, *insert_before;
  	struct edac_pci_ctl_info *rover;
956b9ba15   Joe Perches   edac: Convert deb...
101
102
  	edac_dbg(1, "
  ");
d4c1465b7   Doug Thompson   drivers/edac: fix...
103

91b99041c   Dave Jiang   drivers/edac: upd...
104
105
106
  	insert_before = &edac_pci_list;
  
  	/* Determine if already on the list */
d4c1465b7   Doug Thompson   drivers/edac: fix...
107
108
  	rover = find_edac_pci_by_dev(pci->dev);
  	if (unlikely(rover != NULL))
91b99041c   Dave Jiang   drivers/edac: upd...
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
  		goto fail0;
  
  	/* Insert in ascending order by 'pci_idx', so find position */
  	list_for_each(item, &edac_pci_list) {
  		rover = list_entry(item, struct edac_pci_ctl_info, link);
  
  		if (rover->pci_idx >= pci->pci_idx) {
  			if (unlikely(rover->pci_idx == pci->pci_idx))
  				goto fail1;
  
  			insert_before = item;
  			break;
  		}
  	}
  
  	list_add_tail_rcu(&pci->link, insert_before);
  	return 0;
052dfb45c   Douglas Thompson   drivers/edac: cle...
126
  fail0:
91b99041c   Dave Jiang   drivers/edac: upd...
127
  	edac_printk(KERN_WARNING, EDAC_PCI,
052dfb45c   Douglas Thompson   drivers/edac: cle...
128
129
  		"%s (%s) %s %s already assigned %d
  ",
281efb17d   Kay Sievers   edac: struct devi...
130
  		dev_name(rover->dev), edac_dev_name(rover),
052dfb45c   Douglas Thompson   drivers/edac: cle...
131
  		rover->mod_name, rover->ctl_name, rover->pci_idx);
91b99041c   Dave Jiang   drivers/edac: upd...
132
  	return 1;
052dfb45c   Douglas Thompson   drivers/edac: cle...
133
  fail1:
91b99041c   Dave Jiang   drivers/edac: upd...
134
  	edac_printk(KERN_WARNING, EDAC_PCI,
052dfb45c   Douglas Thompson   drivers/edac: cle...
135
136
137
138
139
  		"but in low-level driver: attempt to assign
  "
  		"\tduplicate pci_idx %d in %s()
  ", rover->pci_idx,
  		__func__);
91b99041c   Dave Jiang   drivers/edac: upd...
140
141
142
143
  	return 1;
  }
  
  /*
91b99041c   Dave Jiang   drivers/edac: upd...
144
   * del_edac_pci_from_global_list
d4c1465b7   Doug Thompson   drivers/edac: fix...
145
146
   *
   *	remove the PCI control struct from the global list
91b99041c   Dave Jiang   drivers/edac: upd...
147
148
149
150
   */
  static void del_edac_pci_from_global_list(struct edac_pci_ctl_info *pci)
  {
  	list_del_rcu(&pci->link);
e2e770987   Lai Jiangshan   edac,rcu: use syn...
151
152
153
154
155
156
  
  	/* these are for safe removal of devices from global list while
  	 * NMI handlers may be traversing list
  	 */
  	synchronize_rcu();
  	INIT_LIST_HEAD(&pci->link);
91b99041c   Dave Jiang   drivers/edac: upd...
157
  }
91b99041c   Dave Jiang   drivers/edac: upd...
158
159
  /*
   * edac_pci_workq_function()
d4c1465b7   Doug Thompson   drivers/edac: fix...
160
161
162
   *
   * 	periodic function that performs the operation
   *	scheduled by a workq request, for a given PCI control struct
91b99041c   Dave Jiang   drivers/edac: upd...
163
   */
91b99041c   Dave Jiang   drivers/edac: upd...
164
165
  static void edac_pci_workq_function(struct work_struct *work_req)
  {
fbeb43847   Jean Delvare   edac: use to_dela...
166
  	struct delayed_work *d_work = to_delayed_work(work_req);
91b99041c   Dave Jiang   drivers/edac: upd...
167
  	struct edac_pci_ctl_info *pci = to_edac_pci_ctl_work(d_work);
d4c1465b7   Doug Thompson   drivers/edac: fix...
168
169
  	int msec;
  	unsigned long delay;
91b99041c   Dave Jiang   drivers/edac: upd...
170

956b9ba15   Joe Perches   edac: Convert deb...
171
172
  	edac_dbg(3, "checking
  ");
91b99041c   Dave Jiang   drivers/edac: upd...
173

d4c1465b7   Doug Thompson   drivers/edac: fix...
174
  	mutex_lock(&edac_pci_ctls_mutex);
91b99041c   Dave Jiang   drivers/edac: upd...
175

06e912d4d   Borislav Petkov   EDAC: Cleanup/syn...
176
177
178
  	if (pci->op_state != OP_RUNNING_POLL) {
  		mutex_unlock(&edac_pci_ctls_mutex);
  		return;
d4c1465b7   Doug Thompson   drivers/edac: fix...
179
  	}
91b99041c   Dave Jiang   drivers/edac: upd...
180

06e912d4d   Borislav Petkov   EDAC: Cleanup/syn...
181
182
183
184
185
186
187
188
189
190
191
  	if (edac_pci_get_check_errors())
  		pci->edac_check(pci);
  
  	/* if we are on a one second period, then use round */
  	msec = edac_pci_get_poll_msec();
  	if (msec == 1000)
  		delay = round_jiffies_relative(msecs_to_jiffies(msec));
  	else
  		delay = msecs_to_jiffies(msec);
  
  	edac_queue_work(&pci->work, delay);
d4c1465b7   Doug Thompson   drivers/edac: fix...
192
  	mutex_unlock(&edac_pci_ctls_mutex);
91b99041c   Dave Jiang   drivers/edac: upd...
193
  }
8641a3845   Harry Ciao   edac: Add edac_pc...
194
195
196
197
198
  int edac_pci_alloc_index(void)
  {
  	return atomic_inc_return(&pci_indexes) - 1;
  }
  EXPORT_SYMBOL_GPL(edac_pci_alloc_index);
91b99041c   Dave Jiang   drivers/edac: upd...
199
200
  int edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx)
  {
956b9ba15   Joe Perches   edac: Convert deb...
201
202
  	edac_dbg(0, "
  ");
91b99041c   Dave Jiang   drivers/edac: upd...
203
204
  
  	pci->pci_idx = edac_idx;
d4c1465b7   Doug Thompson   drivers/edac: fix...
205
  	pci->start_time = jiffies;
91b99041c   Dave Jiang   drivers/edac: upd...
206

d4c1465b7   Doug Thompson   drivers/edac: fix...
207
  	mutex_lock(&edac_pci_ctls_mutex);
91b99041c   Dave Jiang   drivers/edac: upd...
208
209
210
  
  	if (add_edac_pci_to_global_list(pci))
  		goto fail0;
91b99041c   Dave Jiang   drivers/edac: upd...
211
212
213
214
215
216
  	if (edac_pci_create_sysfs(pci)) {
  		edac_pci_printk(pci, KERN_WARNING,
  				"failed to create sysfs pci
  ");
  		goto fail1;
  	}
096676061   Borislav Petkov   EDAC: Balance wor...
217
  	if (pci->edac_check) {
91b99041c   Dave Jiang   drivers/edac: upd...
218
  		pci->op_state = OP_RUNNING_POLL;
626a7a4db   Borislav Petkov   EDAC: Kill workqu...
219
220
  		INIT_DELAYED_WORK(&pci->work, edac_pci_workq_function);
  		edac_queue_work(&pci->work, msecs_to_jiffies(edac_pci_get_poll_msec()));
91b99041c   Dave Jiang   drivers/edac: upd...
221
222
223
224
225
  	} else {
  		pci->op_state = OP_RUNNING_INTERRUPT;
  	}
  
  	edac_pci_printk(pci, KERN_INFO,
7270a6085   Robert Richter   edac: Unify repor...
226
227
228
229
  		"Giving out device to module %s controller %s: DEV %s (%s)
  ",
  		pci->mod_name, pci->ctl_name, pci->dev_name,
  		edac_op_state_to_string(pci->op_state));
91b99041c   Dave Jiang   drivers/edac: upd...
230

d4c1465b7   Doug Thompson   drivers/edac: fix...
231
  	mutex_unlock(&edac_pci_ctls_mutex);
91b99041c   Dave Jiang   drivers/edac: upd...
232
  	return 0;
d4c1465b7   Doug Thompson   drivers/edac: fix...
233
  	/* error unwind stack */
052dfb45c   Douglas Thompson   drivers/edac: cle...
234
  fail1:
91b99041c   Dave Jiang   drivers/edac: upd...
235
  	del_edac_pci_from_global_list(pci);
052dfb45c   Douglas Thompson   drivers/edac: cle...
236
  fail0:
d4c1465b7   Doug Thompson   drivers/edac: fix...
237
  	mutex_unlock(&edac_pci_ctls_mutex);
91b99041c   Dave Jiang   drivers/edac: upd...
238
239
240
  	return 1;
  }
  EXPORT_SYMBOL_GPL(edac_pci_add_device);
079708b91   Douglas Thompson   drivers/edac: cor...
241
  struct edac_pci_ctl_info *edac_pci_del_device(struct device *dev)
91b99041c   Dave Jiang   drivers/edac: upd...
242
243
  {
  	struct edac_pci_ctl_info *pci;
956b9ba15   Joe Perches   edac: Convert deb...
244
245
  	edac_dbg(0, "
  ");
91b99041c   Dave Jiang   drivers/edac: upd...
246

d4c1465b7   Doug Thompson   drivers/edac: fix...
247
  	mutex_lock(&edac_pci_ctls_mutex);
91b99041c   Dave Jiang   drivers/edac: upd...
248

d4c1465b7   Doug Thompson   drivers/edac: fix...
249
250
251
252
253
254
  	/* ensure the control struct is on the global list
  	 * if not, then leave
  	 */
  	pci = find_edac_pci_by_dev(dev);
  	if (pci  == NULL) {
  		mutex_unlock(&edac_pci_ctls_mutex);
91b99041c   Dave Jiang   drivers/edac: upd...
255
256
257
258
  		return NULL;
  	}
  
  	pci->op_state = OP_OFFLINE;
91b99041c   Dave Jiang   drivers/edac: upd...
259
  	del_edac_pci_from_global_list(pci);
d4c1465b7   Doug Thompson   drivers/edac: fix...
260
  	mutex_unlock(&edac_pci_ctls_mutex);
096676061   Borislav Petkov   EDAC: Balance wor...
261
  	if (pci->edac_check)
626a7a4db   Borislav Petkov   EDAC: Kill workqu...
262
  		edac_stop_work(&pci->work);
91b99041c   Dave Jiang   drivers/edac: upd...
263
264
  
  	edac_printk(KERN_INFO, EDAC_PCI,
052dfb45c   Douglas Thompson   drivers/edac: cle...
265
266
  		"Removed device %d for %s %s: DEV %s
  ",
17aa7e034   Stephen Rothwell   dev_name introduc...
267
  		pci->pci_idx, pci->mod_name, pci->ctl_name, edac_dev_name(pci));
91b99041c   Dave Jiang   drivers/edac: upd...
268
269
270
271
  
  	return pci;
  }
  EXPORT_SYMBOL_GPL(edac_pci_del_device);
d4c1465b7   Doug Thompson   drivers/edac: fix...
272
273
274
275
276
  /*
   * edac_pci_generic_check
   *
   *	a Generic parity check API
   */
1a45027d1   Adrian Bunk   edac: remove unne...
277
  static void edac_pci_generic_check(struct edac_pci_ctl_info *pci)
91b99041c   Dave Jiang   drivers/edac: upd...
278
  {
956b9ba15   Joe Perches   edac: Convert deb...
279
280
  	edac_dbg(4, "
  ");
91b99041c   Dave Jiang   drivers/edac: upd...
281
282
  	edac_pci_do_parity_check();
  }
d4c1465b7   Doug Thompson   drivers/edac: fix...
283
  /* free running instance index counter */
f044091ca   Douglas Thompson   drivers/edac: rem...
284
  static int edac_pci_idx;
91b99041c   Dave Jiang   drivers/edac: upd...
285
286
287
288
289
  #define EDAC_PCI_GENCTL_NAME	"EDAC PCI controller"
  
  struct edac_pci_gen_data {
  	int edac_idx;
  };
079708b91   Douglas Thompson   drivers/edac: cor...
290
  struct edac_pci_ctl_info *edac_pci_create_generic_ctl(struct device *dev,
052dfb45c   Douglas Thompson   drivers/edac: cle...
291
  						const char *mod_name)
91b99041c   Dave Jiang   drivers/edac: upd...
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
  {
  	struct edac_pci_ctl_info *pci;
  	struct edac_pci_gen_data *pdata;
  
  	pci = edac_pci_alloc_ctl_info(sizeof(*pdata), EDAC_PCI_GENCTL_NAME);
  	if (!pci)
  		return NULL;
  
  	pdata = pci->pvt_info;
  	pci->dev = dev;
  	dev_set_drvdata(pci->dev, pci);
  	pci->dev_name = pci_name(to_pci_dev(dev));
  
  	pci->mod_name = mod_name;
  	pci->ctl_name = EDAC_PCI_GENCTL_NAME;
876bb331e   Borislav Petkov   EDAC: Respect ope...
307
308
  	if (edac_op_state == EDAC_OPSTATE_POLL)
  		pci->edac_check = edac_pci_generic_check;
91b99041c   Dave Jiang   drivers/edac: upd...
309
310
311
312
  
  	pdata->edac_idx = edac_pci_idx++;
  
  	if (edac_pci_add_device(pci, pdata->edac_idx) > 0) {
956b9ba15   Joe Perches   edac: Convert deb...
313
314
  		edac_dbg(3, "failed edac_pci_add_device()
  ");
91b99041c   Dave Jiang   drivers/edac: upd...
315
316
317
318
319
320
321
322
323
324
  		edac_pci_free_ctl_info(pci);
  		return NULL;
  	}
  
  	return pci;
  }
  EXPORT_SYMBOL_GPL(edac_pci_create_generic_ctl);
  
  void edac_pci_release_generic_ctl(struct edac_pci_ctl_info *pci)
  {
956b9ba15   Joe Perches   edac: Convert deb...
325
326
  	edac_dbg(0, "pci mod=%s
  ", pci->mod_name);
d4c1465b7   Doug Thompson   drivers/edac: fix...
327

91b99041c   Dave Jiang   drivers/edac: upd...
328
329
330
331
  	edac_pci_del_device(pci->dev);
  	edac_pci_free_ctl_info(pci);
  }
  EXPORT_SYMBOL_GPL(edac_pci_release_generic_ctl);