OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [edac/] [edac_pci.c] - Blame information for rev 62

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
2
 * EDAC PCI component
3
 *
4
 * Author: Dave Jiang <djiang@mvista.com>
5
 *
6
 * 2007 (c) MontaVista Software, Inc. This file is licensed under
7
 * the terms of the GNU General Public License version 2. This program
8
 * is licensed "as is" without any warranty of any kind, whether express
9
 * or implied.
10
 *
11
 */
12
#include <linux/module.h>
13
#include <linux/types.h>
14
#include <linux/smp.h>
15
#include <linux/init.h>
16
#include <linux/sysctl.h>
17
#include <linux/highmem.h>
18
#include <linux/timer.h>
19
#include <linux/slab.h>
20
#include <linux/spinlock.h>
21
#include <linux/list.h>
22
#include <linux/sysdev.h>
23
#include <linux/ctype.h>
24
#include <linux/workqueue.h>
25
#include <asm/uaccess.h>
26
#include <asm/page.h>
27
 
28
#include "edac_core.h"
29
#include "edac_module.h"
30
 
31
static DEFINE_MUTEX(edac_pci_ctls_mutex);
32
static struct list_head edac_pci_list = LIST_HEAD_INIT(edac_pci_list);
33
 
34
/*
35
 * edac_pci_alloc_ctl_info
36
 *
37
 *      The alloc() function for the 'edac_pci' control info
38
 *      structure. The chip driver will allocate one of these for each
39
 *      edac_pci it is going to control/register with the EDAC CORE.
40
 */
41
struct edac_pci_ctl_info *edac_pci_alloc_ctl_info(unsigned int sz_pvt,
42
                                                const char *edac_pci_name)
43
{
44
        struct edac_pci_ctl_info *pci;
45
        void *pvt;
46
        unsigned int size;
47
 
48
        debugf1("%s()\n", __func__);
49
 
50
        pci = (struct edac_pci_ctl_info *)0;
51
        pvt = edac_align_ptr(&pci[1], sz_pvt);
52
        size = ((unsigned long)pvt) + sz_pvt;
53
 
54
        /* Alloc the needed control struct memory */
55
        pci = kzalloc(size, GFP_KERNEL);
56
        if (pci  == NULL)
57
                return NULL;
58
 
59
        /* Now much private space */
60
        pvt = sz_pvt ? ((char *)pci) + ((unsigned long)pvt) : NULL;
61
 
62
        pci->pvt_info = pvt;
63
        pci->op_state = OP_ALLOC;
64
 
65
        snprintf(pci->name, strlen(edac_pci_name) + 1, "%s", edac_pci_name);
66
 
67
        return pci;
68
}
69
EXPORT_SYMBOL_GPL(edac_pci_alloc_ctl_info);
70
 
71
/*
72
 * edac_pci_free_ctl_info()
73
 *
74
 *      Last action on the pci control structure.
75
 *
76
 *      call the remove sysfs informaton, which will unregister
77
 *      this control struct's kobj. When that kobj's ref count
78
 *      goes to zero, its release function will be call and then
79
 *      kfree() the memory.
80
 */
81
void edac_pci_free_ctl_info(struct edac_pci_ctl_info *pci)
82
{
83
        debugf1("%s()\n", __func__);
84
 
85
        edac_pci_remove_sysfs(pci);
86
}
87
EXPORT_SYMBOL_GPL(edac_pci_free_ctl_info);
88
 
89
/*
90
 * find_edac_pci_by_dev()
91
 *      scans the edac_pci list for a specific 'struct device *'
92
 *
93
 *      return NULL if not found, or return control struct pointer
94
 */
95
static struct edac_pci_ctl_info *find_edac_pci_by_dev(struct device *dev)
96
{
97
        struct edac_pci_ctl_info *pci;
98
        struct list_head *item;
99
 
100
        debugf1("%s()\n", __func__);
101
 
102
        list_for_each(item, &edac_pci_list) {
103
                pci = list_entry(item, struct edac_pci_ctl_info, link);
104
 
105
                if (pci->dev == dev)
106
                        return pci;
107
        }
108
 
109
        return NULL;
110
}
111
 
112
/*
113
 * add_edac_pci_to_global_list
114
 *      Before calling this function, caller must assign a unique value to
115
 *      edac_dev->pci_idx.
116
 *      Return:
117
 *              0 on success
118
 *              1 on failure
119
 */
