/* Based on drivers/edac/i82860_edac.c */
#define I855_PCI_DEVICE_ID 0x3584 /* PCI Device ID of the memory
controller in the 855 GME */
#define I855_ERRSTS_REGISTER 0x62 /* Error Status Register's offset
in the PCI configuration space */
#define I855_EAP_REGISTER 0x98 /* Error Address Pointer Register's
offset in the PCI configuration space */
struct i855_error_info {
u16 errsts; /* Error Type */
u32 eap; /* Error Location */
};
/* Get error information */
static void
i855_get_error_info(struct mem_ctl_info *mci,
struct i855_error_info *info)
{
struct pci_dev *pdev;
pdev = to_pci_dev(mci->dev);
/* Read error type */
pci_read_config_word(pdev, I855_ERRSTS_REGISTER, &info->errsts);
/* Read error location */
pci_read_config_dword(pdev, I855_EAP_REGISTER, &info->eap);
}
/* Process errors */
static int
i855_process_error_info(struct mem_ctl_info *mci,
struct i855_error_info *info,
int handle_errors)
{
int row;
info->eap >>= PAGE_SHIFT;
row = edac_mc_find_csrow_by_page(mci, info->eap); /* Find culprit row */
/* Handle using services provided by the EDAC core.
Populate sysfs, generate error messages, and so on */
if (is_MBE()) { /* is_MBE() looks at I855_ERRSTS_REGISTER and checks
for an MBE. Implementation not shown */
edac_mc_handle_ue(mci, info->eap, 0, row, "i855 UE");
} else if (is_SBE()) { /* is_SBE() looks at I855_ERRSTS_REGISTER and checks
for an SBE. Implementation not shown */
edac_mc_handle_ce(mci, info->eap, 0, info->derrsyn, row, 0,
"i855 CE");
}
return 1;
}
/* This method is registered with the EDAC core from i855_probe() */
static void
i855_check(struct mem_ctl_info *mci)
{
struct i855_error_info info;
i855_get_error_info(mci, &info);
i855_process_error_info(mci, &info, 1);
}
/* The PCI driver probe method, part of the pci_driver structure */
static int
i855_probe(struct pci_dev *pdev, int dev_idx)
{
struct mem_ctl_info *mci;
/* ... */
pci_enable_device(pdev);
/* Allocate control memory for this memory controller.
The 3 arguments to edac_mc_alloc() correspond to the
amount of requested private storage, number of chip-select
rows, and number of channels in your memory layout */
mci = edac_mc_alloc(0, CSROWS, CHANNELS);
/* ... */
mci->edac_check = i855_check; /* Supply the check method to the
EDAC core */
/* Do other memory controller initializations */
/* ... */
/* Register this memory controller with the EDAC core */
edac_mc_add_mc(mci, 0);
/* ... */
}
/* Remove method */
static void __devexit
i855_remove(struct pci_dev *pdev)
{
struct mem_ctl_info *mci = edac_mc_find_mci_by_pdev(pdev);
if (mci && !edac_mc_del_mc(mci)) {
edac_mc_free(mci); /* Free memory for this controller. Reverse
of edac_mc_alloc() */
}
}
/* PCI Device ID Table */
static const struct pci_device_id i855_pci_tbl[] __devinitdata = {
{PCI_VEND_DEV(INTEL, I855_PCI_DEVICE_ID),
PCI_ANY_ID, PCI_ANY_ID, 0, 0,},
{0,},
};
MODULE_DEVICE_TABLE(pci, i855_pci_tbl);
/* pci_driver structure for this device.
Re-visit Chapter 10 for a detailed explanation */
static struct pci_driver i855_driver = {
.name = "855",
.probe = i855_probe,
.remove = __devexit_p(i855_remove),
.id_table = i855_pci_tbl,
};
/* Driver Initialization */
static int __init
i855_init(void)
{
/* ... */
pci_rc = pci_register_driver(&i855_driver);
/* ... */
}
syntax highlighted by Code2HTML, v. 0.9.1