Merge branch 'devel'

* devel: (33 commits)
  edac i5000, i5400: fix pointer math in i5000_get_mc_regs()
  edac: allow specifying the error count with fake_inject
  edac: add support for Calxeda highbank L2 cache ecc
  edac: add support for Calxeda highbank memory controller
  edac: create top-level debugfs directory
  sb_edac: properly handle error count
  i7core_edac: properly handle error count
  edac: edac_mc_handle_error(): add an error_count parameter
  edac: remove arch-specific parameter for the error handler
  amd64_edac: Don't pass driver name as an error parameter
  edac_mc: check for allocation failure in edac_mc_alloc()
  edac: Increase version to 3.0.0
  edac_mc: Cleanup per-dimm_info debug messages
  edac: Convert debugfX to edac_dbg(X,
  edac: Use more normal debugging macro style
  edac: Don't add __func__ or __FILE__ for debugf[0-9] msgs
  Edac: Add ABI Documentation for the new device nodes
  edac: move documentation ABI to ABI/testing/sysfs-devices-edac
  i7core_edac: change the mem allocation scheme to make Documentation/kobject.txt happy
  edac: change the mem allocation scheme to make Documentation/kobject.txt happy
  ...
This commit is contained in:
Mauro Carvalho Chehab
2012-07-29 21:11:05 -03:00
48 changed files with 3506 additions and 2590 deletions

View File

@@ -13,9 +13,11 @@
#define _LINUX_EDAC_H_
#include <linux/atomic.h>
#include <linux/device.h>
#include <linux/kobject.h>
#include <linux/completion.h>
#include <linux/workqueue.h>
#include <linux/debugfs.h>
struct device;
@@ -49,7 +51,19 @@ static inline void opstate_init(void)
#define EDAC_MC_LABEL_LEN 31
#define MC_PROC_NAME_MAX_LEN 7
/* memory devices */
/**
* enum dev_type - describe the type of memory DRAM chips used at the stick
* @DEV_UNKNOWN: Can't be determined, or MC doesn't support detect it
* @DEV_X1: 1 bit for data
* @DEV_X2: 2 bits for data
* @DEV_X4: 4 bits for data
* @DEV_X8: 8 bits for data
* @DEV_X16: 16 bits for data
* @DEV_X32: 32 bits for data
* @DEV_X64: 64 bits for data
*
* Typical values are x4 and x8.
*/
enum dev_type {
DEV_UNKNOWN = 0,
DEV_X1,
@@ -167,18 +181,30 @@ enum mem_type {
#define MEM_FLAG_DDR3 BIT(MEM_DDR3)
#define MEM_FLAG_RDDR3 BIT(MEM_RDDR3)
/* chipset Error Detection and Correction capabilities and mode */
/**
* enum edac-type - Error Detection and Correction capabilities and mode
* @EDAC_UNKNOWN: Unknown if ECC is available
* @EDAC_NONE: Doesn't support ECC
* @EDAC_RESERVED: Reserved ECC type
* @EDAC_PARITY: Detects parity errors
* @EDAC_EC: Error Checking - no correction
* @EDAC_SECDED: Single bit error correction, Double detection
* @EDAC_S2ECD2ED: Chipkill x2 devices - do these exist?
* @EDAC_S4ECD4ED: Chipkill x4 devices
* @EDAC_S8ECD8ED: Chipkill x8 devices
* @EDAC_S16ECD16ED: Chipkill x16 devices
*/
enum edac_type {
EDAC_UNKNOWN = 0, /* Unknown if ECC is available */
EDAC_NONE, /* Doesn't support ECC */
EDAC_RESERVED, /* Reserved ECC type */
EDAC_PARITY, /* Detects parity errors */
EDAC_EC, /* Error Checking - no correction */
EDAC_SECDED, /* Single bit error correction, Double detection */
EDAC_S2ECD2ED, /* Chipkill x2 devices - do these exist? */
EDAC_S4ECD4ED, /* Chipkill x4 devices */
EDAC_S8ECD8ED, /* Chipkill x8 devices */
EDAC_S16ECD16ED, /* Chipkill x16 devices */
EDAC_UNKNOWN = 0,
EDAC_NONE,
EDAC_RESERVED,
EDAC_PARITY,
EDAC_EC,
EDAC_SECDED,
EDAC_S2ECD2ED,
EDAC_S4ECD4ED,
EDAC_S8ECD8ED,
EDAC_S16ECD16ED,
};
#define EDAC_FLAG_UNKNOWN BIT(EDAC_UNKNOWN)
@@ -191,18 +217,30 @@ enum edac_type {
#define EDAC_FLAG_S8ECD8ED BIT(EDAC_S8ECD8ED)
#define EDAC_FLAG_S16ECD16ED BIT(EDAC_S16ECD16ED)
/* scrubbing capabilities */
/**
* enum scrub_type - scrubbing capabilities
* @SCRUB_UNKNOWN Unknown if scrubber is available
* @SCRUB_NONE: No scrubber
* @SCRUB_SW_PROG: SW progressive (sequential) scrubbing
* @SCRUB_SW_SRC: Software scrub only errors
* @SCRUB_SW_PROG_SRC: Progressive software scrub from an error
* @SCRUB_SW_TUNABLE: Software scrub frequency is tunable
* @SCRUB_HW_PROG: HW progressive (sequential) scrubbing
* @SCRUB_HW_SRC: Hardware scrub only errors
* @SCRUB_HW_PROG_SRC: Progressive hardware scrub from an error
* SCRUB_HW_TUNABLE: Hardware scrub frequency is tunable
*/
enum scrub_type {
SCRUB_UNKNOWN = 0, /* Unknown if scrubber is available */
SCRUB_NONE, /* No scrubber */
SCRUB_SW_PROG, /* SW progressive (sequential) scrubbing */
SCRUB_SW_SRC, /* Software scrub only errors */
SCRUB_SW_PROG_SRC, /* Progressive software scrub from an error */
SCRUB_SW_TUNABLE, /* Software scrub frequency is tunable */
SCRUB_HW_PROG, /* HW progressive (sequential) scrubbing */
SCRUB_HW_SRC, /* Hardware scrub only errors */
SCRUB_HW_PROG_SRC, /* Progressive hardware scrub from an error */
SCRUB_HW_TUNABLE /* Hardware scrub frequency is tunable */
SCRUB_UNKNOWN = 0,
SCRUB_NONE,
SCRUB_SW_PROG,
SCRUB_SW_SRC,
SCRUB_SW_PROG_SRC,
SCRUB_SW_TUNABLE,
SCRUB_HW_PROG,
SCRUB_HW_SRC,
SCRUB_HW_PROG_SRC,
SCRUB_HW_TUNABLE
};
#define SCRUB_FLAG_SW_PROG BIT(SCRUB_SW_PROG)
@@ -374,7 +412,44 @@ struct edac_mc_layer {
#define EDAC_MAX_LAYERS 3
/**
* EDAC_DIMM_PTR - Macro responsible to find a pointer inside a pointer array
* EDAC_DIMM_OFF - Macro responsible to get a pointer offset inside a pointer array
* for the element given by [layer0,layer1,layer2] position
*
* @layers: a struct edac_mc_layer array, describing how many elements
* were allocated for each layer
* @n_layers: Number of layers at the @layers array
* @layer0: layer0 position
* @layer1: layer1 position. Unused if n_layers < 2
* @layer2: layer2 position. Unused if n_layers < 3
*
* For 1 layer, this macro returns &var[layer0] - &var
* For 2 layers, this macro is similar to allocate a bi-dimensional array
* and to return "&var[layer0][layer1] - &var"
* For 3 layers, this macro is similar to allocate a tri-dimensional array
* and to return "&var[layer0][layer1][layer2] - &var"
*
* A loop could be used here to make it more generic, but, as we only have
* 3 layers, this is a little faster.
* By design, layers can never be 0 or more than 3. If that ever happens,
* a NULL is returned, causing an OOPS during the memory allocation routine,
* with would point to the developer that he's doing something wrong.
*/
#define EDAC_DIMM_OFF(layers, nlayers, layer0, layer1, layer2) ({ \
int __i; \
if ((nlayers) == 1) \
__i = layer0; \
else if ((nlayers) == 2) \
__i = (layer1) + ((layers[1]).size * (layer0)); \
else if ((nlayers) == 3) \
__i = (layer2) + ((layers[2]).size * ((layer1) + \
((layers[1]).size * (layer0)))); \
else \
__i = -EINVAL; \
__i; \
})
/**
* EDAC_DIMM_PTR - Macro responsible to get a pointer inside a pointer array
* for the element given by [layer0,layer1,layer2] position
*
* @layers: a struct edac_mc_layer array, describing how many elements
@@ -391,30 +466,20 @@ struct edac_mc_layer {
* and to return "&var[layer0][layer1]"
* For 3 layers, this macro is similar to allocate a tri-dimensional array
* and to return "&var[layer0][layer1][layer2]"
*
* A loop could be used here to make it more generic, but, as we only have
* 3 layers, this is a little faster.
* By design, layers can never be 0 or more than 3. If that ever happens,
* a NULL is returned, causing an OOPS during the memory allocation routine,
* with would point to the developer that he's doing something wrong.
*/
#define EDAC_DIMM_PTR(layers, var, nlayers, layer0, layer1, layer2) ({ \
typeof(var) __p; \
if ((nlayers) == 1) \
__p = &var[layer0]; \
else if ((nlayers) == 2) \
__p = &var[(layer1) + ((layers[1]).size * (layer0))]; \
else if ((nlayers) == 3) \
__p = &var[(layer2) + ((layers[2]).size * ((layer1) + \
((layers[1]).size * (layer0))))]; \
else \
typeof(*var) __p; \
int ___i = EDAC_DIMM_OFF(layers, nlayers, layer0, layer1, layer2); \
if (___i < 0) \
__p = NULL; \
else \
__p = (var)[___i]; \
__p; \
})
/* FIXME: add the proper per-location error counts */
struct dimm_info {
struct device dev;
char label[EDAC_MC_LABEL_LEN + 1]; /* DIMM label on motherboard */
/* Memory location data */
@@ -456,6 +521,8 @@ struct rank_info {
};
struct csrow_info {
struct device dev;
/* Used only by edac_mc_find_csrow_by_page() */
unsigned long first_page; /* first page number in csrow */
unsigned long last_page; /* last page number in csrow */
@@ -469,44 +536,26 @@ struct csrow_info {
struct mem_ctl_info *mci; /* the parent */
struct kobject kobj; /* sysfs kobject for this csrow */
/* channel information for this csrow */
u32 nr_channels;
struct rank_info *channels;
struct rank_info **channels;
};
struct mcidev_sysfs_group {
const char *name; /* group name */
const struct mcidev_sysfs_attribute *mcidev_attr; /* group attributes */
};
struct mcidev_sysfs_group_kobj {
struct list_head list; /* list for all instances within a mc */
struct kobject kobj; /* kobj for the group */
const struct mcidev_sysfs_group *grp; /* group description table */
struct mem_ctl_info *mci; /* the parent */
};
/* mcidev_sysfs_attribute structure
* used for driver sysfs attributes and in mem_ctl_info
* sysfs top level entries
/*
* struct errcount_attribute - used to store the several error counts
*/
struct mcidev_sysfs_attribute {
/* It should use either attr or grp */
struct attribute attr;
const struct mcidev_sysfs_group *grp; /* Points to a group of attributes */
/* Ops for show/store values at the attribute - not used on group */
ssize_t (*show)(struct mem_ctl_info *,char *);
ssize_t (*store)(struct mem_ctl_info *, const char *,size_t);
struct errcount_attribute_data {
int n_layers;
int pos[EDAC_MAX_LAYERS];
int layer0, layer1, layer2;
};
/* MEMORY controller information structure
*/
struct mem_ctl_info {
struct device dev;
struct bus_type bus;
struct list_head link; /* for global list of mem_ctl_info structs */
struct module *owner; /* Module owner of this control struct */
@@ -548,10 +597,18 @@ struct mem_ctl_info {
unsigned long (*ctl_page_to_phys) (struct mem_ctl_info * mci,
unsigned long page);
int mc_idx;
struct csrow_info *csrows;
struct csrow_info **csrows;
unsigned nr_csrows, num_cschannel;
/* Memory Controller hierarchy */
/*
* Memory Controller hierarchy
*
* There are basically two types of memory controller: the ones that
* sees memory sticks ("dimms"), and the ones that sees memory ranks.
* All old memory controllers enumerate memories per rank, but most
* of the recent drivers enumerate memories per DIMM, instead.
* When the memory controller is per rank, mem_is_per_rank is true.
*/
unsigned n_layers;
struct edac_mc_layer *layers;
bool mem_is_per_rank;
@@ -560,14 +617,14 @@ struct mem_ctl_info {
* DIMM info. Will eventually remove the entire csrows_info some day
*/
unsigned tot_dimms;
struct dimm_info *dimms;
struct dimm_info **dimms;
/*
* FIXME - what about controllers on other busses? - IDs must be
* unique. dev pointer should be sufficiently unique, but
* BUS:SLOT.FUNC numbers may not be unique.
*/
struct device *dev;
struct device *pdev;
const char *mod_name;
const char *mod_ver;
const char *ctl_name;
@@ -586,12 +643,6 @@ struct mem_ctl_info {
struct completion complete;
/* edac sysfs device control */
struct kobject edac_mci_kobj;
/* list for all grp instances within a mc */
struct list_head grp_kobj_list;
/* Additional top controller level attributes, but specified
* by the low level driver.
*
@@ -609,6 +660,13 @@ struct mem_ctl_info {
/* the internal state of this controller instance */
int op_state;
#ifdef CONFIG_EDAC_DEBUG
struct dentry *debugfs;
u8 fake_inject_layer[EDAC_MAX_LAYERS];
u32 fake_inject_ue;
u16 fake_inject_count;
#endif
};
#endif