120
static int add_edac_pci_to_global_list(struct edac_pci_ctl_info *pci)
121
{
122
        struct list_head *item, *insert_before;
123
        struct edac_pci_ctl_info *rover;
124
 
125
        debugf1("%s()\n", __func__);
126
 
127
        insert_before = &edac_pci_list;
128
 
129
        /* Determine if already on the list */
130
        rover = find_edac_pci_by_dev(pci->dev);
131
        if (unlikely(rover != NULL))
132
                goto fail0;
133
 
134
        /* Insert in ascending order by 'pci_idx', so find position */
135
        list_for_each(item, &edac_pci_list) {
136
                rover = list_entry(item, struct edac_pci_ctl_info, link);
137
 
138
                if (rover->pci_idx >= pci->pci_idx) {
139
                        if (unlikely(rover->pci_idx == pci->pci_idx))
140
                                goto fail1;
141
 
142
                        insert_before = item;
143
                        break;
144
                }
145
        }
146
 
147
        list_add_tail_rcu(&pci->link, insert_before);
148
        return 0;
149
 
150
fail0:
151
        edac_printk(KERN_WARNING, EDAC_PCI,
152
                "%s (%s) %s %s already assigned %d\n",
153
                rover->dev->bus_id, dev_name(rover),
154
                rover->mod_name, rover->ctl_name, rover->pci_idx);
155
        return 1;
156
 
157
fail1:
158
        edac_printk(KERN_WARNING, EDAC_PCI,
159
                "but in low-level driver: attempt to assign\n"
160
                "\tduplicate pci_idx %d in %s()\n", rover->pci_idx,
161
                __func__);
162
        return 1;
163
}
164
 
165
/*
166
 * complete_edac_pci_list_del
167
 *
168
 *      RCU completion callback to indicate item is deleted
169
 */
170
static void complete_edac_pci_list_del(struct rcu_head *head)
171
{
172
        struct edac_pci_ctl_info *pci;
173
 
174
        pci = container_of(head, struct edac_pci_ctl_info, rcu);
175
        INIT_LIST_HEAD(&pci->link);
176
        complete(&pci->complete);
177
}
178
 
179
/*
180
 * del_edac_pci_from_global_list
181
 *
182
 *      remove the PCI control struct from the global list
183
 */
184
static void del_edac_pci_from_global_list(struct edac_pci_ctl_info *pci)
185
{
186
        list_del_rcu(&pci->link);
187
        init_completion(&pci->complete);
188
        call_rcu(&pci->rcu, complete_edac_pci_list_del);
189
        wait_for_completion(&pci->complete);
190
}
191
 
192
/*
193
 * edac_pci_find()
194
 *      Search for an edac_pci_ctl_info structure whose index is 'idx'
195
 *
196
 * If found, return a pointer to the structure
197
 * Else return NULL.
198
 *
199
 * Caller must hold pci_ctls_mutex.
200
 */
201
struct edac_pci_ctl_info *edac_pci_find(int idx)
202
{
203
        struct list_head *item;
204
        struct edac_pci_ctl_info *pci;
205
 
206
        /* Iterage over list, looking for exact match of ID */
207
        list_for_each(item, &edac_pci_list) {
208
                pci = list_entry(item, struct edac_pci_ctl_info, link);
209
 
210
                if (pci->pci_idx >= idx) {
211
                        if (pci->pci_idx == idx)
212
                                return pci;
213
 
214
                        /* not on list, so terminate early */
215
                        break;
216
                }
217
        }
218
 
219
        return NULL;
220
}
221
EXPORT_SYMBOL_GPL(edac_pci_find);
222
 
223
/*
224
 * edac_pci_workq_function()
225
 *
226
 *      periodic function that performs the operation
227
 *      scheduled by a workq request, for a given PCI control struct
228
 */
229
static void edac_pci_workq_function(struct work_struct *work_req)
230
{
231
        struct delayed_work *d_work = (struct delayed_work *)work_req;
232
        struct edac_pci_ctl_info *pci = to_edac_pci_ctl_work(d_work);
233
        int msec;
234
        unsigned long delay;
235
 
236
        debugf3("%s() checking\n", __func__);
237
 
238
        mutex_lock(&edac_pci_ctls_mutex);
239
 
240
        if (pci->op_state == OP_RUNNING_POLL) {
241
                /* we might be in POLL mode, but there may NOT be a poll func
242
                 */
243
                if ((pci->edac_check != NULL) && edac_pci_get_check_errors())
244
                        pci->edac_check(pci);
245
 
246
                /* if we are on a one second period, then use round */
247
                msec = edac_pci_get_poll_msec();
248
                if (msec == 1000)
249
                        delay = round_jiffies(msecs_to_jiffies(msec));
250
                else
251
                        delay = msecs_to_jiffies(msec);
252
 
253
                /* Reschedule only if we are in POLL mode */
254
                queue_delayed_work(edac_workqueue, &pci->work, delay);
255
        }
256
 
257
        mutex_unlock(&edac_pci_ctls_mutex);
258
}
259
 
