From: Jan-Simon Moeller <jsmoeller@linuxfoundation.org>
Date: Tue, 21 Apr 2020 18:29:16 +0000 (+0200)
Subject: RFC/WIP: Add edac-inject-module
X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=commitdiff_plain;h=a12a235e206e091a6bfdee261ca142b70d683b34;p=AGL%2Fmeta-agl-devel.git

RFC/WIP: Add edac-inject-module

Signed-off-by: Jan-Simon Moeller <jsmoeller@linuxfoundation.org>
Change-Id: Ice3c4f9e6ce1d0b6b30fd4d21546fc5aec99ac06
---

diff --git a/meta-elisa/conf/layer.conf b/meta-elisa/conf/layer.conf
new file mode 100644
index 00000000..564a29ba
--- /dev/null
+++ b/meta-elisa/conf/layer.conf
@@ -0,0 +1,12 @@
+# We have a conf and classes directory, add to BBPATH
+BBPATH .= ":${LAYERDIR}"
+
+# We have recipes-* directories, add to BBFILES
+BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \
+	${LAYERDIR}/recipes-*/*/*.bbappend"
+
+BBFILE_COLLECTIONS += "meta-elisa"
+BBFILE_PATTERN_meta-elisa = "^${LAYERDIR}/"
+BBFILE_PRIORITY_meta-elisa = "6"
+
+LAYERSERIES_COMPAT_meta-elisa = "zeus dunfell"
diff --git a/meta-elisa/recipes-kernel/linux/edac-inject-module_1.0.bb b/meta-elisa/recipes-kernel/linux/edac-inject-module_1.0.bb
new file mode 100644
index 00000000..e02b64ab
--- /dev/null
+++ b/meta-elisa/recipes-kernel/linux/edac-inject-module_1.0.bb
@@ -0,0 +1,30 @@
+DESCRIPTION = "Builds the EDAC inject module"
+HOMEPAGE = "https://github.com/elisa-tech/linux/commits/edac_inject"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/GPL-2.0;md5=801f80980d171dd6425610833a22dbe6"
+
+DEPENDS += "virtual/kernel"
+
+SRC_URI = " \
+	file://edac_device.h  \
+	file://edac_inject.c  \
+	file://edac_mc.h  \
+	file://edac_module.h  \
+	file://edac_pci.h  \
+	file://Makefile  \
+"
+
+S = "${WORKDIR}/"
+
+inherit module
+
+EXTRA_OEMAKE += "KDIR=${STAGING_KERNEL_DIR}"
+
+do_compile_prepend () {
+    sed -i "s/arm-none-linux-gnueabi-/${TARGET_PREFIX}/g" ${S}/Makefile
+}
+
+do_install () {
+        install -d ${D}/lib/modules/${KERNEL_VERSION}/drivers/edac/
+        install -m 0755 ${S}/edac_inject.ko ${D}/lib/modules/${KERNEL_VERSION}/drivers/edac/
+}
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
diff --git a/templates/feature/agl-meta-elisa/50_bblayers.conf.inc b/templates/feature/agl-meta-elisa/50_bblayers.conf.inc
new file mode 100644
index 00000000..ba32fd9e
--- /dev/null
+++ b/templates/feature/agl-meta-elisa/50_bblayers.conf.inc
@@ -0,0 +1,5 @@
+
+BBLAYERS =+ " \
+	${METADIR}/meta-agl-devel/meta-elisa \
+	"
+
diff --git a/templates/feature/agl-meta-elisa/50_local.conf.inc b/templates/feature/agl-meta-elisa/50_local.conf.inc
new file mode 100644
index 00000000..d85635e3
--- /dev/null
+++ b/templates/feature/agl-meta-elisa/50_local.conf.inc
@@ -0,0 +1,3 @@
+#
+
+IMAGE_INSTALL_append = " edac-inject-module"
diff --git a/templates/feature/agl-meta-elisa/README_feature_agl-meta-elisa.md b/templates/feature/agl-meta-elisa/README_feature_agl-meta-elisa.md
new file mode 100644
index 00000000..4aaeb102
--- /dev/null
+++ b/templates/feature/agl-meta-elisa/README_feature_agl-meta-elisa.md
@@ -0,0 +1,9 @@
+---
+description: Feature agl-meta-elisa
+Maintainer: Yamaguchi-san
+---
+	
+### Feature agl-meta-elisa
+	 
+Components based to interact with the Elisa project.
+