18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * EDAC PCI component
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Author: Dave Jiang <djiang@mvista.com>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * 2007 (c) MontaVista Software, Inc. This file is licensed under
78c2ecf20Sopenharmony_ci * the terms of the GNU General Public License version 2. This program
88c2ecf20Sopenharmony_ci * is licensed "as is" without any warranty of any kind, whether express
98c2ecf20Sopenharmony_ci * or implied.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci#include <asm/page.h>
138c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
148c2ecf20Sopenharmony_ci#include <linux/ctype.h>
158c2ecf20Sopenharmony_ci#include <linux/highmem.h>
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/slab.h>
198c2ecf20Sopenharmony_ci#include <linux/smp.h>
208c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
218c2ecf20Sopenharmony_ci#include <linux/sysctl.h>
228c2ecf20Sopenharmony_ci#include <linux/timer.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "edac_pci.h"
258c2ecf20Sopenharmony_ci#include "edac_module.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(edac_pci_ctls_mutex);
288c2ecf20Sopenharmony_cistatic LIST_HEAD(edac_pci_list);
298c2ecf20Sopenharmony_cistatic atomic_t pci_indexes = ATOMIC_INIT(0);
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistruct edac_pci_ctl_info *edac_pci_alloc_ctl_info(unsigned int sz_pvt,
328c2ecf20Sopenharmony_ci						const char *edac_pci_name)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	struct edac_pci_ctl_info *pci;
358c2ecf20Sopenharmony_ci	void *p = NULL, *pvt;
368c2ecf20Sopenharmony_ci	unsigned int size;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	edac_dbg(1, "\n");
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	pci = edac_align_ptr(&p, sizeof(*pci), 1);
418c2ecf20Sopenharmony_ci	pvt = edac_align_ptr(&p, 1, sz_pvt);
428c2ecf20Sopenharmony_ci	size = ((unsigned long)pvt) + sz_pvt;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	/* Alloc the needed control struct memory */
458c2ecf20Sopenharmony_ci	pci = kzalloc(size, GFP_KERNEL);
468c2ecf20Sopenharmony_ci	if (pci  == NULL)
478c2ecf20Sopenharmony_ci		return NULL;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	/* Now much private space */
508c2ecf20Sopenharmony_ci	pvt = sz_pvt ? ((char *)pci) + ((unsigned long)pvt) : NULL;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	pci->pvt_info = pvt;
538c2ecf20Sopenharmony_ci	pci->op_state = OP_ALLOC;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	snprintf(pci->name, strlen(edac_pci_name) + 1, "%s", edac_pci_name);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	return pci;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_pci_alloc_ctl_info);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_civoid edac_pci_free_ctl_info(struct edac_pci_ctl_info *pci)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	edac_dbg(1, "\n");
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	edac_pci_remove_sysfs(pci);
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_pci_free_ctl_info);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/*
708c2ecf20Sopenharmony_ci * find_edac_pci_by_dev()
718c2ecf20Sopenharmony_ci * 	scans the edac_pci list for a specific 'struct device *'
728c2ecf20Sopenharmony_ci *
738c2ecf20Sopenharmony_ci *	return NULL if not found, or return control struct pointer
748c2ecf20Sopenharmony_ci */
758c2ecf20Sopenharmony_cistatic struct edac_pci_ctl_info *find_edac_pci_by_dev(struct device *dev)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	struct edac_pci_ctl_info *pci;
788c2ecf20Sopenharmony_ci	struct list_head *item;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	edac_dbg(1, "\n");
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	list_for_each(item, &edac_pci_list) {
838c2ecf20Sopenharmony_ci		pci = list_entry(item, struct edac_pci_ctl_info, link);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci		if (pci->dev == dev)
868c2ecf20Sopenharmony_ci			return pci;
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	return NULL;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/*
938c2ecf20Sopenharmony_ci * add_edac_pci_to_global_list
948c2ecf20Sopenharmony_ci * 	Before calling this function, caller must assign a unique value to
958c2ecf20Sopenharmony_ci * 	edac_dev->pci_idx.
968c2ecf20Sopenharmony_ci * 	Return:
978c2ecf20Sopenharmony_ci * 		0 on success
988c2ecf20Sopenharmony_ci * 		1 on failure
998c2ecf20Sopenharmony_ci */
1008c2ecf20Sopenharmony_cistatic int add_edac_pci_to_global_list(struct edac_pci_ctl_info *pci)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	struct list_head *item, *insert_before;
1038c2ecf20Sopenharmony_ci	struct edac_pci_ctl_info *rover;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	edac_dbg(1, "\n");
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	insert_before = &edac_pci_list;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	/* Determine if already on the list */
1108c2ecf20Sopenharmony_ci	rover = find_edac_pci_by_dev(pci->dev);
1118c2ecf20Sopenharmony_ci	if (unlikely(rover != NULL))
1128c2ecf20Sopenharmony_ci		goto fail0;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	/* Insert in ascending order by 'pci_idx', so find position */
1158c2ecf20Sopenharmony_ci	list_for_each(item, &edac_pci_list) {
1168c2ecf20Sopenharmony_ci		rover = list_entry(item, struct edac_pci_ctl_info, link);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci		if (rover->pci_idx >= pci->pci_idx) {
1198c2ecf20Sopenharmony_ci			if (unlikely(rover->pci_idx == pci->pci_idx))
1208c2ecf20Sopenharmony_ci				goto fail1;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci			insert_before = item;
1238c2ecf20Sopenharmony_ci			break;
1248c2ecf20Sopenharmony_ci		}
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	list_add_tail_rcu(&pci->link, insert_before);
1288c2ecf20Sopenharmony_ci	return 0;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cifail0:
1318c2ecf20Sopenharmony_ci	edac_printk(KERN_WARNING, EDAC_PCI,
1328c2ecf20Sopenharmony_ci		"%s (%s) %s %s already assigned %d\n",
1338c2ecf20Sopenharmony_ci		dev_name(rover->dev), edac_dev_name(rover),
1348c2ecf20Sopenharmony_ci		rover->mod_name, rover->ctl_name, rover->pci_idx);
1358c2ecf20Sopenharmony_ci	return 1;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cifail1:
1388c2ecf20Sopenharmony_ci	edac_printk(KERN_WARNING, EDAC_PCI,
1398c2ecf20Sopenharmony_ci		"but in low-level driver: attempt to assign\n"
1408c2ecf20Sopenharmony_ci		"\tduplicate pci_idx %d in %s()\n", rover->pci_idx,
1418c2ecf20Sopenharmony_ci		__func__);
1428c2ecf20Sopenharmony_ci	return 1;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci/*
1468c2ecf20Sopenharmony_ci * del_edac_pci_from_global_list
1478c2ecf20Sopenharmony_ci *
1488c2ecf20Sopenharmony_ci *	remove the PCI control struct from the global list
1498c2ecf20Sopenharmony_ci */
1508c2ecf20Sopenharmony_cistatic void del_edac_pci_from_global_list(struct edac_pci_ctl_info *pci)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	list_del_rcu(&pci->link);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	/* these are for safe removal of devices from global list while
1558c2ecf20Sopenharmony_ci	 * NMI handlers may be traversing list
1568c2ecf20Sopenharmony_ci	 */
1578c2ecf20Sopenharmony_ci	synchronize_rcu();
1588c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&pci->link);
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci/*
1628c2ecf20Sopenharmony_ci * edac_pci_workq_function()
1638c2ecf20Sopenharmony_ci *
1648c2ecf20Sopenharmony_ci * 	periodic function that performs the operation
1658c2ecf20Sopenharmony_ci *	scheduled by a workq request, for a given PCI control struct
1668c2ecf20Sopenharmony_ci */
1678c2ecf20Sopenharmony_cistatic void edac_pci_workq_function(struct work_struct *work_req)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	struct delayed_work *d_work = to_delayed_work(work_req);
1708c2ecf20Sopenharmony_ci	struct edac_pci_ctl_info *pci = to_edac_pci_ctl_work(d_work);
1718c2ecf20Sopenharmony_ci	int msec;
1728c2ecf20Sopenharmony_ci	unsigned long delay;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	edac_dbg(3, "checking\n");
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	mutex_lock(&edac_pci_ctls_mutex);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	if (pci->op_state != OP_RUNNING_POLL) {
1798c2ecf20Sopenharmony_ci		mutex_unlock(&edac_pci_ctls_mutex);
1808c2ecf20Sopenharmony_ci		return;
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (edac_pci_get_check_errors())
1848c2ecf20Sopenharmony_ci		pci->edac_check(pci);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	/* if we are on a one second period, then use round */
1878c2ecf20Sopenharmony_ci	msec = edac_pci_get_poll_msec();
1888c2ecf20Sopenharmony_ci	if (msec == 1000)
1898c2ecf20Sopenharmony_ci		delay = round_jiffies_relative(msecs_to_jiffies(msec));
1908c2ecf20Sopenharmony_ci	else
1918c2ecf20Sopenharmony_ci		delay = msecs_to_jiffies(msec);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	edac_queue_work(&pci->work, delay);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	mutex_unlock(&edac_pci_ctls_mutex);
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ciint edac_pci_alloc_index(void)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	return atomic_inc_return(&pci_indexes) - 1;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_pci_alloc_index);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ciint edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	edac_dbg(0, "\n");
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	pci->pci_idx = edac_idx;
2098c2ecf20Sopenharmony_ci	pci->start_time = jiffies;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	mutex_lock(&edac_pci_ctls_mutex);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	if (add_edac_pci_to_global_list(pci))
2148c2ecf20Sopenharmony_ci		goto fail0;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (edac_pci_create_sysfs(pci)) {
2178c2ecf20Sopenharmony_ci		edac_pci_printk(pci, KERN_WARNING,
2188c2ecf20Sopenharmony_ci				"failed to create sysfs pci\n");
2198c2ecf20Sopenharmony_ci		goto fail1;
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	if (pci->edac_check) {
2238c2ecf20Sopenharmony_ci		pci->op_state = OP_RUNNING_POLL;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci		INIT_DELAYED_WORK(&pci->work, edac_pci_workq_function);
2268c2ecf20Sopenharmony_ci		edac_queue_work(&pci->work, msecs_to_jiffies(edac_pci_get_poll_msec()));
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	} else {
2298c2ecf20Sopenharmony_ci		pci->op_state = OP_RUNNING_INTERRUPT;
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	edac_pci_printk(pci, KERN_INFO,
2338c2ecf20Sopenharmony_ci		"Giving out device to module %s controller %s: DEV %s (%s)\n",
2348c2ecf20Sopenharmony_ci		pci->mod_name, pci->ctl_name, pci->dev_name,
2358c2ecf20Sopenharmony_ci		edac_op_state_to_string(pci->op_state));
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	mutex_unlock(&edac_pci_ctls_mutex);
2388c2ecf20Sopenharmony_ci	return 0;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	/* error unwind stack */
2418c2ecf20Sopenharmony_cifail1:
2428c2ecf20Sopenharmony_ci	del_edac_pci_from_global_list(pci);
2438c2ecf20Sopenharmony_cifail0:
2448c2ecf20Sopenharmony_ci	mutex_unlock(&edac_pci_ctls_mutex);
2458c2ecf20Sopenharmony_ci	return 1;
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_pci_add_device);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistruct edac_pci_ctl_info *edac_pci_del_device(struct device *dev)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	struct edac_pci_ctl_info *pci;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	edac_dbg(0, "\n");
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	mutex_lock(&edac_pci_ctls_mutex);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	/* ensure the control struct is on the global list
2588c2ecf20Sopenharmony_ci	 * if not, then leave
2598c2ecf20Sopenharmony_ci	 */
2608c2ecf20Sopenharmony_ci	pci = find_edac_pci_by_dev(dev);
2618c2ecf20Sopenharmony_ci	if (pci  == NULL) {
2628c2ecf20Sopenharmony_ci		mutex_unlock(&edac_pci_ctls_mutex);
2638c2ecf20Sopenharmony_ci		return NULL;
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	pci->op_state = OP_OFFLINE;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	del_edac_pci_from_global_list(pci);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	mutex_unlock(&edac_pci_ctls_mutex);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	if (pci->edac_check)
2738c2ecf20Sopenharmony_ci		edac_stop_work(&pci->work);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	edac_printk(KERN_INFO, EDAC_PCI,
2768c2ecf20Sopenharmony_ci		"Removed device %d for %s %s: DEV %s\n",
2778c2ecf20Sopenharmony_ci		pci->pci_idx, pci->mod_name, pci->ctl_name, edac_dev_name(pci));
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	return pci;
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_pci_del_device);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci/*
2848c2ecf20Sopenharmony_ci * edac_pci_generic_check
2858c2ecf20Sopenharmony_ci *
2868c2ecf20Sopenharmony_ci *	a Generic parity check API
2878c2ecf20Sopenharmony_ci */
2888c2ecf20Sopenharmony_cistatic void edac_pci_generic_check(struct edac_pci_ctl_info *pci)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	edac_dbg(4, "\n");
2918c2ecf20Sopenharmony_ci	edac_pci_do_parity_check();
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci/* free running instance index counter */
2958c2ecf20Sopenharmony_cistatic int edac_pci_idx;
2968c2ecf20Sopenharmony_ci#define EDAC_PCI_GENCTL_NAME	"EDAC PCI controller"
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistruct edac_pci_gen_data {
2998c2ecf20Sopenharmony_ci	int edac_idx;
3008c2ecf20Sopenharmony_ci};
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistruct edac_pci_ctl_info *edac_pci_create_generic_ctl(struct device *dev,
3038c2ecf20Sopenharmony_ci						const char *mod_name)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	struct edac_pci_ctl_info *pci;
3068c2ecf20Sopenharmony_ci	struct edac_pci_gen_data *pdata;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	pci = edac_pci_alloc_ctl_info(sizeof(*pdata), EDAC_PCI_GENCTL_NAME);
3098c2ecf20Sopenharmony_ci	if (!pci)
3108c2ecf20Sopenharmony_ci		return NULL;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	pdata = pci->pvt_info;
3138c2ecf20Sopenharmony_ci	pci->dev = dev;
3148c2ecf20Sopenharmony_ci	dev_set_drvdata(pci->dev, pci);
3158c2ecf20Sopenharmony_ci	pci->dev_name = pci_name(to_pci_dev(dev));
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	pci->mod_name = mod_name;
3188c2ecf20Sopenharmony_ci	pci->ctl_name = EDAC_PCI_GENCTL_NAME;
3198c2ecf20Sopenharmony_ci	if (edac_op_state == EDAC_OPSTATE_POLL)
3208c2ecf20Sopenharmony_ci		pci->edac_check = edac_pci_generic_check;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	pdata->edac_idx = edac_pci_idx++;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	if (edac_pci_add_device(pci, pdata->edac_idx) > 0) {
3258c2ecf20Sopenharmony_ci		edac_dbg(3, "failed edac_pci_add_device()\n");
3268c2ecf20Sopenharmony_ci		edac_pci_free_ctl_info(pci);
3278c2ecf20Sopenharmony_ci		return NULL;
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	return pci;
3318c2ecf20Sopenharmony_ci}
3328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_pci_create_generic_ctl);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_civoid edac_pci_release_generic_ctl(struct edac_pci_ctl_info *pci)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	edac_dbg(0, "pci mod=%s\n", pci->mod_name);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	edac_pci_del_device(pci->dev);
3398c2ecf20Sopenharmony_ci	edac_pci_free_ctl_info(pci);
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_pci_release_generic_ctl);
342