260
/*
261
 * edac_pci_workq_setup()
262
 *      initialize a workq item for this edac_pci instance
263
 *      passing in the new delay period in msec
264
 *
265
 *      locking model:
266
 *              called when 'edac_pci_ctls_mutex' is locked
267
 */
268
static void edac_pci_workq_setup(struct edac_pci_ctl_info *pci,
269
                                 unsigned int msec)
270
{
271
        debugf0("%s()\n", __func__);
272
 
273
        INIT_DELAYED_WORK(&pci->work, edac_pci_workq_function);
274
        queue_delayed_work(edac_workqueue, &pci->work,
275
                        msecs_to_jiffies(edac_pci_get_poll_msec()));
276
}
277
 
278
/*
279
 * edac_pci_workq_teardown()
280
 *      stop the workq processing on this edac_pci instance
281
 */
282
static void edac_pci_workq_teardown(struct edac_pci_ctl_info *pci)
283
{
284
        int status;
285
 
286
        debugf0("%s()\n", __func__);
287
 
288
        status = cancel_delayed_work(&pci->work);
289
        if (status == 0)
290
                flush_workqueue(edac_workqueue);
291
}
292
 
293
/*
294
 * edac_pci_reset_delay_period
295
 *
296
 *      called with a new period value for the workq period
297
 *      a) stop current workq timer
298
 *      b) restart workq timer with new value
299
 */
300
void edac_pci_reset_delay_period(struct edac_pci_ctl_info *pci,
301
                                 unsigned long value)
302
{
303
        debugf0("%s()\n", __func__);
304
 
305
        edac_pci_workq_teardown(pci);
306
 
307
        /* need to lock for the setup */
308
        mutex_lock(&edac_pci_ctls_mutex);
309
 
310
        edac_pci_workq_setup(pci, value);
311
 
312
        mutex_unlock(&edac_pci_ctls_mutex);
313
}
314
EXPORT_SYMBOL_GPL(edac_pci_reset_delay_period);
315
 
316
/*
317
 * edac_pci_add_device: Insert the 'edac_dev' structure into the
318
 * edac_pci global list and create sysfs entries associated with
319
 * edac_pci structure.
320
 * @pci: pointer to the edac_device structure to be added to the list
321
 * @edac_idx: A unique numeric identifier to be assigned to the
322
 * 'edac_pci' structure.
323
 *
324
 * Return:
325
 *      0       Success
326
 *      !0      Failure
327
 */
328
int edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx)
329
{
330
        debugf0("%s()\n", __func__);
331
 
332
        pci->pci_idx = edac_idx;
333
        pci->start_time = jiffies;
334
 
335
        mutex_lock(&edac_pci_ctls_mutex);
336
 
337
        if (add_edac_pci_to_global_list(pci))
338
                goto fail0;
339
 
340
        if (edac_pci_create_sysfs(pci)) {
341
                edac_pci_printk(pci, KERN_WARNING,
342
                                "failed to create sysfs pci\n");
343
                goto fail1;
344
        }
345
 
346
        if (pci->edac_check != NULL) {
347
                pci->op_state = OP_RUNNING_POLL;
348
 
349
                edac_pci_workq_setup(pci, 1000);
350
        } else {
351
                pci->op_state = OP_RUNNING_INTERRUPT;
352
        }
353
 
354
        edac_pci_printk(pci, KERN_INFO,
355
                        "Giving out device to module '%s' controller '%s':"
356
                        " DEV '%s' (%s)\n",
357
                        pci->mod_name,
358
                        pci->ctl_name,
359
                        dev_name(pci), edac_op_state_to_string(pci->op_state));
360
 
361
        mutex_unlock(&edac_pci_ctls_mutex);
362
        return 0;
363
 
364
        /* error unwind stack */
365
fail1:
366
        del_edac_pci_from_global_list(pci);
367
fail0:
368
        mutex_unlock(&edac_pci_ctls_mutex);
369
        return 1;
370
}
371
EXPORT_SYMBOL_GPL(edac_pci_add_device);
372
 
