Merge branch 'linux_next' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/i7core

* 'linux_next' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/i7core: (34 commits)
  i7core_edac: return -ENODEV when devices were already probed
  i7core_edac: properly terminate pci_dev_table
  i7core_edac: Avoid PCI refcount to reach zero on successive load/reload
  i7core_edac: Fix refcount error at PCI devices
  i7core_edac: it is safe to i7core_unregister_mci() when mci=NULL
  i7core_edac: Fix an oops at i7core probe
  i7core_edac: Remove unused member channels in i7core_pvt
  i7core_edac: Remove unused arg csrow from get_dimm_config
  i7core_edac: Reduce args of i7core_register_mci
  i7core_edac: Introduce i7core_unregister_mci
  i7core_edac: Use saved pointers
  i7core_edac: Check probe counter in i7core_remove
  i7core_edac: Call pci_dev_put() when alloc_i7core_dev()  failed
  i7core_edac: Fix error path of i7core_register_mci
  i7core_edac: Fix order of lines in i7core_register_mci
  i7core_edac: Always do get/put for all devices
  i7core_edac: Introduce i7core_pci_ctl_create/release
  i7core_edac: Introduce free_i7core_dev
  i7core_edac: Introduce alloc_i7core_dev
  i7core_edac: Reduce args of i7core_get_onedevice
  ...
This commit is contained in:
Linus Torvalds 2010-10-26 10:13:48 -07:00
commit da62aa69c1
4 changed files with 312 additions and 223 deletions

View File

