xref: /openbmc/linux/drivers/edac/edac_pci.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1*3bb16560SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
291b99041SDave Jiang /*
391b99041SDave Jiang  * EDAC PCI component
491b99041SDave Jiang  *
591b99041SDave Jiang  * Author: Dave Jiang <djiang@mvista.com>
691b99041SDave Jiang  *
7*3bb16560SThomas Gleixner  * 2007 (c) MontaVista Software, Inc.
891b99041SDave Jiang  */
991b99041SDave Jiang #include <asm/page.h>
107c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
110b892c71SMauro Carvalho Chehab #include <linux/ctype.h>
120b892c71SMauro Carvalho Chehab #include <linux/highmem.h>
130b892c71SMauro Carvalho Chehab #include <linux/init.h>
140b892c71SMauro Carvalho Chehab #include <linux/module.h>
150b892c71SMauro Carvalho Chehab #include <linux/slab.h>
160b892c71SMauro Carvalho Chehab #include <linux/smp.h>
170b892c71SMauro Carvalho Chehab #include <linux/spinlock.h>
180b892c71SMauro Carvalho Chehab #include <linux/sysctl.h>
190b892c71SMauro Carvalho Chehab #include <linux/timer.h>
2091b99041SDave Jiang 
210b892c71SMauro Carvalho Chehab #include "edac_pci.h"
2291b99041SDave Jiang #include "edac_module.h"
2391b99041SDave Jiang 
2491b99041SDave Jiang static DEFINE_MUTEX(edac_pci_ctls_mutex);
25ff6ac2a6SRobert P. J. Day static LIST_HEAD(edac_pci_list);
268641a384SHarry Ciao static atomic_t pci_indexes = ATOMIC_INIT(0);
2791b99041SDave Jiang 
edac_pci_alloc_ctl_info(unsigned int sz_pvt,const char * edac_pci_name)28079708b9SDouglas Thompson struct edac_pci_ctl_info *edac_pci_alloc_ctl_info(unsigned int sz_pvt,
2991b99041SDave Jiang 						  const char *edac_pci_name)
3091b99041SDave Jiang {
3191b99041SDave Jiang 	struct edac_pci_ctl_info *pci;
3291b99041SDave Jiang 
33956b9ba1SJoe Perches 	edac_dbg(1, "\n");
34d4c1465bSDoug Thompson 
35fb8cd45cSBorislav Petkov 	pci = kzalloc(sizeof(struct edac_pci_ctl_info), GFP_KERNEL);
36fb8cd45cSBorislav Petkov 	if (!pci)
3791b99041SDave Jiang 		return NULL;
3891b99041SDave Jiang 
39fb8cd45cSBorislav Petkov 	if (sz_pvt) {
40fb8cd45cSBorislav Petkov 		pci->pvt_info = kzalloc(sz_pvt, GFP_KERNEL);
41fb8cd45cSBorislav Petkov 		if (!pci->pvt_info)
42fb8cd45cSBorislav Petkov 			goto free;
43fb8cd45cSBorislav Petkov 	}
4491b99041SDave Jiang 
4591b99041SDave Jiang 	pci->op_state = OP_ALLOC;
4691b99041SDave Jiang 
4791b99041SDave Jiang 	snprintf(pci->name, strlen(edac_pci_name) + 1, "%s", edac_pci_name);
4891b99041SDave Jiang 
4991b99041SDave Jiang 	return pci;
50fb8cd45cSBorislav Petkov 
51fb8cd45cSBorislav Petkov free:
52fb8cd45cSBorislav Petkov 	kfree(pci);
53fb8cd45cSBorislav Petkov 	return NULL;
5491b99041SDave Jiang }
5591b99041SDave Jiang EXPORT_SYMBOL_GPL(edac_pci_alloc_ctl_info);
5691b99041SDave Jiang 
edac_pci_free_ctl_info(struct edac_pci_ctl_info * pci)5791b99041SDave Jiang void edac_pci_free_ctl_info(struct edac_pci_ctl_info *pci)
5891b99041SDave Jiang {
59956b9ba1SJoe Perches 	edac_dbg(1, "\n");
60079708b9SDouglas Thompson 
61d4c1465bSDoug Thompson 	edac_pci_remove_sysfs(pci);
62d4c1465bSDoug Thompson }
6391b99041SDave Jiang EXPORT_SYMBOL_GPL(edac_pci_free_ctl_info);
6491b99041SDave Jiang 
6591b99041SDave Jiang /*
6691b99041SDave Jiang  * find_edac_pci_by_dev()
6791b99041SDave Jiang  * 	scans the edac_pci list for a specific 'struct device *'
68d4c1465bSDoug Thompson  *
69d4c1465bSDoug Thompson  *	return NULL if not found, or return control struct pointer
7091b99041SDave Jiang  */
find_edac_pci_by_dev(struct device * dev)7191b99041SDave Jiang static struct edac_pci_ctl_info *find_edac_pci_by_dev(struct device *dev)
7291b99041SDave Jiang {
7391b99041SDave Jiang 	struct edac_pci_ctl_info *pci;
7491b99041SDave Jiang 	struct list_head *item;
7591b99041SDave Jiang 
76956b9ba1SJoe Perches 	edac_dbg(1, "\n");
7791b99041SDave Jiang 
7891b99041SDave Jiang 	list_for_each(item, &edac_pci_list) {
7991b99041SDave Jiang 		pci = list_entry(item, struct edac_pci_ctl_info, link);
8091b99041SDave Jiang 
8191b99041SDave Jiang 		if (pci->dev == dev)
8291b99041SDave Jiang 			return pci;
8391b99041SDave Jiang 	}
8491b99041SDave Jiang 
8591b99041SDave Jiang 	return NULL;
8691b99041SDave Jiang }
8791b99041SDave Jiang 
8891b99041SDave Jiang /*
8991b99041SDave Jiang  * add_edac_pci_to_global_list
9091b99041SDave Jiang  * 	Before calling this function, caller must assign a unique value to
9191b99041SDave Jiang  * 	edac_dev->pci_idx.
9291b99041SDave Jiang  * 	Return:
9391b99041SDave Jiang  * 		0 on success
9491b99041SDave Jiang  * 		1 on failure
9591b99041SDave Jiang  */
add_edac_pci_to_global_list(struct edac_pci_ctl_info * pci)9691b99041SDave Jiang static int add_edac_pci_to_global_list(struct edac_pci_ctl_info *pci)
9791b99041SDave Jiang {
9891b99041SDave Jiang 	struct list_head *item, *insert_before;
9991b99041SDave Jiang 	struct edac_pci_ctl_info *rover;
10091b99041SDave Jiang 
101956b9ba1SJoe Perches 	edac_dbg(1, "\n");
102d4c1465bSDoug Thompson 
10391b99041SDave Jiang 	insert_before = &edac_pci_list;
10491b99041SDave Jiang 
10591b99041SDave Jiang 	/* Determine if already on the list */
106d4c1465bSDoug Thompson 	rover = find_edac_pci_by_dev(pci->dev);
107d4c1465bSDoug Thompson 	if (unlikely(rover != NULL))
10891b99041SDave Jiang 		goto fail0;
10991b99041SDave Jiang 
11091b99041SDave Jiang 	/* Insert in ascending order by 'pci_idx', so find position */
11191b99041SDave Jiang 	list_for_each(item, &edac_pci_list) {
11291b99041SDave Jiang 		rover = list_entry(item, struct edac_pci_ctl_info, link);
11391b99041SDave Jiang 
11491b99041SDave Jiang 		if (rover->pci_idx >= pci->pci_idx) {
11591b99041SDave Jiang 			if (unlikely(rover->pci_idx == pci->pci_idx))
11691b99041SDave Jiang 				goto fail1;
11791b99041SDave Jiang 
11891b99041SDave Jiang 			insert_before = item;
11991b99041SDave Jiang 			break;
12091b99041SDave Jiang 		}
12191b99041SDave Jiang 	}
12291b99041SDave Jiang 
12391b99041SDave Jiang 	list_add_tail_rcu(&pci->link, insert_before);
12491b99041SDave Jiang 	return 0;
12591b99041SDave Jiang 
12691b99041SDave Jiang fail0:
12791b99041SDave Jiang 	edac_printk(KERN_WARNING, EDAC_PCI,
12891b99041SDave Jiang 		"%s (%s) %s %s already assigned %d\n",
129281efb17SKay Sievers 		dev_name(rover->dev), edac_dev_name(rover),
13091b99041SDave Jiang 		rover->mod_name, rover->ctl_name, rover->pci_idx);
13191b99041SDave Jiang 	return 1;
13291b99041SDave Jiang 
13391b99041SDave Jiang fail1:
13491b99041SDave Jiang 	edac_printk(KERN_WARNING, EDAC_PCI,
13591b99041SDave Jiang 		"but in low-level driver: attempt to assign\n"
136079708b9SDouglas Thompson 		"\tduplicate pci_idx %d in %s()\n", rover->pci_idx,
137079708b9SDouglas Thompson 		__func__);
13891b99041SDave Jiang 	return 1;
13991b99041SDave Jiang }
14091b99041SDave Jiang 
14191b99041SDave Jiang /*
14291b99041SDave Jiang  * del_edac_pci_from_global_list
143d4c1465bSDoug Thompson  *
144d4c1465bSDoug Thompson  *	remove the PCI control struct from the global list
14591b99041SDave Jiang  */
del_edac_pci_from_global_list(struct edac_pci_ctl_info * pci)14691b99041SDave Jiang static void del_edac_pci_from_global_list(struct edac_pci_ctl_info *pci)
14791b99041SDave Jiang {
14891b99041SDave Jiang 	list_del_rcu(&pci->link);
149e2e77098SLai Jiangshan 
150e2e77098SLai Jiangshan 	/* these are for safe removal of devices from global list while
151e2e77098SLai Jiangshan 	 * NMI handlers may be traversing list
152e2e77098SLai Jiangshan 	 */
153e2e77098SLai Jiangshan 	synchronize_rcu();
154e2e77098SLai Jiangshan 	INIT_LIST_HEAD(&pci->link);
15591b99041SDave Jiang }
15691b99041SDave Jiang 
15791b99041SDave Jiang /*
15891b99041SDave Jiang  * edac_pci_workq_function()
159d4c1465bSDoug Thompson  *
160d4c1465bSDoug Thompson  * 	periodic function that performs the operation
161d4c1465bSDoug Thompson  *	scheduled by a workq request, for a given PCI control struct
16291b99041SDave Jiang  */
edac_pci_workq_function(struct work_struct * work_req)16391b99041SDave Jiang static void edac_pci_workq_function(struct work_struct *work_req)
16491b99041SDave Jiang {
165fbeb4384SJean Delvare 	struct delayed_work *d_work = to_delayed_work(work_req);
16691b99041SDave Jiang 	struct edac_pci_ctl_info *pci = to_edac_pci_ctl_work(d_work);
167d4c1465bSDoug Thompson 	int msec;
168d4c1465bSDoug Thompson 	unsigned long delay;
16991b99041SDave Jiang 
170956b9ba1SJoe Perches 	edac_dbg(3, "checking\n");
17191b99041SDave Jiang 
172d4c1465bSDoug Thompson 	mutex_lock(&edac_pci_ctls_mutex);
173d4c1465bSDoug Thompson 
17406e912d4SBorislav Petkov 	if (pci->op_state != OP_RUNNING_POLL) {
17506e912d4SBorislav Petkov 		mutex_unlock(&edac_pci_ctls_mutex);
17606e912d4SBorislav Petkov 		return;
17706e912d4SBorislav Petkov 	}
17806e912d4SBorislav Petkov 
17906e912d4SBorislav Petkov 	if (edac_pci_get_check_errors())
18091b99041SDave Jiang 		pci->edac_check(pci);
18191b99041SDave Jiang 
182d4c1465bSDoug Thompson 	/* if we are on a one second period, then use round */
183d4c1465bSDoug Thompson 	msec = edac_pci_get_poll_msec();
184d4c1465bSDoug Thompson 	if (msec == 1000)
185c2ae24cfSAnton Blanchard 		delay = round_jiffies_relative(msecs_to_jiffies(msec));
186d4c1465bSDoug Thompson 	else
187d4c1465bSDoug Thompson 		delay = msecs_to_jiffies(msec);
18891b99041SDave Jiang 
189c4cf3b45SBorislav Petkov 	edac_queue_work(&pci->work, delay);
190d4c1465bSDoug Thompson 
191d4c1465bSDoug Thompson 	mutex_unlock(&edac_pci_ctls_mutex);
19291b99041SDave Jiang }
19391b99041SDave Jiang 
edac_pci_alloc_index(void)1948641a384SHarry Ciao int edac_pci_alloc_index(void)
1958641a384SHarry Ciao {
1968641a384SHarry Ciao 	return atomic_inc_return(&pci_indexes) - 1;
1978641a384SHarry Ciao }
1988641a384SHarry Ciao EXPORT_SYMBOL_GPL(edac_pci_alloc_index);
1998641a384SHarry Ciao 
edac_pci_add_device(struct edac_pci_ctl_info * pci,int edac_idx)20091b99041SDave Jiang int edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx)
20191b99041SDave Jiang {
202956b9ba1SJoe Perches 	edac_dbg(0, "\n");
20391b99041SDave Jiang 
20491b99041SDave Jiang 	pci->pci_idx = edac_idx;
205d4c1465bSDoug Thompson 	pci->start_time = jiffies;
20691b99041SDave Jiang 
207d4c1465bSDoug Thompson 	mutex_lock(&edac_pci_ctls_mutex);
20891b99041SDave Jiang 
20991b99041SDave Jiang 	if (add_edac_pci_to_global_list(pci))
21091b99041SDave Jiang 		goto fail0;
21191b99041SDave Jiang 
21291b99041SDave Jiang 	if (edac_pci_create_sysfs(pci)) {
21391b99041SDave Jiang 		edac_pci_printk(pci, KERN_WARNING,
21491b99041SDave Jiang 				"failed to create sysfs pci\n");
21591b99041SDave Jiang 		goto fail1;
21691b99041SDave Jiang 	}
21791b99041SDave Jiang 
21809667606SBorislav Petkov 	if (pci->edac_check) {
21991b99041SDave Jiang 		pci->op_state = OP_RUNNING_POLL;
22091b99041SDave Jiang 
221626a7a4dSBorislav Petkov 		INIT_DELAYED_WORK(&pci->work, edac_pci_workq_function);
222626a7a4dSBorislav Petkov 		edac_queue_work(&pci->work, msecs_to_jiffies(edac_pci_get_poll_msec()));
223626a7a4dSBorislav Petkov 
22491b99041SDave Jiang 	} else {
22591b99041SDave Jiang 		pci->op_state = OP_RUNNING_INTERRUPT;
22691b99041SDave Jiang 	}
22791b99041SDave Jiang 
22891b99041SDave Jiang 	edac_pci_printk(pci, KERN_INFO,
2297270a608SRobert Richter 		"Giving out device to module %s controller %s: DEV %s (%s)\n",
2307270a608SRobert Richter 		pci->mod_name, pci->ctl_name, pci->dev_name,
2317270a608SRobert Richter 		edac_op_state_to_string(pci->op_state));
23291b99041SDave Jiang 
233d4c1465bSDoug Thompson 	mutex_unlock(&edac_pci_ctls_mutex);
23491b99041SDave Jiang 	return 0;
23591b99041SDave Jiang 
236d4c1465bSDoug Thompson 	/* error unwind stack */
23791b99041SDave Jiang fail1:
23891b99041SDave Jiang 	del_edac_pci_from_global_list(pci);
23991b99041SDave Jiang fail0:
240d4c1465bSDoug Thompson 	mutex_unlock(&edac_pci_ctls_mutex);
24191b99041SDave Jiang 	return 1;
24291b99041SDave Jiang }
24391b99041SDave Jiang EXPORT_SYMBOL_GPL(edac_pci_add_device);
24491b99041SDave Jiang 
edac_pci_del_device(struct device * dev)24591b99041SDave Jiang struct edac_pci_ctl_info *edac_pci_del_device(struct device *dev)
24691b99041SDave Jiang {
24791b99041SDave Jiang 	struct edac_pci_ctl_info *pci;
24891b99041SDave Jiang 
249956b9ba1SJoe Perches 	edac_dbg(0, "\n");
25091b99041SDave Jiang 
251d4c1465bSDoug Thompson 	mutex_lock(&edac_pci_ctls_mutex);
25291b99041SDave Jiang 
253d4c1465bSDoug Thompson 	/* ensure the control struct is on the global list
254d4c1465bSDoug Thompson 	 * if not, then leave
255d4c1465bSDoug Thompson 	 */
256d4c1465bSDoug Thompson 	pci = find_edac_pci_by_dev(dev);
257d4c1465bSDoug Thompson 	if (pci  == NULL) {
258d4c1465bSDoug Thompson 		mutex_unlock(&edac_pci_ctls_mutex);
25991b99041SDave Jiang 		return NULL;
26091b99041SDave Jiang 	}
26191b99041SDave Jiang 
26291b99041SDave Jiang 	pci->op_state = OP_OFFLINE;
26391b99041SDave Jiang 
26491b99041SDave Jiang 	del_edac_pci_from_global_list(pci);
26591b99041SDave Jiang 
266d4c1465bSDoug Thompson 	mutex_unlock(&edac_pci_ctls_mutex);
267d4c1465bSDoug Thompson 
26809667606SBorislav Petkov 	if (pci->edac_check)
269626a7a4dSBorislav Petkov 		edac_stop_work(&pci->work);
27091b99041SDave Jiang 
27191b99041SDave Jiang 	edac_printk(KERN_INFO, EDAC_PCI,
27291b99041SDave Jiang 		"Removed device %d for %s %s: DEV %s\n",
27317aa7e03SStephen Rothwell 		pci->pci_idx, pci->mod_name, pci->ctl_name, edac_dev_name(pci));
27491b99041SDave Jiang 
27591b99041SDave Jiang 	return pci;
27691b99041SDave Jiang }
27791b99041SDave Jiang EXPORT_SYMBOL_GPL(edac_pci_del_device);
27891b99041SDave Jiang 
279d4c1465bSDoug Thompson /*
280d4c1465bSDoug Thompson  * edac_pci_generic_check
281d4c1465bSDoug Thompson  *
282d4c1465bSDoug Thompson  *	a Generic parity check API
283d4c1465bSDoug Thompson  */
edac_pci_generic_check(struct edac_pci_ctl_info * pci)2841a45027dSAdrian Bunk static void edac_pci_generic_check(struct edac_pci_ctl_info *pci)
28591b99041SDave Jiang {
286956b9ba1SJoe Perches 	edac_dbg(4, "\n");
28791b99041SDave Jiang 	edac_pci_do_parity_check();
28891b99041SDave Jiang }
28991b99041SDave Jiang 
290d4c1465bSDoug Thompson /* free running instance index counter */
291f044091cSDouglas Thompson static int edac_pci_idx;
29291b99041SDave Jiang #define EDAC_PCI_GENCTL_NAME	"EDAC PCI controller"
29391b99041SDave Jiang 
29491b99041SDave Jiang struct edac_pci_gen_data {
29591b99041SDave Jiang 	int edac_idx;
29691b99041SDave Jiang };
29791b99041SDave Jiang 
edac_pci_create_generic_ctl(struct device * dev,const char * mod_name)298079708b9SDouglas Thompson struct edac_pci_ctl_info *edac_pci_create_generic_ctl(struct device *dev,
299079708b9SDouglas Thompson 						const char *mod_name)
30091b99041SDave Jiang {
30191b99041SDave Jiang 	struct edac_pci_ctl_info *pci;
30291b99041SDave Jiang 	struct edac_pci_gen_data *pdata;
30391b99041SDave Jiang 
30491b99041SDave Jiang 	pci = edac_pci_alloc_ctl_info(sizeof(*pdata), EDAC_PCI_GENCTL_NAME);
30591b99041SDave Jiang 	if (!pci)
30691b99041SDave Jiang 		return NULL;
30791b99041SDave Jiang 
30891b99041SDave Jiang 	pdata = pci->pvt_info;
30991b99041SDave Jiang 	pci->dev = dev;
31091b99041SDave Jiang 	dev_set_drvdata(pci->dev, pci);
31191b99041SDave Jiang 	pci->dev_name = pci_name(to_pci_dev(dev));
31291b99041SDave Jiang 
31391b99041SDave Jiang 	pci->mod_name = mod_name;
31491b99041SDave Jiang 	pci->ctl_name = EDAC_PCI_GENCTL_NAME;
315876bb331SBorislav Petkov 	if (edac_op_state == EDAC_OPSTATE_POLL)
31691b99041SDave Jiang 		pci->edac_check = edac_pci_generic_check;
31791b99041SDave Jiang 
31891b99041SDave Jiang 	pdata->edac_idx = edac_pci_idx++;
31991b99041SDave Jiang 
32091b99041SDave Jiang 	if (edac_pci_add_device(pci, pdata->edac_idx) > 0) {
321956b9ba1SJoe Perches 		edac_dbg(3, "failed edac_pci_add_device()\n");
32291b99041SDave Jiang 		edac_pci_free_ctl_info(pci);
32391b99041SDave Jiang 		return NULL;
32491b99041SDave Jiang 	}
32591b99041SDave Jiang 
32691b99041SDave Jiang 	return pci;
32791b99041SDave Jiang }
32891b99041SDave Jiang EXPORT_SYMBOL_GPL(edac_pci_create_generic_ctl);
32991b99041SDave Jiang 
edac_pci_release_generic_ctl(struct edac_pci_ctl_info * pci)33091b99041SDave Jiang void edac_pci_release_generic_ctl(struct edac_pci_ctl_info *pci)
33191b99041SDave Jiang {
332956b9ba1SJoe Perches 	edac_dbg(0, "pci mod=%s\n", pci->mod_name);
333d4c1465bSDoug Thompson 
33491b99041SDave Jiang 	edac_pci_del_device(pci->dev);
33591b99041SDave Jiang 	edac_pci_free_ctl_info(pci);
33691b99041SDave Jiang }
33791b99041SDave Jiang EXPORT_SYMBOL_GPL(edac_pci_release_generic_ctl);
338