|
@@ -765,22 +765,174 @@ static void e752x_check(struct mem_ctl_info *mci)
|
|
|
e752x_process_error_info(mci, &info, 1);
|
|
|
}
|
|
|
|
|
|
-static int e752x_probe1(struct pci_dev *pdev, int dev_idx)
|
|
|
+/* Return 1 if dual channel mode is active. Else return 0. */
|
|
|
+static inline int dual_channel_active(u16 ddrcsr)
|
|
|
+{
|
|
|
+ return (((ddrcsr >> 12) & 3) == 3);
|
|
|
+}
|
|
|
+
|
|
|
+static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
|
|
|
+ u16 ddrcsr)
|
|
|
+{
|
|
|
+ struct csrow_info *csrow;
|
|
|
+ unsigned long last_cumul_size;
|
|
|
+ int index, mem_dev, drc_chan;
|
|
|
+ int drc_drbg; /* DRB granularity 0=64mb, 1=128mb */
|
|
|
+ int drc_ddim; /* DRAM Data Integrity Mode 0=none, 2=edac */
|
|
|
+ u8 value;
|
|
|
+ u32 dra, drc, cumul_size;
|
|
|
+
|
|
|
+ pci_read_config_dword(pdev, E752X_DRA, &dra);
|
|
|
+ pci_read_config_dword(pdev, E752X_DRC, &drc);
|
|
|
+ drc_chan = dual_channel_active(ddrcsr);
|
|
|
+ drc_drbg = drc_chan + 1; /* 128 in dual mode, 64 in single */
|
|
|
+ drc_ddim = (drc >> 20) & 0x3;
|
|
|
+
|
|
|
+ /* The dram row boundary (DRB) reg values are boundary address for
|
|
|
+ * each DRAM row with a granularity of 64 or 128MB (single/dual
|
|
|
+ * channel operation). DRB regs are cumulative; therefore DRB7 will
|
|
|
+ * contain the total memory contained in all eight rows.
|
|
|
+ */
|
|
|
+ for (last_cumul_size = index = 0; index < mci->nr_csrows; index++) {
|
|
|
+ /* mem_dev 0=x8, 1=x4 */
|
|
|
+ mem_dev = (dra >> (index * 4 + 2)) & 0x3;
|
|
|
+ csrow = &mci->csrows[index];
|
|
|
+
|
|
|
+ mem_dev = (mem_dev == 2);
|
|
|
+ pci_read_config_byte(pdev, E752X_DRB + index, &value);
|
|
|
+ /* convert a 128 or 64 MiB DRB to a page size. */
|
|
|
+ cumul_size = value << (25 + drc_drbg - PAGE_SHIFT);
|
|
|
+ debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index,
|
|
|
+ cumul_size);
|
|
|
+ if (cumul_size == last_cumul_size)
|
|
|
+ continue; /* not populated */
|
|
|
+
|
|
|
+ csrow->first_page = last_cumul_size;
|
|
|
+ csrow->last_page = cumul_size - 1;
|
|
|
+ csrow->nr_pages = cumul_size - last_cumul_size;
|
|
|
+ last_cumul_size = cumul_size;
|
|
|
+ csrow->grain = 1 << 12; /* 4KiB - resolution of CELOG */
|
|
|
+ csrow->mtype = MEM_RDDR; /* only one type supported */
|
|
|
+ csrow->dtype = mem_dev ? DEV_X4 : DEV_X8;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * if single channel or x8 devices then SECDED
|
|
|
+ * if dual channel and x4 then S4ECD4ED
|
|
|
+ */
|
|
|
+ if (drc_ddim) {
|
|
|
+ if (drc_chan && mem_dev) {
|
|
|
+ csrow->edac_mode = EDAC_S4ECD4ED;
|
|
|
+ mci->edac_cap |= EDAC_FLAG_S4ECD4ED;
|
|
|
+ } else {
|
|
|
+ csrow->edac_mode = EDAC_SECDED;
|
|
|
+ mci->edac_cap |= EDAC_FLAG_SECDED;
|
|
|
+ }
|
|
|
+ } else
|
|
|
+ csrow->edac_mode = EDAC_NONE;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void e752x_init_mem_map_table(struct pci_dev *pdev,
|
|
|
+ struct e752x_pvt *pvt)
|
|
|
{
|
|
|
- int rc = -ENODEV;
|
|
|
int index;
|
|
|
+ u8 value, last, row, stat8;
|
|
|
+
|
|
|
+ last = 0;
|
|
|
+ row = 0;
|
|
|
+
|
|
|
+ for (index = 0; index < 8; index += 2) {
|
|
|
+ pci_read_config_byte(pdev, E752X_DRB + index, &value);
|
|
|
+ /* test if there is a dimm in this slot */
|
|
|
+ if (value == last) {
|
|
|
+ /* no dimm in the slot, so flag it as empty */
|
|
|
+ pvt->map[index] = 0xff;
|
|
|
+ pvt->map[index + 1] = 0xff;
|
|
|
+ } else { /* there is a dimm in the slot */
|
|
|
+ pvt->map[index] = row;
|
|
|
+ row++;
|
|
|
+ last = value;
|
|
|
+ /* test the next value to see if the dimm is double
|
|
|
+ * sided
|
|
|
+ */
|
|
|
+ pci_read_config_byte(pdev, E752X_DRB + index + 1,
|
|
|
+ &value);
|
|
|
+ pvt->map[index + 1] = (value == last) ?
|
|
|
+ 0xff : /* the dimm is single sided,
|
|
|
+ so flag as empty */
|
|
|
+ row; /* this is a double sided dimm
|
|
|
+ to save the next row # */
|
|
|
+ row++;
|
|
|
+ last = value;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* set the map type. 1 = normal, 0 = reversed */
|
|
|
+ pci_read_config_byte(pdev, E752X_DRM, &stat8);
|
|
|
+ pvt->map_type = ((stat8 & 0x0f) > ((stat8 >> 4) & 0x0f));
|
|
|
+}
|
|
|
+
|
|
|
+/* Return 0 on success or 1 on failure. */
|
|
|
+static int e752x_get_devs(struct pci_dev *pdev, int dev_idx,
|
|
|
+ struct e752x_pvt *pvt)
|
|
|
+{
|
|
|
+ struct pci_dev *dev;
|
|
|
+
|
|
|
+ pvt->bridge_ck = pci_get_device(PCI_VENDOR_ID_INTEL,
|
|
|
+ pvt->dev_info->err_dev,
|
|
|
+ pvt->bridge_ck);
|
|
|
+
|
|
|
+ if (pvt->bridge_ck == NULL)
|
|
|
+ pvt->bridge_ck = pci_scan_single_device(pdev->bus,
|
|
|
+ PCI_DEVFN(0, 1));
|
|
|
+
|
|
|
+ if (pvt->bridge_ck == NULL) {
|
|
|
+ e752x_printk(KERN_ERR, "error reporting device not found:"
|
|
|
+ "vendor %x device 0x%x (broken BIOS?)\n",
|
|
|
+ PCI_VENDOR_ID_INTEL, e752x_devs[dev_idx].err_dev);
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ dev = pci_get_device(PCI_VENDOR_ID_INTEL, e752x_devs[dev_idx].ctl_dev,
|
|
|
+ NULL);
|
|
|
+
|
|
|
+ if (dev == NULL)
|
|
|
+ goto fail;
|
|
|
+
|
|
|
+ pvt->dev_d0f0 = dev;
|
|
|
+ pvt->dev_d0f1 = pci_dev_get(pvt->bridge_ck);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+fail:
|
|
|
+ pci_dev_put(pvt->bridge_ck);
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+static void e752x_init_error_reporting_regs(struct e752x_pvt *pvt)
|
|
|
+{
|
|
|
+ struct pci_dev *dev;
|
|
|
+
|
|
|
+ dev = pvt->dev_d0f1;
|
|
|
+ /* Turn off error disable & SMI in case the BIOS turned it on */
|
|
|
+ pci_write_config_byte(dev, E752X_HI_ERRMASK, 0x00);
|
|
|
+ pci_write_config_byte(dev, E752X_HI_SMICMD, 0x00);
|
|
|
+ pci_write_config_word(dev, E752X_SYSBUS_ERRMASK, 0x00);
|
|
|
+ pci_write_config_word(dev, E752X_SYSBUS_SMICMD, 0x00);
|
|
|
+ pci_write_config_byte(dev, E752X_BUF_ERRMASK, 0x00);
|
|
|
+ pci_write_config_byte(dev, E752X_BUF_SMICMD, 0x00);
|
|
|
+ pci_write_config_byte(dev, E752X_DRAM_ERRMASK, 0x00);
|
|
|
+ pci_write_config_byte(dev, E752X_DRAM_SMICMD, 0x00);
|
|
|
+}
|
|
|
+
|
|
|
+static int e752x_probe1(struct pci_dev *pdev, int dev_idx)
|
|
|
+{
|
|
|
u16 pci_data;
|
|
|
u8 stat8;
|
|
|
- struct mem_ctl_info *mci = NULL;
|
|
|
- struct e752x_pvt *pvt = NULL;
|
|
|
+ struct mem_ctl_info *mci;
|
|
|
+ struct e752x_pvt *pvt;
|
|
|
u16 ddrcsr;
|
|
|
- u32 drc;
|
|
|
int drc_chan; /* Number of channels 0=1chan,1=2chan */
|
|
|
- int drc_drbg; /* DRB granularity 0=64mb, 1=128mb */
|
|
|
- int drc_ddim; /* DRAM Data Integrity Mode 0=none,2=edac */
|
|
|
- u32 dra;
|
|
|
- unsigned long last_cumul_size;
|
|
|
- struct pci_dev *dev = NULL;
|
|
|
struct e752x_error_info discard;
|
|
|
|
|
|
debugf0("%s(): mci\n", __func__);
|
|
@@ -794,25 +946,20 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx)
|
|
|
if (!force_function_unhide && !(stat8 & (1 << 5))) {
|
|
|
printk(KERN_INFO "Contact your BIOS vendor to see if the "
|
|
|
"E752x error registers can be safely un-hidden\n");
|
|
|
- goto fail;
|
|
|
+ return -ENOMEM;
|
|
|
}
|
|
|
stat8 |= (1 << 5);
|
|
|
pci_write_config_byte(pdev, E752X_DEVPRES1, stat8);
|
|
|
|
|
|
- /* need to find out the number of channels */
|
|
|
- pci_read_config_dword(pdev, E752X_DRC, &drc);
|
|
|
pci_read_config_word(pdev, E752X_DDRCSR, &ddrcsr);
|
|
|
/* FIXME: should check >>12 or 0xf, true for all? */
|
|
|
/* Dual channel = 1, Single channel = 0 */
|
|
|
- drc_chan = (((ddrcsr >> 12) & 3) == 3);
|
|
|
- drc_drbg = drc_chan + 1; /* 128 in dual mode, 64 in single */
|
|
|
- drc_ddim = (drc >> 20) & 0x3;
|
|
|
+ drc_chan = dual_channel_active(ddrcsr);
|
|
|
|
|
|
mci = edac_mc_alloc(sizeof(*pvt), E752X_NR_CSROWS, drc_chan + 1);
|
|
|
|
|
|
if (mci == NULL) {
|
|
|
- rc = -ENOMEM;
|
|
|
- goto fail;
|
|
|
+ return -ENOMEM;
|
|
|
}
|
|
|
|
|
|
debugf3("%s(): init mci\n", __func__);
|
|
@@ -827,113 +974,20 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx)
|
|
|
debugf3("%s(): init pvt\n", __func__);
|
|
|
pvt = (struct e752x_pvt *) mci->pvt_info;
|
|
|
pvt->dev_info = &e752x_devs[dev_idx];
|
|
|
- pvt->bridge_ck = pci_get_device(PCI_VENDOR_ID_INTEL,
|
|
|
- pvt->dev_info->err_dev,
|
|
|
- pvt->bridge_ck);
|
|
|
-
|
|
|
- if (pvt->bridge_ck == NULL)
|
|
|
- pvt->bridge_ck = pci_scan_single_device(pdev->bus,
|
|
|
- PCI_DEVFN(0, 1));
|
|
|
+ pvt->mc_symmetric = ((ddrcsr & 0x10) != 0);
|
|
|
|
|
|
- if (pvt->bridge_ck == NULL) {
|
|
|
- e752x_printk(KERN_ERR, "error reporting device not found:"
|
|
|
- "vendor %x device 0x%x (broken BIOS?)\n",
|
|
|
- PCI_VENDOR_ID_INTEL, e752x_devs[dev_idx].err_dev);
|
|
|
- goto fail;
|
|
|
+ if (e752x_get_devs(pdev, dev_idx, pvt)) {
|
|
|
+ edac_mc_free(mci);
|
|
|
+ return -ENODEV;
|
|
|
}
|
|
|
|
|
|
- pvt->mc_symmetric = ((ddrcsr & 0x10) != 0);
|
|
|
debugf3("%s(): more mci init\n", __func__);
|
|
|
mci->ctl_name = pvt->dev_info->ctl_name;
|
|
|
mci->edac_check = e752x_check;
|
|
|
mci->ctl_page_to_phys = ctl_page_to_phys;
|
|
|
|
|
|
- /* find out the device types */
|
|
|
- pci_read_config_dword(pdev, E752X_DRA, &dra);
|
|
|
-
|
|
|
- /*
|
|
|
- * The dram row boundary (DRB) reg values are boundary address for
|
|
|
- * each DRAM row with a granularity of 64 or 128MB (single/dual
|
|
|
- * channel operation). DRB regs are cumulative; therefore DRB7 will
|
|
|
- * contain the total memory contained in all eight rows.
|
|
|
- */
|
|
|
- for (last_cumul_size = index = 0; index < mci->nr_csrows; index++) {
|
|
|
- u8 value;
|
|
|
- u32 cumul_size;
|
|
|
-
|
|
|
- /* mem_dev 0=x8, 1=x4 */
|
|
|
- int mem_dev = (dra >> (index * 4 + 2)) & 0x3;
|
|
|
- struct csrow_info *csrow = &mci->csrows[index];
|
|
|
-
|
|
|
- mem_dev = (mem_dev == 2);
|
|
|
- pci_read_config_byte(pdev, E752X_DRB + index, &value);
|
|
|
- /* convert a 128 or 64 MiB DRB to a page size. */
|
|
|
- cumul_size = value << (25 + drc_drbg - PAGE_SHIFT);
|
|
|
- debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index,
|
|
|
- cumul_size);
|
|
|
-
|
|
|
- if (cumul_size == last_cumul_size)
|
|
|
- continue; /* not populated */
|
|
|
-
|
|
|
- csrow->first_page = last_cumul_size;
|
|
|
- csrow->last_page = cumul_size - 1;
|
|
|
- csrow->nr_pages = cumul_size - last_cumul_size;
|
|
|
- last_cumul_size = cumul_size;
|
|
|
- csrow->grain = 1 << 12; /* 4KiB - resolution of CELOG */
|
|
|
- csrow->mtype = MEM_RDDR; /* only one type supported */
|
|
|
- csrow->dtype = mem_dev ? DEV_X4 : DEV_X8;
|
|
|
-
|
|
|
- /*
|
|
|
- * if single channel or x8 devices then SECDED
|
|
|
- * if dual channel and x4 then S4ECD4ED
|
|
|
- */
|
|
|
- if (drc_ddim) {
|
|
|
- if (drc_chan && mem_dev) {
|
|
|
- csrow->edac_mode = EDAC_S4ECD4ED;
|
|
|
- mci->edac_cap |= EDAC_FLAG_S4ECD4ED;
|
|
|
- } else {
|
|
|
- csrow->edac_mode = EDAC_SECDED;
|
|
|
- mci->edac_cap |= EDAC_FLAG_SECDED;
|
|
|
- }
|
|
|
- } else
|
|
|
- csrow->edac_mode = EDAC_NONE;
|
|
|
- }
|
|
|
-
|
|
|
- /* Fill in the memory map table */
|
|
|
- {
|
|
|
- u8 value;
|
|
|
- u8 last = 0;
|
|
|
- u8 row = 0;
|
|
|
-
|
|
|
- for (index = 0; index < 8; index += 2) {
|
|
|
- pci_read_config_byte(pdev, E752X_DRB + index, &value);
|
|
|
-
|
|
|
- /* test if there is a dimm in this slot */
|
|
|
- if (value == last) {
|
|
|
- /* no dimm in the slot, so flag it as empty */
|
|
|
- pvt->map[index] = 0xff;
|
|
|
- pvt->map[index + 1] = 0xff;
|
|
|
- } else { /* there is a dimm in the slot */
|
|
|
- pvt->map[index] = row;
|
|
|
- row++;
|
|
|
- last = value;
|
|
|
- /* test the next value to see if the dimm is
|
|
|
- double sided */
|
|
|
- pci_read_config_byte(pdev,
|
|
|
- E752X_DRB + index + 1,
|
|
|
- &value);
|
|
|
- pvt->map[index + 1] = (value == last) ?
|
|
|
- 0xff : /* the dimm is single sided,
|
|
|
- * so flag as empty
|
|
|
- */
|
|
|
- row; /* this is a double sided dimm
|
|
|
- * to save the next row #
|
|
|
- */
|
|
|
- row++;
|
|
|
- last = value;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ e752x_init_csrows(mci, pdev, ddrcsr);
|
|
|
+ e752x_init_mem_map_table(pdev, pvt);
|
|
|
|
|
|
/* set the map type. 1 = normal, 0 = reversed */
|
|
|
pci_read_config_byte(pdev, E752X_DRM, &stat8);
|
|
@@ -961,21 +1015,7 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx)
|
|
|
goto fail;
|
|
|
}
|
|
|
|
|
|
- dev = pci_get_device(PCI_VENDOR_ID_INTEL, e752x_devs[dev_idx].ctl_dev,
|
|
|
- NULL);
|
|
|
- pvt->dev_d0f0 = dev;
|
|
|
- /* find the error reporting device and clear errors */
|
|
|
- dev = pvt->dev_d0f1 = pci_dev_get(pvt->bridge_ck);
|
|
|
- /* Turn off error disable & SMI in case the BIOS turned it on */
|
|
|
- pci_write_config_byte(dev, E752X_HI_ERRMASK, 0x00);
|
|
|
- pci_write_config_byte(dev, E752X_HI_SMICMD, 0x00);
|
|
|
- pci_write_config_word(dev, E752X_SYSBUS_ERRMASK, 0x00);
|
|
|
- pci_write_config_word(dev, E752X_SYSBUS_SMICMD, 0x00);
|
|
|
- pci_write_config_byte(dev, E752X_BUF_ERRMASK, 0x00);
|
|
|
- pci_write_config_byte(dev, E752X_BUF_SMICMD, 0x00);
|
|
|
- pci_write_config_byte(dev, E752X_DRAM_ERRMASK, 0x00);
|
|
|
- pci_write_config_byte(dev, E752X_DRAM_SMICMD, 0x00);
|
|
|
-
|
|
|
+ e752x_init_error_reporting_regs(pvt);
|
|
|
e752x_get_error_info(mci, &discard); /* clear other MCH errors */
|
|
|
|
|
|
/* get this far and it's successful */
|
|
@@ -983,20 +1023,12 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx)
|
|
|
return 0;
|
|
|
|
|
|
fail:
|
|
|
- if (mci) {
|
|
|
- if (pvt->dev_d0f0)
|
|
|
- pci_dev_put(pvt->dev_d0f0);
|
|
|
-
|
|
|
- if (pvt->dev_d0f1)
|
|
|
- pci_dev_put(pvt->dev_d0f1);
|
|
|
-
|
|
|
- if (pvt->bridge_ck)
|
|
|
- pci_dev_put(pvt->bridge_ck);
|
|
|
-
|
|
|
- edac_mc_free(mci);
|
|
|
- }
|
|
|
+ pci_dev_put(pvt->dev_d0f0);
|
|
|
+ pci_dev_put(pvt->dev_d0f1);
|
|
|
+ pci_dev_put(pvt->bridge_ck);
|
|
|
+ edac_mc_free(mci);
|
|
|
|
|
|
- return rc;
|
|
|
+ return -ENODEV;
|
|
|
}
|
|
|
|
|
|
/* returns count (>= 0), or negative on error */
|