@ -42,8 +42,10 @@
#if PAGE_SHIFT < 20 #if PAGE_SHIFT < 20
#define PAGES_TO_MiB( pages ) ( ( pages ) >> ( 20 - PAGE_SHIFT ) ) #define PAGES_TO_MiB( pages ) ( ( pages ) >> ( 20 - PAGE_SHIFT ) )
#define MiB_TO_PAGES(mb) ((mb) >> (20 - PAGE_SHIFT))
#else /* PAGE_SHIFT > 20 */ #else /* PAGE_SHIFT > 20 */
#define PAGES_TO_MiB( pages ) ( ( pages ) << ( PAGE_SHIFT - 20 ) ) #define PAGES_TO_MiB( pages ) ( ( pages ) << ( PAGE_SHIFT - 20 ) )
#define MiB_TO_PAGES(mb) ((mb) >> (PAGE_SHIFT - 20))
#endif #endif
#define edac_printk(level, prefix, fmt, arg...) \ #define edac_printk(level, prefix, fmt, arg...) \
@ -328,7 +330,7 @@ struct csrow_info {
struct mcidev_sysfs_group { struct mcidev_sysfs_group {
const char *name; /* group name */ const char *name; /* group name */
struct mcidev_sysfs_attribute *mcidev_attr; /* group attributes */ const struct mcidev_sysfs_attribute *mcidev_attr; /* group attributes */
}; };
struct mcidev_sysfs_group_kobj { struct mcidev_sysfs_group_kobj {
@ -336,7 +338,7 @@ struct mcidev_sysfs_group_kobj {
struct kobject kobj; /* kobj for the group */ struct kobject kobj; /* kobj for the group */
struct mcidev_sysfs_group *grp; /* group description table */ const struct mcidev_sysfs_group *grp; /* group description table */
struct mem_ctl_info *mci; /* the parent */ struct mem_ctl_info *mci; /* the parent */
}; };
@ -347,7 +349,7 @@ struct mcidev_sysfs_group_kobj {
struct mcidev_sysfs_attribute { struct mcidev_sysfs_attribute {
/* It should use either attr or grp */ /* It should use either attr or grp */
struct attribute attr; struct attribute attr;
struct mcidev_sysfs_group *grp; /* Points to a group of attributes */ const struct mcidev_sysfs_group *grp; /* Points to a group of attributes */
/* Ops for show/store values at the attribute - not used on group */ /* Ops for show/store values at the attribute - not used on group */
ssize_t (*show)(struct mem_ctl_info *,char *); ssize_t (*show)(struct mem_ctl_info *,char *);
@ -440,7 +442,7 @@ struct mem_ctl_info {
* If attributes are desired, then set to array of attributes * If attributes are desired, then set to array of attributes
* If no attributes are desired, leave NULL * If no attributes are desired, leave NULL
*/ */
struct mcidev_sysfs_attribute *mc_driver_sysfs_attributes; const struct mcidev_sysfs_attribute *mc_driver_sysfs_attributes;
/* work struct for this MC */ /* work struct for this MC */
struct delayed_work work; struct delayed_work work;
@ -810,6 +812,7 @@ extern struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
extern int edac_mc_add_mc(struct mem_ctl_info *mci); extern int edac_mc_add_mc(struct mem_ctl_info *mci);
extern void edac_mc_free(struct mem_ctl_info *mci); extern void edac_mc_free(struct mem_ctl_info *mci);
extern struct mem_ctl_info *edac_mc_find(int idx); extern struct mem_ctl_info *edac_mc_find(int idx);
extern struct mem_ctl_info *find_mci_by_dev(struct device *dev);
extern struct mem_ctl_info *edac_mc_del_mc(struct device *dev); extern struct mem_ctl_info *edac_mc_del_mc(struct device *dev);
extern int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, extern int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci,
unsigned long page); unsigned long page);

View File

@ -207,6 +207,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
} }
mci->op_state = OP_ALLOC; mci->op_state = OP_ALLOC;
INIT_LIST_HEAD(&mci->grp_kobj_list);
/* /*
* Initialize the 'root' kobj for the edac_mc controller * Initialize the 'root' kobj for the edac_mc controller
@ -234,18 +235,24 @@ EXPORT_SYMBOL_GPL(edac_mc_alloc);
*/ */
void edac_mc_free(struct mem_ctl_info *mci) void edac_mc_free(struct mem_ctl_info *mci)
{ {
debugf1("%s()\n", __func__);
edac_mc_unregister_sysfs_main_kobj(mci); edac_mc_unregister_sysfs_main_kobj(mci);
/* free the mci instance memory here */
kfree(mci);
} }
EXPORT_SYMBOL_GPL(edac_mc_free); EXPORT_SYMBOL_GPL(edac_mc_free);
/* /**
* find_mci_by_dev * find_mci_by_dev
* *
* scan list of controllers looking for the one that manages * scan list of controllers looking for the one that manages
* the 'dev' device * the 'dev' device
* @dev: pointer to a struct device related with the MCI
*/ */
static struct mem_ctl_info *find_mci_by_dev(struct device *dev) struct mem_ctl_info *find_mci_by_dev(struct device *dev)
{ {
struct mem_ctl_info *mci; struct mem_ctl_info *mci;
struct list_head *item; struct list_head *item;
@ -261,6 +268,7 @@ static struct mem_ctl_info *find_mci_by_dev(struct device *dev)
return NULL; return NULL;
} }
EXPORT_SYMBOL_GPL(find_mci_by_dev);
/* /*
* handler for EDAC to check if NMI type handler has asserted interrupt * handler for EDAC to check if NMI type handler has asserted interrupt

View File

@ -631,9 +631,6 @@ static void edac_mci_control_release(struct kobject *kobj)
/* decrement the module ref count */ /* decrement the module ref count */
module_put(mci->owner); module_put(mci->owner);
/* free the mci instance memory here */
kfree(mci);
} }
static struct kobj_type ktype_mci = { static struct kobj_type ktype_mci = {
@ -713,6 +710,8 @@ fail_out:
*/ */
void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci) void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci)
{ {
debugf1("%s()\n", __func__);
/* delete the kobj from the mc_kset */ /* delete the kobj from the mc_kset */
kobject_put(&mci->edac_mci_kobj); kobject_put(&mci->edac_mci_kobj);
} }
@ -760,8 +759,6 @@ static void edac_inst_grp_release(struct kobject *kobj)
grp = container_of(kobj, struct mcidev_sysfs_group_kobj, kobj); grp = container_of(kobj, struct mcidev_sysfs_group_kobj, kobj);
mci = grp->mci; mci = grp->mci;
kobject_put(&mci->edac_mci_kobj);
} }
/* Intermediate show/store table */ /* Intermediate show/store table */
@ -784,7 +781,7 @@ static struct kobj_type ktype_inst_grp = {
* object tree. * object tree.
*/ */
static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci, static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci,
struct mcidev_sysfs_attribute *sysfs_attrib, const struct mcidev_sysfs_attribute *sysfs_attrib,
struct kobject *kobj) struct kobject *kobj)
{ {
int err; int err;
@ -792,6 +789,7 @@ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci,
debugf1("%s()\n", __func__); debugf1("%s()\n", __func__);
while (sysfs_attrib) { while (sysfs_attrib) {
debugf1("%s() sysfs_attrib = %p\n",__func__, sysfs_attrib);
if (sysfs_attrib->grp) { if (sysfs_attrib->grp) {
struct mcidev_sysfs_group_kobj *grp_kobj; struct mcidev_sysfs_group_kobj *grp_kobj;
@ -799,10 +797,9 @@ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci,
if (!grp_kobj) if (!grp_kobj)
return -ENOMEM; return -ENOMEM;
list_add_tail(&grp_kobj->list, &mci->grp_kobj_list);
grp_kobj->grp = sysfs_attrib->grp; grp_kobj->grp = sysfs_attrib->grp;
grp_kobj->mci = mci; grp_kobj->mci = mci;
list_add_tail(&grp_kobj->list, &mci->grp_kobj_list);
debugf0("%s() grp %s, mci %p\n", __func__, debugf0("%s() grp %s, mci %p\n", __func__,
sysfs_attrib->grp->name, mci); sysfs_attrib->grp->name, mci);
@ -811,26 +808,28 @@ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci,
&ktype_inst_grp, &ktype_inst_grp,
&mci->edac_mci_kobj, &mci->edac_mci_kobj,
sysfs_attrib->grp->name); sysfs_attrib->grp->name);
if (err) if (err < 0) {
printk(KERN_ERR "kobject_init_and_add failed: %d\n", err);
return err; return err;
}
err = edac_create_mci_instance_attributes(mci, err = edac_create_mci_instance_attributes(mci,
grp_kobj->grp->mcidev_attr, grp_kobj->grp->mcidev_attr,
&grp_kobj->kobj); &grp_kobj->kobj);
if (err) if (err < 0)
return err; return err;
} else if (sysfs_attrib->attr.name) { } else if (sysfs_attrib->attr.name) {
debugf0("%s() file %s\n", __func__, debugf0("%s() file %s\n", __func__,
sysfs_attrib->attr.name); sysfs_attrib->attr.name);
err = sysfs_create_file(kobj, &sysfs_attrib->attr); err = sysfs_create_file(kobj, &sysfs_attrib->attr);
if (err < 0) {
printk(KERN_ERR "sysfs_create_file failed: %d\n", err);
return err;
}
} else } else
break; break;
if (err) {
return err;
}
sysfs_attrib++; sysfs_attrib++;
} }
@ -843,7 +842,7 @@ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci,
* directory of this mci instance. * directory of this mci instance.
*/ */
static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci, static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci,
struct mcidev_sysfs_attribute *sysfs_attrib, const struct mcidev_sysfs_attribute *sysfs_attrib,
struct kobject *kobj, int count) struct kobject *kobj, int count)
{ {
struct mcidev_sysfs_group_kobj *grp_kobj, *tmp; struct mcidev_sysfs_group_kobj *grp_kobj, *tmp;
@ -855,13 +854,24 @@ static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci,
* Remove first all the atributes * Remove first all the atributes
*/ */
while (sysfs_attrib) { while (sysfs_attrib) {
debugf1("%s() sysfs_attrib = %p\n",__func__, sysfs_attrib);
if (sysfs_attrib->grp) { if (sysfs_attrib->grp) {
list_for_each_entry(grp_kobj, &mci->grp_kobj_list, debugf1("%s() seeking for group %s\n",
list) __func__, sysfs_attrib->grp->name);
if (grp_kobj->grp == sysfs_attrib->grp) list_for_each_entry(grp_kobj,
&mci->grp_kobj_list, list) {
debugf1("%s() grp_kobj->grp = %p\n",__func__, grp_kobj->grp);
if (grp_kobj->grp == sysfs_attrib->grp) {
edac_remove_mci_instance_attributes(mci, edac_remove_mci_instance_attributes(mci,
grp_kobj->grp->mcidev_attr, grp_kobj->grp->mcidev_attr,
&grp_kobj->kobj, count + 1); &grp_kobj->kobj, count + 1);
debugf0("%s() group %s\n", __func__,
sysfs_attrib->grp->name);
kobject_put(&grp_kobj->kobj);
}
}
debugf1("%s() end of seeking for group %s\n",
__func__, sysfs_attrib->grp->name);
} else if (sysfs_attrib->attr.name) { } else if (sysfs_attrib->attr.name) {
debugf0("%s() file %s\n", __func__, debugf0("%s() file %s\n", __func__,
sysfs_attrib->attr.name); sysfs_attrib->attr.name);
@ -871,15 +881,14 @@ static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci,
sysfs_attrib++; sysfs_attrib++;
} }
/* /* Remove the group objects */
* Now that all attributes got removed, it is save to remove all groups if (count)
*/ return;
if (!count) list_for_each_entry_safe(grp_kobj, tmp,
list_for_each_entry_safe(grp_kobj, tmp, &mci->grp_kobj_list, &mci->grp_kobj_list, list) {
list) { list_del(&grp_kobj->list);
debugf0("%s() grp %s\n", __func__, grp_kobj->grp->name); kfree(grp_kobj);
kobject_put(&grp_kobj->kobj); }
}
} }
@ -971,6 +980,7 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
debugf0("%s()\n", __func__); debugf0("%s()\n", __func__);
/* remove all csrow kobjects */ /* remove all csrow kobjects */
debugf0("%s() unregister this mci kobj\n", __func__);
for (i = 0; i < mci->nr_csrows; i++) { for (i = 0; i < mci->nr_csrows; i++) {
if (mci->csrows[i].nr_pages > 0) { if (mci->csrows[i].nr_pages > 0) {
debugf0("%s() unreg csrow-%d\n", __func__, i); debugf0("%s() unreg csrow-%d\n", __func__, i);
@ -978,20 +988,20 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
} }
} }
debugf0("%s() remove_link\n", __func__); /* remove this mci instance's attribtes */
if (mci->mc_driver_sysfs_attributes) {
debugf0("%s() unregister mci private attributes\n", __func__);
edac_remove_mci_instance_attributes(mci,
mci->mc_driver_sysfs_attributes,
&mci->edac_mci_kobj, 0);
}
/* remove the symlink */ /* remove the symlink */
debugf0("%s() remove_link\n", __func__);
sysfs_remove_link(&mci->edac_mci_kobj, EDAC_DEVICE_SYMLINK); sysfs_remove_link(&mci->edac_mci_kobj, EDAC_DEVICE_SYMLINK);
debugf0("%s() remove_mci_instance\n", __func__);
/* remove this mci instance's attribtes */
edac_remove_mci_instance_attributes(mci,
mci->mc_driver_sysfs_attributes,
&mci->edac_mci_kobj, 0);
debugf0("%s() unregister this mci kobj\n", __func__);
/* unregister this instance's kobject */ /* unregister this instance's kobject */
debugf0("%s() remove_mci_instance\n", __func__);
kobject_put(&mci->edac_mci_kobj); kobject_put(&mci->edac_mci_kobj);
} }