373
/*
374
 * edac_pci_del_device()
375
 *      Remove sysfs entries for specified edac_pci structure and
376
 *      then remove edac_pci structure from global list
377
 *
378
 * @dev:
379
 *      Pointer to 'struct device' representing edac_pci structure
380
 *      to remove
381
 *
382
 * Return:
383
 *      Pointer to removed edac_pci structure,
384
 *      or NULL if device not found
385
 */
386
struct edac_pci_ctl_info *edac_pci_del_device(struct device *dev)
387
{
388
        struct edac_pci_ctl_info *pci;
389
 
390
        debugf0("%s()\n", __func__);
391
 
392
        mutex_lock(&edac_pci_ctls_mutex);
393
 
394
        /* ensure the control struct is on the global list
395
         * if not, then leave
396
         */
397
        pci = find_edac_pci_by_dev(dev);
398
        if (pci  == NULL) {
399
                mutex_unlock(&edac_pci_ctls_mutex);
400
                return NULL;
401
        }
402
 
403
        pci->op_state = OP_OFFLINE;
404
 
405
        del_edac_pci_from_global_list(pci);
406
 
407
        mutex_unlock(&edac_pci_ctls_mutex);
408
 
409
        /* stop the workq timer */
410
        edac_pci_workq_teardown(pci);
411
 
412
        edac_printk(KERN_INFO, EDAC_PCI,
413
                "Removed device %d for %s %s: DEV %s\n",
414
                pci->pci_idx, pci->mod_name, pci->ctl_name, dev_name(pci));
415
 
416
        return pci;
417
}
418
EXPORT_SYMBOL_GPL(edac_pci_del_device);
419
 
420
/*
421
 * edac_pci_generic_check
422
 *
423
 *      a Generic parity check API
424
 */
425
void edac_pci_generic_check(struct edac_pci_ctl_info *pci)
426
{
427
        debugf4("%s()\n", __func__);
428
        edac_pci_do_parity_check();
429
}
430
 
431
/* free running instance index counter */
432
static int edac_pci_idx;
433
#define EDAC_PCI_GENCTL_NAME    "EDAC PCI controller"
434
 
435
struct edac_pci_gen_data {
436
        int edac_idx;
437
};
438
 
439
/*
440
 * edac_pci_create_generic_ctl
441
 *
442
 *      A generic constructor for a PCI parity polling device
443
 *      Some systems have more than one domain of PCI busses.
444
 *      For systems with one domain, then this API will
445
 *      provide for a generic poller.
446
 *
447
 *      This routine calls the edac_pci_alloc_ctl_info() for
448
 *      the generic device, with default values
449
 */
450
struct edac_pci_ctl_info *edac_pci_create_generic_ctl(struct device *dev,
451
                                                const char *mod_name)
452
{
453
        struct edac_pci_ctl_info *pci;
454
        struct edac_pci_gen_data *pdata;
455
 
456
        pci = edac_pci_alloc_ctl_info(sizeof(*pdata), EDAC_PCI_GENCTL_NAME);
457
        if (!pci)
458
                return NULL;
459
 
460
        pdata = pci->pvt_info;
461
        pci->dev = dev;
462
        dev_set_drvdata(pci->dev, pci);
463
        pci->dev_name = pci_name(to_pci_dev(dev));
464
 
465
        pci->mod_name = mod_name;
466
        pci->ctl_name = EDAC_PCI_GENCTL_NAME;
467
        pci->edac_check = edac_pci_generic_check;
468
 
469
        pdata->edac_idx = edac_pci_idx++;
470
 
471
        if (edac_pci_add_device(pci, pdata->edac_idx) > 0) {
472
                debugf3("%s(): failed edac_pci_add_device()\n", __func__);
473
                edac_pci_free_ctl_info(pci);
474
                return NULL;
475
        }
476
 
477
        return pci;
478
}
479
EXPORT_SYMBOL_GPL(edac_pci_create_generic_ctl);
480
 
481
/*
482
 * edac_pci_release_generic_ctl
483
 *
484
 *      The release function of a generic EDAC PCI polling device
485
 */
486
void edac_pci_release_generic_ctl(struct edac_pci_ctl_info *pci)
487
{
488
        debugf0("%s() pci mod=%s\n", __func__, pci->mod_name);
489
 
490
        edac_pci_del_device(pci->dev);
491
        edac_pci_free_ctl_info(pci);
492
}
493
EXPORT_SYMBOL_GPL(edac_pci_release_generic_ctl);

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.