aboutsummaryrefslogtreecommitdiffstats
path: root/meta-elisa/recipes-kernel/linux/files
diff options
context:
space:
mode:
authorJan-Simon Moeller <jsmoeller@linuxfoundation.org>2020-04-21 20:29:16 +0200
committerJan-Simon Moeller <jsmoeller@linuxfoundation.org>2020-04-21 20:29:16 +0200
commita12a235e206e091a6bfdee261ca142b70d683b34 (patch)
tree866360c8e158bd56cfbbd7fcbaac32912a4aa6ab /meta-elisa/recipes-kernel/linux/files
parent33ecec083876b8fc1484f36bdf5938b26bfb5799 (diff)
RFC/WIP: Add edac-inject-modulesandbox/jsmoeller/edac
Signed-off-by: Jan-Simon Moeller <jsmoeller@linuxfoundation.org> Change-Id: Ice3c4f9e6ce1d0b6b30fd4d21546fc5aec99ac06
Diffstat (limited to 'meta-elisa/recipes-kernel/linux/files')
-rw-r--r--meta-elisa/recipes-kernel/linux/files/Makefile12
-rw-r--r--meta-elisa/recipes-kernel/linux/files/edac_device.h352
-rw-r--r--meta-elisa/recipes-kernel/linux/files/edac_inject.c357
-rw-r--r--meta-elisa/recipes-kernel/linux/files/edac_inject.rst43
-rw-r--r--meta-elisa/recipes-kernel/linux/files/edac_mc.h258
-rw-r--r--meta-elisa/recipes-kernel/linux/files/edac_module.h130
-rw-r--r--meta-elisa/recipes-kernel/linux/files/edac_pci.h271
7 files changed, 1423 insertions, 0 deletions
diff --git a/meta-elisa/recipes-kernel/linux/files/Makefile b/meta-elisa/recipes-kernel/linux/files/Makefile
new file mode 100644
index 00000000..017577c7
--- /dev/null
+++ b/meta-elisa/recipes-kernel/linux/files/Makefile
@@ -0,0 +1,12 @@
+obj-m += edac_inject.o
+
+export KDIR := /lib/modules/$(shell uname -r)/build
+
+allofit: modules
+modules:
+ @$(MAKE) -C $(KDIR) M=$(shell pwd) modules
+kernel_clean:
+ @$(MAKE) -C $(KDIR) M=$(shell pwd) clean
+
+clean: kernel_clean
+ rm -rf Module.symvers modules.order
diff --git a/meta-elisa/recipes-kernel/linux/files/edac_device.h b/meta-elisa/recipes-kernel/linux/files/edac_device.h
new file mode 100644
index 00000000..c4c0e0bd
--- /dev/null
+++ b/meta-elisa/recipes-kernel/linux/files/edac_device.h
@@ -0,0 +1,352 @@
+/*
+ * Defines, structures, APIs for edac_device
+ *
+ * (C) 2007 Linux Networx (http://lnxi.com)
+ * This file may be distributed under the terms of the
+ * GNU General Public License.
+ *
+ * Written by Thayne Harbaugh
+ * Based on work by Dan Hollis <goemon at anime dot net> and others.
+ * http://www.anime.net/~goemon/linux-ecc/
+ *
+ * NMI handling support added by
+ * Dave Peterson <dsp@llnl.gov> <dave_peterson@pobox.com>
+ *
+ * Refactored for multi-source files:
+ * Doug Thompson <norsk5@xmission.com>
+ *
+ * Please look at Documentation/driver-api/edac.rst for more info about
+ * EDAC core structs and functions.
+ */
+
+#ifndef _EDAC_DEVICE_H_
+#define _EDAC_DEVICE_H_
+
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/edac.h>
+#include <linux/kobject.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/sysfs.h>
+#include <linux/workqueue.h>
+
+
+/*
+ * The following are the structures to provide for a generic
+ * or abstract 'edac_device'. This set of structures and the
+ * code that implements the APIs for the same, provide for
+ * registering EDAC type devices which are NOT standard memory.
+ *
+ * CPU caches (L1 and L2)
+ * DMA engines
+ * Core CPU switches
+ * Fabric switch units
+ * PCIe interface controllers
+ * other EDAC/ECC type devices that can be monitored for
+ * errors, etc.
+ *
+ * It allows for a 2 level set of hierarchy. For example:
+ *
+ * cache could be composed of L1, L2 and L3 levels of cache.
+ * Each CPU core would have its own L1 cache, while sharing
+ * L2 and maybe L3 caches.
+ *
+ * View them arranged, via the sysfs presentation:
+ * /sys/devices/system/edac/..
+ *
+ * mc/ <existing memory device directory>
+ * cpu/cpu0/.. <L1 and L2 block directory>
+ * /L1-cache/ce_count
+ * /ue_count
+ * /L2-cache/ce_count
+ * /ue_count
+ * cpu/cpu1/.. <L1 and L2 block directory>
+ * /L1-cache/ce_count
+ * /ue_count
+ * /L2-cache/ce_count
+ * /ue_count
+ * ...
+ *
+ * the L1 and L2 directories would be "edac_device_block's"
+ */
+
+struct edac_device_counter {
+ u32 ue_count;
+ u32 ce_count;
+};
+
+/* forward reference */
+struct edac_device_ctl_info;
+struct edac_device_block;
+
+/* edac_dev_sysfs_attribute structure
+ * used for driver sysfs attributes in mem_ctl_info
+ * for extra controls and attributes:
+ * like high level error Injection controls
+ */
+struct edac_dev_sysfs_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct edac_device_ctl_info *, char *);
+ ssize_t (*store)(struct edac_device_ctl_info *, const char *, size_t);
+};
+
+/* edac_dev_sysfs_block_attribute structure
+ *
+ * used in leaf 'block' nodes for adding controls/attributes
+ *
+ * each block in each instance of the containing control structure
+ * can have an array of the following. The show and store functions
+ * will be filled in with the show/store function in the
+ * low level driver.
+ *
+ * The 'value' field will be the actual value field used for
+ * counting
+ */
+struct edac_dev_sysfs_block_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct kobject *, struct attribute *, char *);
+ ssize_t (*store)(struct kobject *, struct attribute *,
+ const char *, size_t);
+ struct edac_device_block *block;
+
+ unsigned int value;
+};
+
+/* device block control structure */
+struct edac_device_block {
+ struct edac_device_instance *instance; /* Up Pointer */
+ char name[EDAC_DEVICE_NAME_LEN + 1];
+
+ struct edac_device_counter counters; /* basic UE and CE counters */
+
+ int nr_attribs; /* how many attributes */
+
+ /* this block's attributes, could be NULL */
+ struct edac_dev_sysfs_block_attribute *block_attributes;
+
+ /* edac sysfs device control */
+ struct kobject kobj;
+};
+
+/* device instance control structure */
+struct edac_device_instance {
+ struct edac_device_ctl_info *ctl; /* Up pointer */
+ char name[EDAC_DEVICE_NAME_LEN + 4];
+
+ struct edac_device_counter counters; /* instance counters */
+
+ u32 nr_blocks; /* how many blocks */
+ struct edac_device_block *blocks; /* block array */
+
+ /* edac sysfs device control */
+ struct kobject kobj;
+};
+
+
+/*
+ * Abstract edac_device control info structure
+ *
+ */
+struct edac_device_ctl_info {
+ /* for global list of edac_device_ctl_info structs */
+ struct list_head link;
+
+ struct module *owner; /* Module owner of this control struct */
+
+ int dev_idx;
+
+ /* Per instance controls for this edac_device */
+ int log_ue; /* boolean for logging UEs */
+ int log_ce; /* boolean for logging CEs */
+ int panic_on_ue; /* boolean for panic'ing on an UE */
+ unsigned poll_msec; /* number of milliseconds to poll interval */
+ unsigned long delay; /* number of jiffies for poll_msec */
+
+ /* Additional top controller level attributes, but specified
+ * by the low level driver.
+ *
+ * Set by the low level driver to provide attributes at the
+ * controller level, same level as 'ue_count' and 'ce_count' above.
+ * An array of structures, NULL terminated
+ *
+ * If attributes are desired, then set to array of attributes
+ * If no attributes are desired, leave NULL
+ */
+ struct edac_dev_sysfs_attribute *sysfs_attributes;
+
+ /* pointer to main 'edac' subsys in sysfs */
+ struct bus_type *edac_subsys;
+
+ /* the internal state of this controller instance */
+ int op_state;
+ /* work struct for this instance */
+ struct delayed_work work;
+
+ /* pointer to edac polling checking routine:
+ * If NOT NULL: points to polling check routine
+ * If NULL: Then assumes INTERRUPT operation, where
+ * MC driver will receive events
+ */
+ void (*edac_check) (struct edac_device_ctl_info * edac_dev);
+
+ struct device *dev; /* pointer to device structure */
+
+ const char *mod_name; /* module name */
+ const char *ctl_name; /* edac controller name */
+ const char *dev_name; /* pci/platform/etc... name */
+
+ void *pvt_info; /* pointer to 'private driver' info */
+
+ unsigned long start_time; /* edac_device load start time (jiffies) */
+
+ struct completion removal_complete;
+
+ /* sysfs top name under 'edac' directory
+ * and instance name:
+ * cpu/cpu0/...
+ * cpu/cpu1/...
+ * cpu/cpu2/...
+ * ...
+ */
+ char name[EDAC_DEVICE_NAME_LEN + 1];
+
+ /* Number of instances supported on this control structure
+ * and the array of those instances
+ */
+ u32 nr_instances;
+ struct edac_device_instance *instances;
+
+ /* Event counters for the this whole EDAC Device */
+ struct edac_device_counter counters;
+
+ /* edac sysfs device control for the 'name'
+ * device this structure controls
+ */
+ struct kobject kobj;
+};
+
+/* To get from the instance's wq to the beginning of the ctl structure */
+#define to_edac_mem_ctl_work(w) \
+ container_of(w, struct mem_ctl_info, work)
+
+#define to_edac_device_ctl_work(w) \
+ container_of(w,struct edac_device_ctl_info,work)
+
+/*
+ * The alloc() and free() functions for the 'edac_device' control info
+ * structure. A MC driver will allocate one of these for each edac_device
+ * it is going to control/register with the EDAC CORE.
+ */
+extern struct edac_device_ctl_info *edac_device_alloc_ctl_info(
+ unsigned sizeof_private,
+ char *edac_device_name, unsigned nr_instances,
+ char *edac_block_name, unsigned nr_blocks,
+ unsigned offset_value,
+ struct edac_dev_sysfs_block_attribute *block_attributes,
+ unsigned nr_attribs,
+ int device_index);
+
+/* The offset value can be:
+ * -1 indicating no offset value
+ * 0 for zero-based block numbers
+ * 1 for 1-based block number
+ * other for other-based block number
+ */
+#define BLOCK_OFFSET_VALUE_OFF ((unsigned) -1)
+
+extern void edac_device_free_ctl_info(struct edac_device_ctl_info *ctl_info);
+
+/**
+ * edac_device_add_device: Insert the 'edac_dev' structure into the
+ * edac_device global list and create sysfs entries associated with
+ * edac_device structure.
+ *
+ * @edac_dev: pointer to edac_device structure to be added to the list
+ * 'edac_device' structure.
+ *
+ * Returns:
+ * 0 on Success, or an error code on failure
+ */
+extern int edac_device_add_device(struct edac_device_ctl_info *edac_dev);
+
+/**
+ * edac_device_del_device:
+ * Remove sysfs entries for specified edac_device structure and
+ * then remove edac_device structure from global list
+ *
+ * @dev:
+ * Pointer to struct &device representing the edac device
+ * structure to remove.
+ *
+ * Returns:
+ * Pointer to removed edac_device structure,
+ * or %NULL if device not found.
+ */
+extern struct edac_device_ctl_info *edac_device_del_device(struct device *dev);
+
+/**
+ * Log correctable errors.
+ *
+ * @edac_dev: pointer to struct &edac_device_ctl_info
+ * @inst_nr: number of the instance where the CE error happened
+ * @count: Number of errors to log.
+ * @block_nr: number of the block where the CE error happened
+ * @msg: message to be printed
+ */
+void edac_device_handle_ce_count(struct edac_device_ctl_info *edac_dev,
+ unsigned int count, int inst_nr, int block_nr,
+ const char *msg);
+
+/**
+ * Log uncorrectable errors.
+ *
+ * @edac_dev: pointer to struct &edac_device_ctl_info
+ * @inst_nr: number of the instance where the CE error happened
+ * @count: Number of errors to log.
+ * @block_nr: number of the block where the CE error happened
+ * @msg: message to be printed
+ */
+void edac_device_handle_ue_count(struct edac_device_ctl_info *edac_dev,
+ unsigned int count, int inst_nr, int block_nr,
+ const char *msg);
+
+/**
+ * edac_device_handle_ce(): Log a single correctable error
+ *
+ * @edac_dev: pointer to struct &edac_device_ctl_info
+ * @inst_nr: number of the instance where the CE error happened
+ * @block_nr: number of the block where the CE error happened
+ * @msg: message to be printed
+ */
+static inline void
+edac_device_handle_ce(struct edac_device_ctl_info *edac_dev, int inst_nr,
+ int block_nr, const char *msg)
+{
+ edac_device_handle_ce_count(edac_dev, 1, inst_nr, block_nr, msg);
+}
+
+/**
+ * edac_device_handle_ue(): Log a single uncorrectable error
+ *
+ * @edac_dev: pointer to struct &edac_device_ctl_info
+ * @inst_nr: number of the instance where the UE error happened
+ * @block_nr: number of the block where the UE error happened
+ * @msg: message to be printed
+ */
+static inline void
+edac_device_handle_ue(struct edac_device_ctl_info *edac_dev, int inst_nr,
+ int block_nr, const char *msg)
+{
+ edac_device_handle_ue_count(edac_dev, 1, inst_nr, block_nr, msg);
+}
+
+/**
+ * edac_device_alloc_index: Allocate a unique device index number
+ *
+ * Returns:
+ * allocated index number
+ */
+extern int edac_device_alloc_index(void);
+extern const char *edac_layer_name[];
+#endif
diff --git a/meta-elisa/recipes-kernel/linux/files/edac_inject.c b/meta-elisa/recipes-kernel/linux/files/edac_inject.c
new file mode 100644
index 00000000..aa431c7c
--- /dev/null
+++ b/meta-elisa/recipes-kernel/linux/files/edac_inject.c
@@ -0,0 +1,357 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * edac_inject.c; platform agnostic MC error injector
+ *
+ * Copyright (c) 2020 Intel Corporation
+ *
+ * Authors: Gabriele Paoloni <gabriele.paoloni@intel.com>
+ * Corey Minyard <cminyard@mvista.com>
+ */
+
+#include <linux/edac.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/stddef.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include "edac_module.h"
+
+static struct platform_device dummy_pdev;
+
+#define EDAC_INJECT_MAX_MSG_SIZE 64
+
+/**
+ * Information about an error.
+ */
+struct inj_errinfo {
+ unsigned long page_frame_number;
+ unsigned long offset_in_page;
+ unsigned long syndrome;
+ int top_layer;
+ int mid_layer;
+ int low_layer;
+ char msg[EDAC_INJECT_MAX_MSG_SIZE];
+ char other_detail[EDAC_INJECT_MAX_MSG_SIZE];
+};
+
+/* A single error type. */
+struct inj_err {
+ struct list_head link;
+ enum hw_event_mc_err_type type;
+ u16 count;
+ struct inj_errinfo info;
+};
+
+/**
+ * mci private structure to store the errors
+ */
+struct inj_pvt {
+ struct mutex lock;
+ struct list_head errors;
+
+ /* Information to put into the edac error report. */
+ struct inj_errinfo info;
+};
+
+static void edac_inject_handle(struct mem_ctl_info *mci,
+ struct inj_err *err)
+{
+ edac_mc_handle_error(err->type, mci, err->count,
+ err->info.page_frame_number,
+ err->info.offset_in_page,
+ err->info.syndrome,
+ err->info.top_layer, err->info.mid_layer,
+ err->info.low_layer,
+ err->info.msg, err->info.other_detail);
+ err->count = 0;
+}
+
+/**
+ * inject_edac_check() - Calls the error checking subroutines
+ * @mci: struct mem_ctl_info pointer
+ */
+static void inject_edac_check(struct mem_ctl_info *mci)
+{
+ struct inj_pvt *pvt = mci->pvt_info;
+ struct inj_err *val, *val2;
+
+ mutex_lock(&pvt->lock);
+ list_for_each_entry_safe(val, val2, &pvt->errors, link) {
+ edac_inject_handle(mci, val);
+ list_del(&val->link);
+ kfree(val);
+ }
+ mutex_unlock(&pvt->lock);
+};
+
+struct inject_edac_attribute {
+ struct device_attribute attr;
+ unsigned long offset;
+ char *default_msg;
+};
+
+#define to_inj_edac_attr(x) container_of(x, struct inject_edac_attribute, attr)
+
+static ssize_t inject_edac_store_count(struct device *dev,
+ struct device_attribute *attr,
+ const char *data, size_t count)
+{
+ struct inject_edac_attribute *ea = to_inj_edac_attr(attr);
+ struct inj_pvt *pvt = to_mci(dev)->pvt_info;
+ struct inj_err *val;
+ int ret;
+ u16 errcount;
+
+ ret = kstrtou16(data, 10, &errcount);
+ if (ret < 0)
+ return ret;
+ if (errcount == 0)
+ goto out;
+
+ val = kzalloc(sizeof(struct inj_err), GFP_KERNEL);
+ if (!val)
+ return -ENOMEM;
+
+ val->type = ea->offset;
+ val->count = errcount;
+ val->info = pvt->info;
+ if (!pvt->info.msg[0])
+ strncpy(val->info.msg, ea->default_msg,
+ EDAC_INJECT_MAX_MSG_SIZE);
+
+ mutex_lock(&pvt->lock);
+ list_add(&val->link, &pvt->errors);
+ mutex_unlock(&pvt->lock);
+
+out:
+ return count;
+}
+
+static ssize_t inject_edac_show_count(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct inject_edac_attribute *ea = to_inj_edac_attr(attr);
+ struct inj_pvt *pvt = to_mci(dev)->pvt_info;
+ struct inj_err *val;
+ unsigned int count = 0;
+
+ mutex_lock(&pvt->lock);
+ list_for_each_entry(val, &pvt->errors, link) {
+ if (val->type == ea->offset)
+ count += val->count;
+ }
+ mutex_unlock(&pvt->lock);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", count);
+}
+
+static ssize_t inject_edac_store_ulong(struct device *dev,
+ struct device_attribute *attr,
+ const char *data, size_t count)
+{
+ struct inject_edac_attribute *ea = to_inj_edac_attr(attr);
+ struct inj_pvt *pvt = to_mci(dev)->pvt_info;
+ unsigned long *val = (unsigned long *) (((char *) pvt) + ea->offset);
+ int ret;
+
+ ret = kstrtoul(data, 10, val);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static ssize_t inject_edac_show_ulong(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct inject_edac_attribute *ea = to_inj_edac_attr(attr);
+ struct inj_pvt *pvt = to_mci(dev)->pvt_info;
+ unsigned long *val = (unsigned long *) (((char *) pvt) + ea->offset);
+
+ return snprintf(buf, PAGE_SIZE, "%lu\n", *val);
+}
+
+static ssize_t inject_edac_store_int(struct device *dev,
+ struct device_attribute *attr,
+ const char *data, size_t count)
+{
+ struct inject_edac_attribute *ea = to_inj_edac_attr(attr);
+ struct inj_pvt *pvt = to_mci(dev)->pvt_info;
+ int *val = (int *) (((char *) pvt) + ea->offset);
+ int ret;
+
+ ret = kstrtoint(data, 10, val);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static ssize_t inject_edac_show_int(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct inject_edac_attribute *ea = to_inj_edac_attr(attr);
+ struct inj_pvt *pvt = to_mci(dev)->pvt_info;
+ int *val = (int *) (((char *) pvt) + ea->offset);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", *val);
+}
+
+static ssize_t inject_edac_store_str(struct device *dev,
+ struct device_attribute *attr,
+ const char *data, size_t count)
+{
+ struct inject_edac_attribute *ea = to_inj_edac_attr(attr);
+ struct inj_pvt *pvt = to_mci(dev)->pvt_info;
+ size_t real_size = count;
+ char *val = (((char *) pvt) + ea->offset);
+
+ while (real_size > 0 && data[real_size - 1] == '\n')
+ real_size--;
+ if (real_size > EDAC_INJECT_MAX_MSG_SIZE - 1)
+ real_size = EDAC_INJECT_MAX_MSG_SIZE - 1;
+ memcpy(val, data, real_size);
+ val[real_size] = '\0';
+
+ return count;
+}
+
+static ssize_t inject_edac_show_str(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct inject_edac_attribute *ea = to_inj_edac_attr(attr);
+ struct inj_pvt *pvt = to_mci(dev)->pvt_info;
+ char *val = (((char *) pvt) + ea->offset);
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", val);
+}
+
+#define DEVICE_INJECT_EDAC_COUNT(_name, _member, _type, _defstr)\
+ struct inject_edac_attribute inject_edac_attr_##_name =\
+ { __ATTR(_name, 0600, inject_edac_show_count,\
+ inject_edac_store_count),\
+ _type, _defstr }
+
+#define DEVICE_INJECT_EDAC_ULONG(_name, _member)\
+ struct inject_edac_attribute inject_edac_attr_##_name =\
+ { __ATTR(_name, 0600, inject_edac_show_ulong,\
+ inject_edac_store_ulong),\
+ offsetof(struct inj_pvt, info._member) }
+
+#define DEVICE_INJECT_EDAC_INT(_name, _member)\
+ struct inject_edac_attribute inject_edac_attr_##_name =\
+ { __ATTR(_name, 0600, inject_edac_show_int,\
+ inject_edac_store_int),\
+ offsetof(struct inj_pvt, info._member) }
+
+#define DEVICE_INJECT_EDAC_STR(_name, _member)\
+ struct inject_edac_attribute inject_edac_attr_##_name =\
+ { __ATTR(_name, 0600, inject_edac_show_str,\
+ inject_edac_store_str),\
+ offsetof(struct inj_pvt, info._member) }
+
+static DEVICE_INJECT_EDAC_COUNT(inject_ce, correctable_errors,
+ HW_EVENT_ERR_CORRECTED,
+ "injected correctable errors");
+static DEVICE_INJECT_EDAC_COUNT(inject_ue, uncorrectable_errors,
+ HW_EVENT_ERR_UNCORRECTED,
+ "injected uncorrectable errors");
+static DEVICE_INJECT_EDAC_COUNT(inject_de, deferrable_errors,
+ HW_EVENT_ERR_DEFERRED,
+ "injected deferrable errors");
+static DEVICE_INJECT_EDAC_COUNT(inject_fe, fatal_errors,
+ HW_EVENT_ERR_FATAL,
+ "injected fatal errors");
+static DEVICE_INJECT_EDAC_COUNT(inject_ie, informative_errors,
+ HW_EVENT_ERR_INFO,
+ "injected informative errors");
+static DEVICE_INJECT_EDAC_ULONG(inject_pfn, page_frame_number);
+static DEVICE_INJECT_EDAC_ULONG(inject_oip, offset_in_page);
+static DEVICE_INJECT_EDAC_ULONG(inject_syndrome, syndrome);
+static DEVICE_INJECT_EDAC_INT(inject_top, top_layer);
+static DEVICE_INJECT_EDAC_INT(inject_mid, mid_layer);
+static DEVICE_INJECT_EDAC_INT(inject_low, low_layer);
+static DEVICE_INJECT_EDAC_STR(inject_msg, msg);
+static DEVICE_INJECT_EDAC_STR(inject_other_detail, other_detail);
+
+static struct attribute *edac_inj_attrs[] = {
+ &inject_edac_attr_inject_ce.attr.attr,
+ &inject_edac_attr_inject_ue.attr.attr,
+ &inject_edac_attr_inject_de.attr.attr,
+ &inject_edac_attr_inject_fe.attr.attr,
+ &inject_edac_attr_inject_ie.attr.attr,
+ &inject_edac_attr_inject_pfn.attr.attr,
+ &inject_edac_attr_inject_oip.attr.attr,
+ &inject_edac_attr_inject_syndrome.attr.attr,
+ &inject_edac_attr_inject_top.attr.attr,
+ &inject_edac_attr_inject_mid.attr.attr,
+ &inject_edac_attr_inject_low.attr.attr,
+ &inject_edac_attr_inject_msg.attr.attr,
+ &inject_edac_attr_inject_other_detail.attr.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(edac_inj);
+
+static int __init edac_inject_init(void)
+{
+ struct edac_mc_layer layer;
+ struct inj_pvt *pvt;
+ struct mem_ctl_info *mci;
+ int rc;
+
+ edac_printk(KERN_INFO, EDAC_MC,
+ "EDAC MC error inject module init\n");
+ edac_printk(KERN_INFO, EDAC_MC,
+ "\t(c) 2020 Intel Corporation\n");
+
+ /* Only POLL mode supported so far */
+ edac_op_state = EDAC_OPSTATE_POLL;
+
+ layer.type = EDAC_MC_LAYER_CHANNEL;
+ layer.size = 1;
+ layer.is_virt_csrow = false;
+ mci = edac_mc_alloc(0, 1, &layer, sizeof(struct inj_pvt));
+ if (!mci) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "EDAC INJECT: edac_mc_alloc failed\n");
+ return -ENOMEM;
+ }
+
+ mci->pdev = &dummy_pdev.dev;
+ pvt = mci->pvt_info;
+ mutex_init(&pvt->lock);
+ INIT_LIST_HEAD(&pvt->errors);
+
+ /* Set the function pointer for periodic errors checks */
+ mci->edac_check = inject_edac_check;
+
+ rc = edac_mc_add_mc_with_groups(mci, edac_inj_groups);
+ if (rc) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "EDAC INJECT: edac_mc_add_mc failed\n");
+ edac_mc_free(mci);
+ }
+
+ return rc;
+}
+
+static void __exit edac_inject_exit(void)
+{
+ struct mem_ctl_info *mci = platform_get_drvdata(&dummy_pdev);
+
+ edac_mc_del_mc(&dummy_pdev.dev);
+ edac_mc_free(mci);
+}
+
+
+module_init(edac_inject_init);
+module_exit(edac_inject_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Gabriele Paoloni <gabriele.paoloni@intel.com>\n");
+MODULE_AUTHOR("Corey Minyard <cminyard@mvista.com>\n");
+MODULE_DESCRIPTION("EDAC MC error inject module");
diff --git a/meta-elisa/recipes-kernel/linux/files/edac_inject.rst b/meta-elisa/recipes-kernel/linux/files/edac_inject.rst
new file mode 100644
index 00000000..0c91eda2
--- /dev/null
+++ b/meta-elisa/recipes-kernel/linux/files/edac_inject.rst
@@ -0,0 +1,43 @@
+EDAC Injection Device
+===========================
+
+This device lets you inject errors into the Linux EDAC system for
+testing userland software that interacts with EDAC, since causing your
+own memory errors is hard.
+
+The interface for injecting errors appears in the sysfs file system in
+the same place as the EDAC driver's interface::
+
+ /sys/devices/system/edac/mc/mc<n>/..
+
+The files for injection all start with "inject_", they are:
+
+- inject_ce - correctable error
+- inject_de - deferred error
+- inject_fe - fatal error
+- inject_ie - informative error
+- inject_ue - uncorrectable error
+
+Writing a number besides zero to these will result in that many errors
+being injected. Each write injects new errors. Reading the value
+returns the number of pending errors to be injected that have not
+yet completed.
+
+In addition to these, some data is passed along with the error to show
+where it occurred. These values mostly default to zero or empty, but
+they can be set in the following write only values:
+
+- inject_low - low layer
+- inject_mid - mid layer
+- inject_msg - error message string. This defaults to "dummy <error
+ type>" where "<error type>" is "correctable error", "uncorrectable
+ error", etc.
+- inject_oip - offset in page
+- inject_other_detail - Other details string
+- inject_pfn - page frame number
+- inject_syndrome - symdrome
+- inject_top - top layer
+
+Note that these values are taken when the count is written, so you are
+free to set up the info, write an error count, change the info, write
+another error count, etc.
diff --git a/meta-elisa/recipes-kernel/linux/files/edac_mc.h b/meta-elisa/recipes-kernel/linux/files/edac_mc.h
new file mode 100644
index 00000000..881b00ea
--- /dev/null
+++ b/meta-elisa/recipes-kernel/linux/files/edac_mc.h
@@ -0,0 +1,258 @@
+/*
+ * Defines, structures, APIs for edac_mc module
+ *
+ * (C) 2007 Linux Networx (http://lnxi.com)
+ * This file may be distributed under the terms of the
+ * GNU General Public License.
+ *
+ * Written by Thayne Harbaugh
+ * Based on work by Dan Hollis <goemon at anime dot net> and others.
+ * http://www.anime.net/~goemon/linux-ecc/
+ *
+ * NMI handling support added by
+ * Dave Peterson <dsp@llnl.gov> <dave_peterson@pobox.com>
+ *
+ * Refactored for multi-source files:
+ * Doug Thompson <norsk5@xmission.com>
+ *
+ * Please look at Documentation/driver-api/edac.rst for more info about
+ * EDAC core structs and functions.
+ */
+
+#ifndef _EDAC_MC_H_
+#define _EDAC_MC_H_
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/smp.h>
+#include <linux/pci.h>
+#include <linux/time.h>
+#include <linux/nmi.h>
+#include <linux/rcupdate.h>
+#include <linux/completion.h>
+#include <linux/kobject.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/edac.h>
+
+#if PAGE_SHIFT < 20
+#define PAGES_TO_MiB(pages) ((pages) >> (20 - PAGE_SHIFT))
+#define MiB_TO_PAGES(mb) ((mb) << (20 - PAGE_SHIFT))
+#else /* PAGE_SHIFT > 20 */
+#define PAGES_TO_MiB(pages) ((pages) << (PAGE_SHIFT - 20))
+#define MiB_TO_PAGES(mb) ((mb) >> (PAGE_SHIFT - 20))
+#endif
+
+#define edac_printk(level, prefix, fmt, arg...) \
+ printk(level "EDAC " prefix ": " fmt, ##arg)
+
+#define edac_mc_printk(mci, level, fmt, arg...) \
+ printk(level "EDAC MC%d: " fmt, mci->mc_idx, ##arg)
+
+#define edac_mc_chipset_printk(mci, level, prefix, fmt, arg...) \
+ printk(level "EDAC " prefix " MC%d: " fmt, mci->mc_idx, ##arg)
+
+#define edac_device_printk(ctl, level, fmt, arg...) \
+ printk(level "EDAC DEVICE%d: " fmt, ctl->dev_idx, ##arg)
+
+#define edac_pci_printk(ctl, level, fmt, arg...) \
+ printk(level "EDAC PCI%d: " fmt, ctl->pci_idx, ##arg)
+
+/* prefixes for edac_printk() and edac_mc_printk() */
+#define EDAC_MC "MC"
+#define EDAC_PCI "PCI"
+#define EDAC_DEBUG "DEBUG"
+
+extern const char * const edac_mem_types[];
+
+#ifdef CONFIG_EDAC_DEBUG
+extern int edac_debug_level;
+
+#define edac_dbg(level, fmt, ...) \
+do { \
+ if (level <= edac_debug_level) \
+ edac_printk(KERN_DEBUG, EDAC_DEBUG, \
+ "%s: " fmt, __func__, ##__VA_ARGS__); \
+} while (0)
+
+#else /* !CONFIG_EDAC_DEBUG */
+
+#define edac_dbg(level, fmt, ...) \
+do { \
+ if (0) \
+ edac_printk(KERN_DEBUG, EDAC_DEBUG, \
+ "%s: " fmt, __func__, ##__VA_ARGS__); \
+} while (0)
+
+#endif /* !CONFIG_EDAC_DEBUG */
+
+#define PCI_VEND_DEV(vend, dev) PCI_VENDOR_ID_ ## vend, \
+ PCI_DEVICE_ID_ ## vend ## _ ## dev
+
+#define edac_dev_name(dev) (dev)->dev_name
+
+#define to_mci(k) container_of(k, struct mem_ctl_info, dev)
+
+/**
+ * edac_mc_alloc() - Allocate and partially fill a struct &mem_ctl_info.
+ *
+ * @mc_num: Memory controller number
+ * @n_layers: Number of MC hierarchy layers
+ * @layers: Describes each layer as seen by the Memory Controller
+ * @sz_pvt: size of private storage needed
+ *
+ *
+ * Everything is kmalloc'ed as one big chunk - more efficient.
+ * Only can be used if all structures have the same lifetime - otherwise
+ * you have to allocate and initialize your own structures.
+ *
+ * Use edac_mc_free() to free mc structures allocated by this function.
+ *
+ * .. note::
+ *
+ * drivers handle multi-rank memories in different ways: in some
+ * drivers, one multi-rank memory stick is mapped as one entry, while, in
+ * others, a single multi-rank memory stick would be mapped into several
+ * entries. Currently, this function will allocate multiple struct dimm_info
+ * on such scenarios, as grouping the multiple ranks require drivers change.
+ *
+ * Returns:
+ * On success, return a pointer to struct mem_ctl_info pointer;
+ * %NULL otherwise
+ */
+struct mem_ctl_info *edac_mc_alloc(unsigned int mc_num,
+ unsigned int n_layers,
+ struct edac_mc_layer *layers,
+ unsigned int sz_pvt);
+
+/**
+ * edac_get_owner - Return the owner's mod_name of EDAC MC
+ *
+ * Returns:
+ * Pointer to mod_name string when EDAC MC is owned. NULL otherwise.
+ */
+extern const char *edac_get_owner(void);
+
+/*
+ * edac_mc_add_mc_with_groups() - Insert the @mci structure into the mci
+ * global list and create sysfs entries associated with @mci structure.
+ *
+ * @mci: pointer to the mci structure to be added to the list
+ * @groups: optional attribute groups for the driver-specific sysfs entries
+ *
+ * Returns:
+ * 0 on Success, or an error code on failure
+ */
+extern int edac_mc_add_mc_with_groups(struct mem_ctl_info *mci,
+ const struct attribute_group **groups);
+#define edac_mc_add_mc(mci) edac_mc_add_mc_with_groups(mci, NULL)
+
+/**
+ * edac_mc_free() - Frees a previously allocated @mci structure
+ *
+ * @mci: pointer to a struct mem_ctl_info structure
+ */
+extern void edac_mc_free(struct mem_ctl_info *mci);
+
+/**
+ * edac_has_mcs() - Check if any MCs have been allocated.
+ *
+ * Returns:
+ * True if MC instances have been registered successfully.
+ * False otherwise.
+ */
+extern bool edac_has_mcs(void);
+
+/**
+ * edac_mc_find() - Search for a mem_ctl_info structure whose index is @idx.
+ *
+ * @idx: index to be seek
+ *
+ * If found, return a pointer to the structure.
+ * Else return NULL.
+ */
+extern struct mem_ctl_info *edac_mc_find(int idx);
+
+/**
+ * find_mci_by_dev() - Scan list of controllers looking for the one that
+ * manages the @dev device.
+ *
+ * @dev: pointer to a struct device related with the MCI
+ *
+ * Returns: on success, returns a pointer to struct &mem_ctl_info;
+ * %NULL otherwise.
+ */
+extern struct mem_ctl_info *find_mci_by_dev(struct device *dev);
+
+/**
+ * edac_mc_del_mc() - Remove sysfs entries for mci structure associated with
+ * @dev and remove mci structure from global list.
+ *
+ * @dev: Pointer to struct &device representing mci structure to remove.
+ *
+ * Returns: pointer to removed mci structure, or %NULL if device not found.
+ */
+extern struct mem_ctl_info *edac_mc_del_mc(struct device *dev);
+
+/**
+ * edac_mc_find_csrow_by_page() - Ancillary routine to identify what csrow
+ * contains a memory page.
+ *
+ * @mci: pointer to a struct mem_ctl_info structure
+ * @page: memory page to find
+ *
+ * Returns: on success, returns the csrow. -1 if not found.
+ */
+extern int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci,
+ unsigned long page);
+
+/**
+ * edac_raw_mc_handle_error() - Reports a memory event to userspace without
+ * doing anything to discover the error location.
+ *
+ * @e: error description
+ *
+ * This raw function is used internally by edac_mc_handle_error(). It should
+ * only be called directly when the hardware error come directly from BIOS,
+ * like in the case of APEI GHES driver.
+ */
+void edac_raw_mc_handle_error(struct edac_raw_error_desc *e);
+
+/**
+ * edac_mc_handle_error() - Reports a memory event to userspace.
+ *
+ * @type: severity of the error (CE/UE/Fatal)
+ * @mci: a struct mem_ctl_info pointer
+ * @error_count: Number of errors of the same type
+ * @page_frame_number: mem page where the error occurred
+ * @offset_in_page: offset of the error inside the page
+ * @syndrome: ECC syndrome
+ * @top_layer: Memory layer[0] position
+ * @mid_layer: Memory layer[1] position
+ * @low_layer: Memory layer[2] position
+ * @msg: Message meaningful to the end users that
+ * explains the event
+ * @other_detail: Technical details about the event that
+ * may help hardware manufacturers and
+ * EDAC developers to analyse the event
+ */
+void edac_mc_handle_error(const enum hw_event_mc_err_type type,
+ struct mem_ctl_info *mci,
+ const u16 error_count,
+ const unsigned long page_frame_number,
+ const unsigned long offset_in_page,
+ const unsigned long syndrome,
+ const int top_layer,
+ const int mid_layer,
+ const int low_layer,
+ const char *msg,
+ const char *other_detail);
+
+/*
+ * edac misc APIs
+ */
+extern char *edac_op_state_to_string(int op_state);
+
+#endif /* _EDAC_MC_H_ */
diff --git a/meta-elisa/recipes-kernel/linux/files/edac_module.h b/meta-elisa/recipes-kernel/linux/files/edac_module.h
new file mode 100644
index 00000000..aa1f9168
--- /dev/null
+++ b/meta-elisa/recipes-kernel/linux/files/edac_module.h
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * edac_module.h
+ *
+ * For defining functions/data for within the EDAC_CORE module only
+ *
+ * written by doug thompson <norsk5@xmission.h>
+ */
+
+#ifndef __EDAC_MODULE_H__
+#define __EDAC_MODULE_H__
+
+#include "edac_mc.h"
+#include "edac_pci.h"
+#include "edac_device.h"
+
+/*
+ * INTERNAL EDAC MODULE:
+ * EDAC memory controller sysfs create/remove functions
+ * and setup/teardown functions
+ *
+ * edac_mc objects
+ */
+ /* on edac_mc_sysfs.c */
+int edac_mc_sysfs_init(void);
+void edac_mc_sysfs_exit(void);
+extern int edac_create_sysfs_mci_device(struct mem_ctl_info *mci,
+ const struct attribute_group **groups);
+extern void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci);
+extern int edac_get_log_ue(void);
+extern int edac_get_log_ce(void);
+extern int edac_get_panic_on_ue(void);
+extern int edac_mc_get_log_ue(void);
+extern int edac_mc_get_log_ce(void);
+extern int edac_mc_get_panic_on_ue(void);
+extern int edac_get_poll_msec(void);
+extern unsigned int edac_mc_get_poll_msec(void);
+
+unsigned edac_dimm_info_location(struct dimm_info *dimm, char *buf,
+ unsigned len);
+
+ /* on edac_device.c */
+extern int edac_device_register_sysfs_main_kobj(
+ struct edac_device_ctl_info *edac_dev);
+extern void edac_device_unregister_sysfs_main_kobj(
+ struct edac_device_ctl_info *edac_dev);
+extern int edac_device_create_sysfs(struct edac_device_ctl_info *edac_dev);
+extern void edac_device_remove_sysfs(struct edac_device_ctl_info *edac_dev);
+
+/* edac core workqueue: single CPU mode */
+int edac_workqueue_setup(void);
+void edac_workqueue_teardown(void);
+bool edac_queue_work(struct delayed_work *work, unsigned long delay);
+bool edac_stop_work(struct delayed_work *work);
+bool edac_mod_work(struct delayed_work *work, unsigned long delay);
+
+extern void edac_device_reset_delay_period(struct edac_device_ctl_info
+ *edac_dev, unsigned long value);
+extern void edac_mc_reset_delay_period(unsigned long value);
+
+extern void *edac_align_ptr(void **p, unsigned size, int n_elems);
+
+/*
+ * EDAC debugfs functions
+ */
+
+#define edac_debugfs_remove_recursive debugfs_remove_recursive
+#define edac_debugfs_remove debugfs_remove
+#ifdef CONFIG_EDAC_DEBUG
+void edac_debugfs_init(void);
+void edac_debugfs_exit(void);
+void edac_create_debugfs_nodes(struct mem_ctl_info *mci);
+struct dentry *edac_debugfs_create_dir(const char *dirname);
+struct dentry *
+edac_debugfs_create_dir_at(const char *dirname, struct dentry *parent);
+struct dentry *
+edac_debugfs_create_file(const char *name, umode_t mode, struct dentry *parent,
+ void *data, const struct file_operations *fops);
+void edac_debugfs_create_x8(const char *name, umode_t mode,
+ struct dentry *parent, u8 *value);
+void edac_debugfs_create_x16(const char *name, umode_t mode,
+ struct dentry *parent, u16 *value);
+void edac_debugfs_create_x32(const char *name, umode_t mode,
+ struct dentry *parent, u32 *value);
+#else
+static inline void edac_debugfs_init(void) { }
+static inline void edac_debugfs_exit(void) { }
+static inline void edac_create_debugfs_nodes(struct mem_ctl_info *mci) { }
+static inline struct dentry *edac_debugfs_create_dir(const char *dirname) { return NULL; }
+static inline struct dentry *
+edac_debugfs_create_dir_at(const char *dirname, struct dentry *parent) { return NULL; }
+static inline struct dentry *
+edac_debugfs_create_file(const char *name, umode_t mode, struct dentry *parent,
+ void *data, const struct file_operations *fops) { return NULL; }
+static inline void edac_debugfs_create_x8(const char *name, umode_t mode,
+ struct dentry *parent, u8 *value) { }
+static inline void edac_debugfs_create_x16(const char *name, umode_t mode,
+ struct dentry *parent, u16 *value) { }
+static inline void edac_debugfs_create_x32(const char *name, umode_t mode,
+ struct dentry *parent, u32 *value) { }
+#endif
+
+/*
+ * EDAC PCI functions
+ */
+#ifdef CONFIG_PCI
+extern void edac_pci_do_parity_check(void);
+extern void edac_pci_clear_parity_errors(void);
+extern int edac_sysfs_pci_setup(void);
+extern void edac_sysfs_pci_teardown(void);
+extern int edac_pci_get_check_errors(void);
+extern int edac_pci_get_poll_msec(void);
+extern void edac_pci_remove_sysfs(struct edac_pci_ctl_info *pci);
+extern void edac_pci_handle_pe(struct edac_pci_ctl_info *pci, const char *msg);
+extern void edac_pci_handle_npe(struct edac_pci_ctl_info *pci,
+ const char *msg);
+#else /* CONFIG_PCI */
+/* pre-process these away */
+#define edac_pci_do_parity_check()
+#define edac_pci_clear_parity_errors()
+#define edac_sysfs_pci_setup() (0)
+#define edac_sysfs_pci_teardown()
+#define edac_pci_get_check_errors()
+#define edac_pci_get_poll_msec()
+#define edac_pci_handle_pe()
+#define edac_pci_handle_npe()
+#endif /* CONFIG_PCI */
+
+#endif /* __EDAC_MODULE_H__ */
diff --git a/meta-elisa/recipes-kernel/linux/files/edac_pci.h b/meta-elisa/recipes-kernel/linux/files/edac_pci.h
new file mode 100644
index 00000000..5175f572
--- /dev/null
+++ b/meta-elisa/recipes-kernel/linux/files/edac_pci.h
@@ -0,0 +1,271 @@
+/*
+ * Defines, structures, APIs for edac_pci and edac_pci_sysfs
+ *
+ * (C) 2007 Linux Networx (http://lnxi.com)
+ * This file may be distributed under the terms of the
+ * GNU General Public License.
+ *
+ * Written by Thayne Harbaugh
+ * Based on work by Dan Hollis <goemon at anime dot net> and others.
+ * http://www.anime.net/~goemon/linux-ecc/
+ *
+ * NMI handling support added by
+ * Dave Peterson <dsp@llnl.gov> <dave_peterson@pobox.com>
+ *
+ * Refactored for multi-source files:
+ * Doug Thompson <norsk5@xmission.com>
+ *
+ * Please look at Documentation/driver-api/edac.rst for more info about
+ * EDAC core structs and functions.
+ */
+
+#ifndef _EDAC_PCI_H_
+#define _EDAC_PCI_H_
+
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/edac.h>
+#include <linux/kobject.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#ifdef CONFIG_PCI
+
+struct edac_pci_counter {
+ atomic_t pe_count;
+ atomic_t npe_count;
+};
+
+/*
+ * Abstract edac_pci control info structure
+ *
+ */
+struct edac_pci_ctl_info {
+ /* for global list of edac_pci_ctl_info structs */
+ struct list_head link;
+
+ int pci_idx;
+
+ struct bus_type *edac_subsys; /* pointer to subsystem */
+
+ /* the internal state of this controller instance */
+ int op_state;
+ /* work struct for this instance */
+ struct delayed_work work;
+
+ /* pointer to edac polling checking routine:
+ * If NOT NULL: points to polling check routine
+ * If NULL: Then assumes INTERRUPT operation, where
+ * MC driver will receive events
+ */
+ void (*edac_check) (struct edac_pci_ctl_info * edac_dev);
+
+ struct device *dev; /* pointer to device structure */
+
+ const char *mod_name; /* module name */
+ const char *ctl_name; /* edac controller name */
+ const char *dev_name; /* pci/platform/etc... name */
+
+ void *pvt_info; /* pointer to 'private driver' info */
+
+ unsigned long start_time; /* edac_pci load start time (jiffies) */
+
+ struct completion complete;
+
+ /* sysfs top name under 'edac' directory
+ * and instance name:
+ * cpu/cpu0/...
+ * cpu/cpu1/...
+ * cpu/cpu2/...
+ * ...
+ */
+ char name[EDAC_DEVICE_NAME_LEN + 1];
+
+ /* Event counters for the this whole EDAC Device */
+ struct edac_pci_counter counters;
+
+ /* edac sysfs device control for the 'name'
+ * device this structure controls
+ */
+ struct kobject kobj;
+};
+
+#define to_edac_pci_ctl_work(w) \
+ container_of(w, struct edac_pci_ctl_info,work)
+
+/* write all or some bits in a byte-register*/
+static inline void pci_write_bits8(struct pci_dev *pdev, int offset, u8 value,
+ u8 mask)
+{
+ if (mask != 0xff) {
+ u8 buf;
+
+ pci_read_config_byte(pdev, offset, &buf);
+ value &= mask;
+ buf &= ~mask;
+ value |= buf;
+ }
+
+ pci_write_config_byte(pdev, offset, value);
+}
+
+/* write all or some bits in a word-register*/
+static inline void pci_write_bits16(struct pci_dev *pdev, int offset,
+ u16 value, u16 mask)
+{
+ if (mask != 0xffff) {
+ u16 buf;
+
+ pci_read_config_word(pdev, offset, &buf);
+ value &= mask;
+ buf &= ~mask;
+ value |= buf;
+ }
+
+ pci_write_config_word(pdev, offset, value);
+}
+
+/*
+ * pci_write_bits32
+ *
+ * edac local routine to do pci_write_config_dword, but adds
+ * a mask parameter. If mask is all ones, ignore the mask.
+ * Otherwise utilize the mask to isolate specified bits
+ *
+ * write all or some bits in a dword-register
+ */
+static inline void pci_write_bits32(struct pci_dev *pdev, int offset,
+ u32 value, u32 mask)
+{
+ if (mask != 0xffffffff) {
+ u32 buf;
+
+ pci_read_config_dword(pdev, offset, &buf);
+ value &= mask;
+ buf &= ~mask;
+ value |= buf;
+ }
+
+ pci_write_config_dword(pdev, offset, value);
+}
+
+#endif /* CONFIG_PCI */
+
+/*
+ * edac_pci APIs
+ */
+
+/**
+ * edac_pci_alloc_ctl_info:
+ * The alloc() function for the 'edac_pci' control info
+ * structure.
+ *
+ * @sz_pvt: size of the private info at struct &edac_pci_ctl_info
+ * @edac_pci_name: name of the PCI device
+ *
+ * The chip driver will allocate one of these for each
+ * edac_pci it is going to control/register with the EDAC CORE.
+ *
+ * Returns: a pointer to struct &edac_pci_ctl_info on success; %NULL otherwise.
+ */
+extern struct edac_pci_ctl_info *edac_pci_alloc_ctl_info(unsigned int sz_pvt,
+ const char *edac_pci_name);
+
+/**
+ * edac_pci_free_ctl_info():
+ * Last action on the pci control structure.
+ *
+ * @pci: pointer to struct &edac_pci_ctl_info
+ *
+ * Calls the remove sysfs information, which will unregister
+ * this control struct's kobj. When that kobj's ref count
+ * goes to zero, its release function will be call and then
+ * kfree() the memory.
+ */
+extern void edac_pci_free_ctl_info(struct edac_pci_ctl_info *pci);
+
+/**
+ * edac_pci_alloc_index: Allocate a unique PCI index number
+ *
+ * Returns:
+ * allocated index number
+ *
+ */
+extern int edac_pci_alloc_index(void);
+
+/**
+ * edac_pci_add_device(): Insert the 'edac_dev' structure into the
+ * edac_pci global list and create sysfs entries associated with
+ * edac_pci structure.
+ *
+ * @pci: pointer to the edac_device structure to be added to the list
+ * @edac_idx: A unique numeric identifier to be assigned to the
+ * 'edac_pci' structure.
+ *
+ * Returns:
+ * 0 on Success, or an error code on failure
+ */
+extern int edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx);
+
+/**
+ * edac_pci_del_device()
+ * Remove sysfs entries for specified edac_pci structure and
+ * then remove edac_pci structure from global list
+ *
+ * @dev:
+ * Pointer to 'struct device' representing edac_pci structure
+ * to remove
+ *
+ * Returns:
+ * Pointer to removed edac_pci structure,
+ * or %NULL if device not found
+ */
+extern struct edac_pci_ctl_info *edac_pci_del_device(struct device *dev);
+
+/**
+ * edac_pci_create_generic_ctl()
+ * A generic constructor for a PCI parity polling device
+ * Some systems have more than one domain of PCI busses.
+ * For systems with one domain, then this API will
+ * provide for a generic poller.
+ *
+ * @dev: pointer to struct &device;
+ * @mod_name: name of the PCI device
+ *
+ * This routine calls the edac_pci_alloc_ctl_info() for
+ * the generic device, with default values
+ *
+ * Returns: Pointer to struct &edac_pci_ctl_info on success, %NULL on
+ * failure.
+ */
+extern struct edac_pci_ctl_info *edac_pci_create_generic_ctl(
+ struct device *dev,
+ const char *mod_name);
+
+/**
+ * edac_pci_release_generic_ctl
+ * The release function of a generic EDAC PCI polling device
+ *
+ * @pci: pointer to struct &edac_pci_ctl_info
+ */
+extern void edac_pci_release_generic_ctl(struct edac_pci_ctl_info *pci);
+
+/**
+ * edac_pci_create_sysfs
+ * Create the controls/attributes for the specified EDAC PCI device
+ *
+ * @pci: pointer to struct &edac_pci_ctl_info
+ */
+extern int edac_pci_create_sysfs(struct edac_pci_ctl_info *pci);
+
+/**
+ * edac_pci_remove_sysfs()
+ * remove the controls and attributes for this EDAC PCI device
+ *
+ * @pci: pointer to struct &edac_pci_ctl_info
+ */
+extern void edac_pci_remove_sysfs(struct edac_pci_ctl_info *pci);
+
+#endif