mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-24 12:44:11 +08:00
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:
commit
da62aa69c1
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user