View File

@ -39,6 +39,14 @@
#include "edac_core.h" #include "edac_core.h"
/* Static vars */
static LIST_HEAD(i7core_edac_list);
static DEFINE_MUTEX(i7core_edac_lock);
static int probed;
static int use_pci_fixup;
module_param(use_pci_fixup, int, 0444);
MODULE_PARM_DESC(use_pci_fixup, "Enable PCI fixup to seek for hidden devices");
/* /*
* This is used for Nehalem-EP and Nehalem-EX devices, where the non-core * This is used for Nehalem-EP and Nehalem-EX devices, where the non-core
* registers start at bus 255, and are not reported by BIOS. * registers start at bus 255, and are not reported by BIOS.
@ -212,8 +220,8 @@ struct pci_id_descr {
}; };
struct pci_id_table { struct pci_id_table {
struct pci_id_descr *descr; const struct pci_id_descr *descr;
int n_devs; int n_devs;
}; };
struct i7core_dev { struct i7core_dev {
@ -235,8 +243,6 @@ struct i7core_pvt {
struct i7core_inject inject; struct i7core_inject inject;
struct i7core_channel channel[NUM_CHANS]; struct i7core_channel channel[NUM_CHANS];
int channels; /* Number of active channels */
int ce_count_available; int ce_count_available;
int csrow_map[NUM_CHANS][MAX_DIMMS]; int csrow_map[NUM_CHANS][MAX_DIMMS];
@ -261,22 +267,22 @@ struct i7core_pvt {
/* Count indicator to show errors not got */ /* Count indicator to show errors not got */
unsigned mce_overrun; unsigned mce_overrun;
};
/* Static vars */ /* Struct to control EDAC polling */
static LIST_HEAD(i7core_edac_list); struct edac_pci_ctl_info *i7core_pci;
static DEFINE_MUTEX(i7core_edac_lock); };
#define PCI_DESCR(device, function, device_id) \ #define PCI_DESCR(device, function, device_id) \
.dev = (device), \ .dev = (device), \
.func = (function), \ .func = (function), \
.dev_id = (device_id) .dev_id = (device_id)
struct pci_id_descr pci_dev_descr_i7core_nehalem[] = { static const struct pci_id_descr pci_dev_descr_i7core_nehalem[] = {
/* Memory controller */ /* Memory controller */
{ PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) }, { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
{ PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) }, { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
/* Exists only for RDIMM */
/* Exists only for RDIMM */
{ PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS), .optional = 1 }, { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS), .optional = 1 },
{ PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) }, { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
@ -297,19 +303,9 @@ struct pci_id_descr pci_dev_descr_i7core_nehalem[] = {
{ PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) }, { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
{ PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) }, { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
{ PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) }, { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
/* Generic Non-core registers */
/*
* This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
* On Xeon 55xx, however, it has a different id (8086:2c40). So,
* the probing code needs to test for the other address in case of
* failure of this one
*/
{ PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NONCORE) },
}; };
struct pci_id_descr pci_dev_descr_lynnfield[] = { static const struct pci_id_descr pci_dev_descr_lynnfield[] = {
{ PCI_DESCR( 3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR) }, { PCI_DESCR( 3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR) },
{ PCI_DESCR( 3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD) }, { PCI_DESCR( 3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD) },
{ PCI_DESCR( 3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST) }, { PCI_DESCR( 3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST) },
@ -323,15 +319,9 @@ struct pci_id_descr pci_dev_descr_lynnfield[] = {
{ PCI_DESCR( 5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR) }, { PCI_DESCR( 5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR) },
{ PCI_DESCR( 5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK) }, { PCI_DESCR( 5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK) },
{ PCI_DESCR( 5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC) }, { PCI_DESCR( 5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC) },
/*
* This is the PCI device has an alternate address on some
* processors like Core i7 860
*/
{ PCI_DESCR( 0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE) },
}; };
struct pci_id_descr pci_dev_descr_i7core_westmere[] = { static const struct pci_id_descr pci_dev_descr_i7core_westmere[] = {
/* Memory controller */ /* Memory controller */
{ PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR_REV2) }, { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR_REV2) },
{ PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD_REV2) }, { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD_REV2) },
@ -356,17 +346,14 @@ struct pci_id_descr pci_dev_descr_i7core_westmere[] = {
{ PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_ADDR_REV2) }, { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_ADDR_REV2) },
{ PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_RANK_REV2) }, { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_RANK_REV2) },
{ PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_TC_REV2) }, { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_TC_REV2) },
/* Generic Non-core registers */
{ PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2) },
}; };
#define PCI_ID_TABLE_ENTRY(A) { A, ARRAY_SIZE(A) } #define PCI_ID_TABLE_ENTRY(A) { .descr=A, .n_devs = ARRAY_SIZE(A) }
struct pci_id_table pci_dev_table[] = { static const struct pci_id_table pci_dev_table[] = {
PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_nehalem), PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_nehalem),
PCI_ID_TABLE_ENTRY(pci_dev_descr_lynnfield), PCI_ID_TABLE_ENTRY(pci_dev_descr_lynnfield),
PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_westmere), PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_westmere),
{0,} /* 0 terminated list. */
}; };
/* /*
@ -378,8 +365,6 @@ static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
{0,} /* 0 terminated list. */ {0,} /* 0 terminated list. */
}; };
static struct edac_pci_ctl_info *i7core_pci;
/**************************************************************************** /****************************************************************************
Anciliary status routines Anciliary status routines
****************************************************************************/ ****************************************************************************/
@ -442,6 +427,36 @@ static struct i7core_dev *get_i7core_dev(u8 socket)
return NULL; return NULL;
} }
static struct i7core_dev *alloc_i7core_dev(u8 socket,
const struct pci_id_table *table)
{
struct i7core_dev *i7core_dev;
i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
if (!i7core_dev)
return NULL;
i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * table->n_devs,
GFP_KERNEL);
if (!i7core_dev->pdev) {
kfree(i7core_dev);
return NULL;
}
i7core_dev->socket = socket;
i7core_dev->n_devs = table->n_devs;
list_add_tail(&i7core_dev->list, &i7core_edac_list);
return i7core_dev;
}
static void free_i7core_dev(struct i7core_dev *i7core_dev)
{
list_del(&i7core_dev->list);
kfree(i7core_dev->pdev);
kfree(i7core_dev);
}
/**************************************************************************** /****************************************************************************
Memory check routines Memory check routines
****************************************************************************/ ****************************************************************************/
@ -484,7 +499,7 @@ static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
* to add a fake description for csrows. * to add a fake description for csrows.
* So, this driver is attributing one DIMM memory for one csrow. * So, this driver is attributing one DIMM memory for one csrow.
*/ */
static int i7core_get_active_channels(u8 socket, unsigned *channels, static int i7core_get_active_channels(const u8 socket, unsigned *channels,
unsigned *csrows) unsigned *csrows)
{ {
struct pci_dev *pdev = NULL; struct pci_dev *pdev = NULL;
@ -545,12 +560,13 @@ static int i7core_get_active_channels(u8 socket, unsigned *channels,
return 0; return 0;
} }
static int get_dimm_config(struct mem_ctl_info *mci, int *csrow) static int get_dimm_config(const struct mem_ctl_info *mci)
{ {
struct i7core_pvt *pvt = mci->pvt_info; struct i7core_pvt *pvt = mci->pvt_info;
struct csrow_info *csr; struct csrow_info *csr;
struct pci_dev *pdev; struct pci_dev *pdev;
int i, j; int i, j;
int csrow = 0;
unsigned long last_page = 0; unsigned long last_page = 0;
enum edac_type mode; enum edac_type mode;
enum mem_type mtype; enum mem_type mtype;
@ -664,13 +680,9 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow)
RANKOFFSET(dimm_dod[j]), RANKOFFSET(dimm_dod[j]),
banks, ranks, rows, cols); banks, ranks, rows, cols);
#if PAGE_SHIFT > 20 npages = MiB_TO_PAGES(size);
npages = size >> (PAGE_SHIFT - 20);
#else
npages = size << (20 - PAGE_SHIFT);
#endif
csr = &mci->csrows[*csrow]; csr = &mci->csrows[csrow];
csr->first_page = last_page + 1; csr->first_page = last_page + 1;
last_page += npages; last_page += npages;
csr->last_page = last_page; csr->last_page = last_page;
@ -678,13 +690,13 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow)
csr->page_mask = 0; csr->page_mask = 0;
csr->grain = 8; csr->grain = 8;
csr->csrow_idx = *csrow; csr->csrow_idx = csrow;
csr->nr_channels = 1; csr->nr_channels = 1;
csr->channels[0].chan_idx = i; csr->channels[0].chan_idx = i;
csr->channels[0].ce_count = 0; csr->channels[0].ce_count = 0;
pvt->csrow_map[i][j] = *csrow; pvt->csrow_map[i][j] = csrow;
switch (banks) { switch (banks) {
case 4: case 4:
@ -703,7 +715,7 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow)
csr->edac_mode = mode; csr->edac_mode = mode;
csr->mtype = mtype; csr->mtype = mtype;
(*csrow)++; csrow++;
} }
pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]); pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
@ -736,7 +748,7 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow)
we're disabling error injection on all write calls to the sysfs nodes that we're disabling error injection on all write calls to the sysfs nodes that
controls the error code injection. controls the error code injection.
*/ */
static int disable_inject(struct mem_ctl_info *mci) static int disable_inject(const struct mem_ctl_info *mci)
{ {
struct i7core_pvt *pvt = mci->pvt_info; struct i7core_pvt *pvt = mci->pvt_info;
@ -921,7 +933,7 @@ DECLARE_ADDR_MATCH(bank, 32);
DECLARE_ADDR_MATCH(page, 0x10000); DECLARE_ADDR_MATCH(page, 0x10000);
DECLARE_ADDR_MATCH(col, 0x4000); DECLARE_ADDR_MATCH(col, 0x4000);
static int write_and_test(struct pci_dev *dev, int where, u32 val) static int write_and_test(struct pci_dev *dev, const int where, const u32 val)
{ {
u32 read; u32 read;
int count; int count;
@ -1120,35 +1132,34 @@ DECLARE_COUNTER(2);
* Sysfs struct * Sysfs struct
*/ */
static const struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = {
static struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = {
ATTR_ADDR_MATCH(channel), ATTR_ADDR_MATCH(channel),
ATTR_ADDR_MATCH(dimm), ATTR_ADDR_MATCH(dimm),
ATTR_ADDR_MATCH(rank), ATTR_ADDR_MATCH(rank),
ATTR_ADDR_MATCH(bank), ATTR_ADDR_MATCH(bank),
ATTR_ADDR_MATCH(page), ATTR_ADDR_MATCH(page),
ATTR_ADDR_MATCH(col), ATTR_ADDR_MATCH(col),
{ .attr = { .name = NULL } } { } /* End of list */
}; };
static struct mcidev_sysfs_group i7core_inject_addrmatch = { static const struct mcidev_sysfs_group i7core_inject_addrmatch = {
.name = "inject_addrmatch", .name = "inject_addrmatch",
.mcidev_attr = i7core_addrmatch_attrs, .mcidev_attr = i7core_addrmatch_attrs,
}; };
static struct mcidev_sysfs_attribute i7core_udimm_counters_attrs[] = { static const struct mcidev_sysfs_attribute i7core_udimm_counters_attrs[] = {
ATTR_COUNTER(0), ATTR_COUNTER(0),
ATTR_COUNTER(1), ATTR_COUNTER(1),
ATTR_COUNTER(2), ATTR_COUNTER(2),
{ .attr = { .name = NULL } } { .attr = { .name = NULL } }
}; };
static struct mcidev_sysfs_group i7core_udimm_counters = { static const struct mcidev_sysfs_group i7core_udimm_counters = {
.name = "all_channel_counts", .name = "all_channel_counts",
.mcidev_attr = i7core_udimm_counters_attrs, .mcidev_attr = i7core_udimm_counters_attrs,
}; };
static struct mcidev_sysfs_attribute i7core_sysfs_attrs[] = { static const struct mcidev_sysfs_attribute i7core_sysfs_rdimm_attrs[] = {
{ {
.attr = { .attr = {
.name = "inject_section", .name = "inject_section",
@ -1180,8 +1191,44 @@ static struct mcidev_sysfs_attribute i7core_sysfs_attrs[] = {
.show = i7core_inject_enable_show, .show = i7core_inject_enable_show,
.store = i7core_inject_enable_store, .store = i7core_inject_enable_store,
}, },
{ .attr = { .name = NULL } }, /* Reserved for udimm counters */ { } /* End of list */
{ .attr = { .name = NULL } } };
static const struct mcidev_sysfs_attribute i7core_sysfs_udimm_attrs[] = {
{
.attr = {
.name = "inject_section",
.mode = (S_IRUGO | S_IWUSR)
},
.show = i7core_inject_section_show,
.store = i7core_inject_section_store,
}, {
.attr = {
.name = "inject_type",
.mode = (S_IRUGO | S_IWUSR)
},
.show = i7core_inject_type_show,
.store = i7core_inject_type_store,
}, {
.attr = {
.name = "inject_eccmask",
.mode = (S_IRUGO | S_IWUSR)
},
.show = i7core_inject_eccmask_show,
.store = i7core_inject_eccmask_store,
}, {
.grp = &i7core_inject_addrmatch,
}, {
.attr = {
.name = "inject_enable",
.mode = (S_IRUGO | S_IWUSR)
},
.show = i7core_inject_enable_show,
.store = i7core_inject_enable_store,
}, {
.grp = &i7core_udimm_counters,
},
{ } /* End of list */
}; };
/**************************************************************************** /****************************************************************************
@ -1189,7 +1236,7 @@ static struct mcidev_sysfs_attribute i7core_sysfs_attrs[] = {
****************************************************************************/ ****************************************************************************/
/* /*
* i7core_put_devices 'put' all the devices that we have * i7core_put_all_devices 'put' all the devices that we have
* reserved via 'get' * reserved via 'get'
*/ */
static void i7core_put_devices(struct i7core_dev *i7core_dev) static void i7core_put_devices(struct i7core_dev *i7core_dev)
@ -1206,23 +1253,23 @@ static void i7core_put_devices(struct i7core_dev *i7core_dev)
PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
pci_dev_put(pdev); pci_dev_put(pdev);
} }
kfree(i7core_dev->pdev);
list_del(&i7core_dev->list);
kfree(i7core_dev);
} }
static void i7core_put_all_devices(void) static void i7core_put_all_devices(void)
{ {
struct i7core_dev *i7core_dev, *tmp; struct i7core_dev *i7core_dev, *tmp;
list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) {
i7core_put_devices(i7core_dev); i7core_put_devices(i7core_dev);
free_i7core_dev(i7core_dev);
}
} }
static void __init i7core_xeon_pci_fixup(struct pci_id_table *table) static void __init i7core_xeon_pci_fixup(const struct pci_id_table *table)
{ {
struct pci_dev *pdev = NULL; struct pci_dev *pdev = NULL;
int i; int i;
/* /*
* On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
* aren't announced by acpi. So, we need to use a legacy scan probing * aren't announced by acpi. So, we need to use a legacy scan probing
@ -1257,16 +1304,18 @@ static unsigned i7core_pci_lastbus(void)
} }
/* /*
* i7core_get_devices Find and perform 'get' operation on the MCH's * i7core_get_all_devices Find and perform 'get' operation on the MCH's
* device/functions we want to reference for this driver * device/functions we want to reference for this driver
* *
* Need to 'get' device 16 func 1 and func 2 * Need to 'get' device 16 func 1 and func 2
*/ */
int i7core_get_onedevice(struct pci_dev **prev, int devno, static int i7core_get_onedevice(struct pci_dev **prev,
struct pci_id_descr *dev_descr, unsigned n_devs, const struct pci_id_table *table,
unsigned last_bus) const unsigned devno,
const unsigned last_bus)
{ {
struct i7core_dev *i7core_dev; struct i7core_dev *i7core_dev;
const struct pci_id_descr *dev_descr = &table->descr[devno];
struct pci_dev *pdev = NULL; struct pci_dev *pdev = NULL;
u8 bus = 0; u8 bus = 0;
@ -1275,20 +1324,6 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno,
pdev = pci_get_device(PCI_VENDOR_ID_INTEL, pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
dev_descr->dev_id, *prev); dev_descr->dev_id, *prev);
/*
* On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
* is at addr 8086:2c40, instead of 8086:2c41. So, we need
* to probe for the alternate address in case of failure
*/
if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_I7_NONCORE && !pdev)
pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT, *prev);
if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE && !pdev)
pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT,
*prev);
if (!pdev) { if (!pdev) {
if (*prev) { if (*prev) {
*prev = pdev; *prev = pdev;
@ -1315,18 +1350,11 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno,
i7core_dev = get_i7core_dev(socket); i7core_dev = get_i7core_dev(socket);
if (!i7core_dev) { if (!i7core_dev) {
i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL); i7core_dev = alloc_i7core_dev(socket, table);
if (!i7core_dev) if (!i7core_dev) {
return -ENOMEM; pci_dev_put(pdev);
i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * n_devs,
GFP_KERNEL);
if (!i7core_dev->pdev) {
kfree(i7core_dev);
return -ENOMEM; return -ENOMEM;
} }
i7core_dev->socket = socket;
i7core_dev->n_devs = n_devs;
list_add_tail(&i7core_dev->list, &i7core_edac_list);
} }
if (i7core_dev->pdev[devno]) { if (i7core_dev->pdev[devno]) {
@ -1368,27 +1396,31 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno,
dev_descr->func, dev_descr->func,
PCI_VENDOR_ID_INTEL, dev_descr->dev_id); PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
/*
* As stated on drivers/pci/search.c, the reference count for
* @from is always decremented if it is not %NULL. So, as we need
* to get all devices up to null, we need to do a get for the device
*/
pci_dev_get(pdev);
*prev = pdev; *prev = pdev;
return 0; return 0;
} }
static int i7core_get_devices(struct pci_id_table *table) static int i7core_get_all_devices(void)
{ {
int i, rc, last_bus; int i, rc, last_bus;
struct pci_dev *pdev = NULL; struct pci_dev *pdev = NULL;
struct pci_id_descr *dev_descr; const struct pci_id_table *table = pci_dev_table;
last_bus = i7core_pci_lastbus(); last_bus = i7core_pci_lastbus();
while (table && table->descr) { while (table && table->descr) {
dev_descr = table->descr;
for (i = 0; i < table->n_devs; i++) { for (i = 0; i < table->n_devs; i++) {
pdev = NULL; pdev = NULL;
do { do {
rc = i7core_get_onedevice(&pdev, i, rc = i7core_get_onedevice(&pdev, table, i,
&dev_descr[i],
table->n_devs,
last_bus); last_bus);
if (rc < 0) { if (rc < 0) {
if (i == 0) { if (i == 0) {
@ -1404,7 +1436,6 @@ static int i7core_get_devices(struct pci_id_table *table)
} }
return 0; return 0;
return 0;
} }
static int mci_bind_devs(struct mem_ctl_info *mci, static int mci_bind_devs(struct mem_ctl_info *mci,
@ -1414,10 +1445,6 @@ static int mci_bind_devs(struct mem_ctl_info *mci,
struct pci_dev *pdev; struct pci_dev *pdev;
int i, func, slot; int i, func, slot;
/* Associates i7core_dev and mci for future usage */
pvt->i7core_dev = i7core_dev;
i7core_dev->mci = mci;
pvt->is_registered = 0; pvt->is_registered = 0;
for (i = 0; i < i7core_dev->n_devs; i++) { for (i = 0; i < i7core_dev->n_devs; i++) {
pdev = i7core_dev->pdev[i]; pdev = i7core_dev->pdev[i];
@ -1448,15 +1475,6 @@ static int mci_bind_devs(struct mem_ctl_info *mci,
pvt->is_registered = 1; pvt->is_registered = 1;
} }
/*
* Add extra nodes to count errors on udimm
* For registered memory, this is not needed, since the counters
* are already displayed at the standard locations
*/
if (!pvt->is_registered)
i7core_sysfs_attrs[ARRAY_SIZE(i7core_sysfs_attrs)-2].grp =
&i7core_udimm_counters;
return 0; return 0;
error: error:
@ -1470,7 +1488,9 @@ error:
Error check routines Error check routines
****************************************************************************/ ****************************************************************************/
static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci, static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci,
int chan, int dimm, int add) const int chan,
const int dimm,
const int add)
{ {
char *msg; char *msg;
struct i7core_pvt *pvt = mci->pvt_info; struct i7core_pvt *pvt = mci->pvt_info;
@ -1487,7 +1507,10 @@ static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci,
} }
static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci, static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
int chan, int new0, int new1, int new2) const int chan,
const int new0,
const int new1,
const int new2)
{ {
struct i7core_pvt *pvt = mci->pvt_info; struct i7core_pvt *pvt = mci->pvt_info;
int add0 = 0, add1 = 0, add2 = 0; int add0 = 0, add1 = 0, add2 = 0;
@ -1641,7 +1664,7 @@ static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci)
* fields * fields
*/ */
static void i7core_mce_output_error(struct mem_ctl_info *mci, static void i7core_mce_output_error(struct mem_ctl_info *mci,
struct mce *m) const struct mce *m)
{ {
struct i7core_pvt *pvt = mci->pvt_info; struct i7core_pvt *pvt = mci->pvt_info;
char *type, *optype, *err, *msg; char *type, *optype, *err, *msg;
@ -1845,28 +1868,85 @@ static int i7core_mce_check_error(void *priv, struct mce *mce)
return 1; return 1;
} }
static int i7core_register_mci(struct i7core_dev *i7core_dev, static void i7core_pci_ctl_create(struct i7core_pvt *pvt)
int num_channels, int num_csrows) {
pvt->i7core_pci = edac_pci_create_generic_ctl(
&pvt->i7core_dev->pdev[0]->dev,
EDAC_MOD_STR);
if (unlikely(!pvt->i7core_pci))
pr_warn("Unable to setup PCI error report via EDAC\n");
}
static void i7core_pci_ctl_release(struct i7core_pvt *pvt)
{
if (likely(pvt->i7core_pci))
edac_pci_release_generic_ctl(pvt->i7core_pci);
else
i7core_printk(KERN_ERR,
"Couldn't find mem_ctl_info for socket %d\n",
pvt->i7core_dev->socket);
pvt->i7core_pci = NULL;
}
static void i7core_unregister_mci(struct i7core_dev *i7core_dev)
{
struct mem_ctl_info *mci = i7core_dev->mci;
struct i7core_pvt *pvt;
if (unlikely(!mci || !mci->pvt_info)) {
debugf0("MC: " __FILE__ ": %s(): dev = %p\n",
__func__, &i7core_dev->pdev[0]->dev);
i7core_printk(KERN_ERR, "Couldn't find mci handler\n");
return;
}
pvt = mci->pvt_info;
debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
__func__, mci, &i7core_dev->pdev[0]->dev);
/* Disable MCE NMI handler */
edac_mce_unregister(&pvt->edac_mce);
/* Disable EDAC polling */
i7core_pci_ctl_release(pvt);
/* Remove MC sysfs nodes */
edac_mc_del_mc(mci->dev);
debugf1("%s: free mci struct\n", mci->ctl_name);
kfree(mci->ctl_name);
edac_mc_free(mci);
i7core_dev->mci = NULL;
}
static int i7core_register_mci(struct i7core_dev *i7core_dev)
{ {
struct mem_ctl_info *mci; struct mem_ctl_info *mci;
struct i7core_pvt *pvt; struct i7core_pvt *pvt;
int csrow = 0; int rc, channels, csrows;
int rc;
/* Check the number of active and not disabled channels */
rc = i7core_get_active_channels(i7core_dev->socket, &channels, &csrows);
if (unlikely(rc < 0))
return rc;
/* allocate a new MC control structure */ /* allocate a new MC control structure */
mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, mci = edac_mc_alloc(sizeof(*pvt), csrows, channels, i7core_dev->socket);
i7core_dev->socket);
if (unlikely(!mci)) if (unlikely(!mci))
return -ENOMEM; return -ENOMEM;
debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci); debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
__func__, mci, &i7core_dev->pdev[0]->dev);
/* record ptr to the generic device */
mci->dev = &i7core_dev->pdev[0]->dev;
pvt = mci->pvt_info; pvt = mci->pvt_info;
memset(pvt, 0, sizeof(*pvt)); memset(pvt, 0, sizeof(*pvt));
/* Associates i7core_dev and mci for future usage */
pvt->i7core_dev = i7core_dev;
i7core_dev->mci = mci;
/* /*
* FIXME: how to handle RDDR3 at MCI level? It is possible to have * FIXME: how to handle RDDR3 at MCI level? It is possible to have
* Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
@ -1881,17 +1961,23 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev,
i7core_dev->socket); i7core_dev->socket);
mci->dev_name = pci_name(i7core_dev->pdev[0]); mci->dev_name = pci_name(i7core_dev->pdev[0]);
mci->ctl_page_to_phys = NULL; mci->ctl_page_to_phys = NULL;
mci->mc_driver_sysfs_attributes = i7core_sysfs_attrs;
/* Set the function pointer to an actual operation function */
mci->edac_check = i7core_check_error;
/* Store pci devices at mci for faster access */ /* Store pci devices at mci for faster access */
rc = mci_bind_devs(mci, i7core_dev); rc = mci_bind_devs(mci, i7core_dev);
if (unlikely(rc < 0)) if (unlikely(rc < 0))
goto fail; goto fail0;
if (pvt->is_registered)
mci->mc_driver_sysfs_attributes = i7core_sysfs_rdimm_attrs;
else
mci->mc_driver_sysfs_attributes = i7core_sysfs_udimm_attrs;
/* Get dimm basic config */ /* Get dimm basic config */
get_dimm_config(mci, &csrow); get_dimm_config(mci);
/* record ptr to the generic device */
mci->dev = &i7core_dev->pdev[0]->dev;
/* Set the function pointer to an actual operation function */
mci->edac_check = i7core_check_error;
/* add this new MC control structure to EDAC's list of MCs */ /* add this new MC control structure to EDAC's list of MCs */
if (unlikely(edac_mc_add_mc(mci))) { if (unlikely(edac_mc_add_mc(mci))) {
@ -1902,19 +1988,7 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev,
*/ */
rc = -EINVAL; rc = -EINVAL;
goto fail; goto fail0;
}
/* allocating generic PCI control info */
i7core_pci = edac_pci_create_generic_ctl(&i7core_dev->pdev[0]->dev,
EDAC_MOD_STR);
if (unlikely(!i7core_pci)) {
printk(KERN_WARNING
"%s(): Unable to create PCI control\n",
__func__);
printk(KERN_WARNING
"%s(): PCI error report via EDAC not setup\n",
__func__);
} }
/* Default error mask is any memory */ /* Default error mask is any memory */
@ -1925,19 +1999,28 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev,
pvt->inject.page = -1; pvt->inject.page = -1;
pvt->inject.col = -1; pvt->inject.col = -1;
/* allocating generic PCI control info */
i7core_pci_ctl_create(pvt);
/* Registers on edac_mce in order to receive memory errors */ /* Registers on edac_mce in order to receive memory errors */
pvt->edac_mce.priv = mci; pvt->edac_mce.priv = mci;
pvt->edac_mce.check_error = i7core_mce_check_error; pvt->edac_mce.check_error = i7core_mce_check_error;
rc = edac_mce_register(&pvt->edac_mce); rc = edac_mce_register(&pvt->edac_mce);
if (unlikely(rc < 0)) { if (unlikely(rc < 0)) {
debugf0("MC: " __FILE__ debugf0("MC: " __FILE__
": %s(): failed edac_mce_register()\n", __func__); ": %s(): failed edac_mce_register()\n", __func__);
goto fail1;
} }
fail: return 0;
if (rc < 0)
edac_mc_free(mci); fail1:
i7core_pci_ctl_release(pvt);
edac_mc_del_mc(mci->dev);
fail0:
kfree(mci->ctl_name);
edac_mc_free(mci);
i7core_dev->mci = NULL;
return rc; return rc;
} }
@ -1949,8 +2032,6 @@ fail:
* < 0 for error code * < 0 for error code
*/ */
static int probed = 0;
static int __devinit i7core_probe(struct pci_dev *pdev, static int __devinit i7core_probe(struct pci_dev *pdev,
const struct pci_device_id *id) const struct pci_device_id *id)
{ {
@ -1965,25 +2046,16 @@ static int __devinit i7core_probe(struct pci_dev *pdev,
*/ */
if (unlikely(probed >= 1)) { if (unlikely(probed >= 1)) {
mutex_unlock(&i7core_edac_lock); mutex_unlock(&i7core_edac_lock);
return -EINVAL; return -ENODEV;
} }
probed++; probed++;
rc = i7core_get_devices(pci_dev_table); rc = i7core_get_all_devices();
if (unlikely(rc < 0)) if (unlikely(rc < 0))
goto fail0; goto fail0;
list_for_each_entry(i7core_dev, &i7core_edac_list, list) { list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
int channels; rc = i7core_register_mci(i7core_dev);
int csrows;
/* Check the number of active and not disabled channels */
rc = i7core_get_active_channels(i7core_dev->socket,
&channels, &csrows);
if (unlikely(rc < 0))
goto fail1;
rc = i7core_register_mci(i7core_dev, channels, csrows);
if (unlikely(rc < 0)) if (unlikely(rc < 0))
goto fail1; goto fail1;
} }
@ -1994,6 +2066,9 @@ static int __devinit i7core_probe(struct pci_dev *pdev,
return 0; return 0;
fail1: fail1:
list_for_each_entry(i7core_dev, &i7core_edac_list, list)
i7core_unregister_mci(i7core_dev);
i7core_put_all_devices(); i7core_put_all_devices();
fail0: fail0:
mutex_unlock(&i7core_edac_lock); mutex_unlock(&i7core_edac_lock);
@ -2006,14 +2081,10 @@ fail0:
*/ */
static void __devexit i7core_remove(struct pci_dev *pdev) static void __devexit i7core_remove(struct pci_dev *pdev)
{ {
struct mem_ctl_info *mci; struct i7core_dev *i7core_dev;
struct i7core_dev *i7core_dev, *tmp;
debugf0(__FILE__ ": %s()\n", __func__); debugf0(__FILE__ ": %s()\n", __func__);
if (i7core_pci)
edac_pci_release_generic_ctl(i7core_pci);
/* /*
* we have a trouble here: pdev value for removal will be wrong, since * we have a trouble here: pdev value for removal will be wrong, since
* it will point to the X58 register used to detect that the machine * it will point to the X58 register used to detect that the machine
@ -2023,22 +2094,18 @@ static void __devexit i7core_remove(struct pci_dev *pdev)
*/ */
mutex_lock(&i7core_edac_lock); mutex_lock(&i7core_edac_lock);
list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) {
mci = edac_mc_del_mc(&i7core_dev->pdev[0]->dev);
if (mci) {
struct i7core_pvt *pvt = mci->pvt_info;
i7core_dev = pvt->i7core_dev; if (unlikely(!probed)) {
edac_mce_unregister(&pvt->edac_mce); mutex_unlock(&i7core_edac_lock);
kfree(mci->ctl_name); return;
edac_mc_free(mci);
i7core_put_devices(i7core_dev);
} else {
i7core_printk(KERN_ERR,
"Couldn't find mci for socket %d\n",
i7core_dev->socket);
}
} }
list_for_each_entry(i7core_dev, &i7core_edac_list, list)
i7core_unregister_mci(i7core_dev);
/* Release PCI resources */
i7core_put_all_devices();
probed--; probed--;
mutex_unlock(&i7core_edac_lock); mutex_unlock(&i7core_edac_lock);
@ -2070,7 +2137,8 @@ static int __init i7core_init(void)
/* Ensure that the OPSTATE is set correctly for POLL or NMI */ /* Ensure that the OPSTATE is set correctly for POLL or NMI */
opstate_init(); opstate_init();
i7core_xeon_pci_fixup(pci_dev_table); if (use_pci_fixup)
i7core_xeon_pci_fixup(pci_dev_table);
pci_rc = pci_register_driver(&i7core_driver); pci_rc = pci_register_driver(&i7core_driver);