aboutsummaryrefslogtreecommitdiff
path: root/drivers/edac
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/edac')
-rw-r--r--drivers/edac/e752x_edac.c40
-rw-r--r--drivers/edac/edac_mc.c175
-rw-r--r--drivers/edac/edac_mc.h28
3 files changed, 222 insertions, 21 deletions
diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c
index c82bc0ed7f1..8bcc887692a 100644
--- a/drivers/edac/e752x_edac.c
+++ b/drivers/edac/e752x_edac.c
@@ -285,8 +285,9 @@ static void do_process_ce(struct mem_ctl_info *mci, u16 error_one,
if (!pvt->map_type)
row = 7 - row;
- edac_mc_handle_ce(mci, page, 0, sec1_syndrome, row, channel,
- "e752x CE");
+ /* e752x mc reads 34:6 of the DRAM linear address */
+ edac_mc_handle_ce(mci, page, offset_in_page(sec1_add << 4),
+ sec1_syndrome, row, channel, "e752x CE");
}
static inline void process_ce(struct mem_ctl_info *mci, u16 error_one,
@@ -319,8 +320,10 @@ static void do_process_ue(struct mem_ctl_info *mci, u16 error_one,
((block_page >> 1) & 3) :
edac_mc_find_csrow_by_page(mci, block_page);
- edac_mc_handle_ue(mci, block_page, 0, row,
- "e752x UE from Read");
+ /* e752x mc reads 34:6 of the DRAM linear address */
+ edac_mc_handle_ue(mci, block_page,
+ offset_in_page(error_2b << 4),
+ row, "e752x UE from Read");
}
if (error_one & 0x0404) {
error_2b = scrb_add;
@@ -333,8 +336,10 @@ static void do_process_ue(struct mem_ctl_info *mci, u16 error_one,
((block_page >> 1) & 3) :
edac_mc_find_csrow_by_page(mci, block_page);
- edac_mc_handle_ue(mci, block_page, 0, row,
- "e752x UE from Scruber");
+ /* e752x mc reads 34:6 of the DRAM linear address */
+ edac_mc_handle_ue(mci, block_page,
+ offset_in_page(error_2b << 4),
+ row, "e752x UE from Scruber");
}
}
@@ -556,17 +561,17 @@ static void e752x_check_sysbus(struct e752x_error_info *info,
error32 = (stat32 >> 16) & 0x3ff;
stat32 = stat32 & 0x3ff;
- if(stat32 & 0x083)
- sysbus_error(1, stat32 & 0x083, error_found, handle_error);
+ if(stat32 & 0x087)
+ sysbus_error(1, stat32 & 0x087, error_found, handle_error);
- if(stat32 & 0x37c)
- sysbus_error(0, stat32 & 0x37c, error_found, handle_error);
+ if(stat32 & 0x378)
+ sysbus_error(0, stat32 & 0x378, error_found, handle_error);
- if(error32 & 0x083)
- sysbus_error(1, error32 & 0x083, error_found, handle_error);
+ if(error32 & 0x087)
+ sysbus_error(1, error32 & 0x087, error_found, handle_error);
- if(error32 & 0x37c)
- sysbus_error(0, error32 & 0x37c, error_found, handle_error);
+ if(error32 & 0x378)
+ sysbus_error(0, error32 & 0x378, error_found, handle_error);
}
static void e752x_check_membuf (struct e752x_error_info *info,
@@ -782,7 +787,12 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
u8 value;
u32 dra, drc, cumul_size;
- pci_read_config_dword(pdev, E752X_DRA, &dra);
+ dra = 0;
+ for (index=0; index < 4; index++) {
+ u8 dra_reg;
+ pci_read_config_byte(pdev, E752X_DRA+index, &dra_reg);
+ dra |= dra_reg << (index * 8);
+ }
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 */
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c
index 1b4fc922180..7b622300d0e 100644
--- a/drivers/edac/edac_mc.c
+++ b/drivers/edac/edac_mc.c
@@ -927,6 +927,57 @@ static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci,
return count;
}
+/* memory scrubbing */
+static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci,
+ const char *data, size_t count)
+{
+ u32 bandwidth = -1;
+
+ if (mci->set_sdram_scrub_rate) {
+
+ memctrl_int_store(&bandwidth, data, count);
+
+ if (!(*mci->set_sdram_scrub_rate)(mci, &bandwidth)) {
+ edac_printk(KERN_DEBUG, EDAC_MC,
+ "Scrub rate set successfully, applied: %d\n",
+ bandwidth);
+ } else {
+ /* FIXME: error codes maybe? */
+ edac_printk(KERN_DEBUG, EDAC_MC,
+ "Scrub rate set FAILED, could not apply: %d\n",
+ bandwidth);
+ }
+ } else {
+ /* FIXME: produce "not implemented" ERROR for user-side. */
+ edac_printk(KERN_WARNING, EDAC_MC,
+ "Memory scrubbing 'set'control is not implemented!\n");
+ }
+ return count;
+}
+
+static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data)
+{
+ u32 bandwidth = -1;
+
+ if (mci->get_sdram_scrub_rate) {
+ if (!(*mci->get_sdram_scrub_rate)(mci, &bandwidth)) {
+ edac_printk(KERN_DEBUG, EDAC_MC,
+ "Scrub rate successfully, fetched: %d\n",
+ bandwidth);
+ } else {
+ /* FIXME: error codes maybe? */
+ edac_printk(KERN_DEBUG, EDAC_MC,
+ "Scrub rate fetch FAILED, got: %d\n",
+ bandwidth);
+ }
+ } else {
+ /* FIXME: produce "not implemented" ERROR for user-side. */
+ edac_printk(KERN_WARNING, EDAC_MC,
+ "Memory scrubbing 'get' control is not implemented!\n");
+ }
+ return sprintf(data, "%d\n", bandwidth);
+}
+
/* default attribute files for the MCI object */
static ssize_t mci_ue_count_show(struct mem_ctl_info *mci, char *data)
{
@@ -1033,6 +1084,9 @@ MCIDEV_ATTR(ce_noinfo_count,S_IRUGO,mci_ce_noinfo_show,NULL);
MCIDEV_ATTR(ue_count,S_IRUGO,mci_ue_count_show,NULL);
MCIDEV_ATTR(ce_count,S_IRUGO,mci_ce_count_show,NULL);
+/* memory scrubber attribute file */
+MCIDEV_ATTR(sdram_scrub_rate,S_IRUGO|S_IWUSR,mci_sdram_scrub_rate_show,mci_sdram_scrub_rate_store);
+
static struct mcidev_attribute *mci_attr[] = {
&mci_attr_reset_counters,
&mci_attr_mc_name,
@@ -1042,6 +1096,7 @@ static struct mcidev_attribute *mci_attr[] = {
&mci_attr_ce_noinfo_count,
&mci_attr_ue_count,
&mci_attr_ce_count,
+ &mci_attr_sdram_scrub_rate,
NULL
};
@@ -1442,11 +1497,11 @@ int edac_mc_add_mc(struct mem_ctl_info *mci, int mc_idx)
/* set load time so that error rate can be tracked */
mci->start_time = jiffies;
- if (edac_create_sysfs_mci_device(mci)) {
- edac_mc_printk(mci, KERN_WARNING,
+ if (edac_create_sysfs_mci_device(mci)) {
+ edac_mc_printk(mci, KERN_WARNING,
"failed to create sysfs device\n");
- goto fail1;
- }
+ goto fail1;
+ }
/* Report action taken */
edac_mc_printk(mci, KERN_INFO, "Giving out device to %s %s: DEV %s\n",
@@ -1703,6 +1758,116 @@ void edac_mc_handle_ue_no_info(struct mem_ctl_info *mci, const char *msg)
EXPORT_SYMBOL_GPL(edac_mc_handle_ue_no_info);
+/*************************************************************
+ * On Fully Buffered DIMM modules, this help function is
+ * called to process UE events
+ */
+void edac_mc_handle_fbd_ue(struct mem_ctl_info *mci,
+ unsigned int csrow,
+ unsigned int channela,
+ unsigned int channelb,
+ char *msg)
+{
+ int len = EDAC_MC_LABEL_LEN * 4;
+ char labels[len + 1];
+ char *pos = labels;
+ int chars;
+
+ if (csrow >= mci->nr_csrows) {
+ /* something is wrong */
+ edac_mc_printk(mci, KERN_ERR,
+ "INTERNAL ERROR: row out of range (%d >= %d)\n",
+ csrow, mci->nr_csrows);
+ edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
+ return;
+ }
+
+ if (channela >= mci->csrows[csrow].nr_channels) {
+ /* something is wrong */
+ edac_mc_printk(mci, KERN_ERR,
+ "INTERNAL ERROR: channel-a out of range "
+ "(%d >= %d)\n",
+ channela, mci->csrows[csrow].nr_channels);
+ edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
+ return;
+ }
+
+ if (channelb >= mci->csrows[csrow].nr_channels) {
+ /* something is wrong */
+ edac_mc_printk(mci, KERN_ERR,
+ "INTERNAL ERROR: channel-b out of range "
+ "(%d >= %d)\n",
+ channelb, mci->csrows[csrow].nr_channels);
+ edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
+ return;
+ }
+
+ mci->ue_count++;
+ mci->csrows[csrow].ue_count++;
+
+ /* Generate the DIMM labels from the specified channels */
+ chars = snprintf(pos, len + 1, "%s",
+ mci->csrows[csrow].channels[channela].label);
+ len -= chars; pos += chars;
+ chars = snprintf(pos, len + 1, "-%s",
+ mci->csrows[csrow].channels[channelb].label);
+
+ if (log_ue)
+ edac_mc_printk(mci, KERN_EMERG,
+ "UE row %d, channel-a= %d channel-b= %d "
+ "labels \"%s\": %s\n", csrow, channela, channelb,
+ labels, msg);
+
+ if (panic_on_ue)
+ panic("UE row %d, channel-a= %d channel-b= %d "
+ "labels \"%s\": %s\n", csrow, channela,
+ channelb, labels, msg);
+}
+EXPORT_SYMBOL(edac_mc_handle_fbd_ue);
+
+/*************************************************************
+ * On Fully Buffered DIMM modules, this help function is
+ * called to process CE events
+ */
+void edac_mc_handle_fbd_ce(struct mem_ctl_info *mci,
+ unsigned int csrow,
+ unsigned int channel,
+ char *msg)
+{
+
+ /* Ensure boundary values */
+ if (csrow >= mci->nr_csrows) {
+ /* something is wrong */
+ edac_mc_printk(mci, KERN_ERR,
+ "INTERNAL ERROR: row out of range (%d >= %d)\n",
+ csrow, mci->nr_csrows);
+ edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
+ return;
+ }
+ if (channel >= mci->csrows[csrow].nr_channels) {
+ /* something is wrong */
+ edac_mc_printk(mci, KERN_ERR,
+ "INTERNAL ERROR: channel out of range (%d >= %d)\n",
+ channel, mci->csrows[csrow].nr_channels);
+ edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
+ return;
+ }
+
+ if (log_ce)
+ /* FIXME - put in DIMM location */
+ edac_mc_printk(mci, KERN_WARNING,
+ "CE row %d, channel %d, label \"%s\": %s\n",
+ csrow, channel,
+ mci->csrows[csrow].channels[channel].label,
+ msg);
+
+ mci->ce_count++;
+ mci->csrows[csrow].ce_count++;
+ mci->csrows[csrow].channels[channel].ce_count++;
+}
+EXPORT_SYMBOL(edac_mc_handle_fbd_ce);
+
+
/*
* Iterate over all MC instances and check for ECC, et al, errors
*/
@@ -1806,7 +1971,7 @@ static void __exit edac_mc_exit(void)
debugf0("%s()\n", __func__);
kthread_stop(edac_thread);
- /* tear down the sysfs device */
+ /* tear down the sysfs device */
edac_sysfs_memctrl_teardown();
edac_sysfs_pci_teardown();
}
diff --git a/drivers/edac/edac_mc.h b/drivers/edac/edac_mc.h
index a1cfd4e3c97..713444cc410 100644
--- a/drivers/edac/edac_mc.h
+++ b/drivers/edac/edac_mc.h
@@ -123,7 +123,9 @@ enum mem_type {
MEM_RDR, /* Registered single data rate SDRAM */
MEM_DDR, /* Double data rate SDRAM */
MEM_RDDR, /* Registered Double data rate SDRAM */
- MEM_RMBS /* Rambus DRAM */
+ MEM_RMBS, /* Rambus DRAM */
+ MEM_DDR2, /* DDR2 RAM */
+ MEM_FB_DDR2, /* fully buffered DDR2 */
};
#define MEM_FLAG_EMPTY BIT(MEM_EMPTY)
@@ -137,6 +139,8 @@ enum mem_type {
#define MEM_FLAG_DDR BIT(MEM_DDR)
#define MEM_FLAG_RDDR BIT(MEM_RDDR)
#define MEM_FLAG_RMBS BIT(MEM_RMBS)
+#define MEM_FLAG_DDR2 BIT(MEM_DDR2)
+#define MEM_FLAG_FB_DDR2 BIT(MEM_FB_DDR2)
/* chipset Error Detection and Correction capabilities and mode */
enum edac_type {
@@ -315,8 +319,21 @@ struct mem_ctl_info {
unsigned long scrub_cap; /* chipset scrub capabilities */
enum scrub_type scrub_mode; /* current scrub mode */
+ /* Translates sdram memory scrub rate given in bytes/sec to the
+ internal representation and configures whatever else needs
+ to be configured.
+ */
+ int (*set_sdram_scrub_rate) (struct mem_ctl_info *mci, u32 *bw);
+
+ /* Get the current sdram memory scrub rate from the internal
+ representation and converts it to the closest matching
+ bandwith in bytes/sec.
+ */
+ int (*get_sdram_scrub_rate) (struct mem_ctl_info *mci, u32 *bw);
+
/* pointer to edac checking routine */
void (*edac_check) (struct mem_ctl_info * mci);
+
/*
* Remaps memory pages: controller pages to physical pages.
* For most MC's, this will be NULL.
@@ -441,6 +458,15 @@ extern void edac_mc_handle_ue(struct mem_ctl_info *mci,
int row, const char *msg);
extern void edac_mc_handle_ue_no_info(struct mem_ctl_info *mci,
const char *msg);
+extern void edac_mc_handle_fbd_ue(struct mem_ctl_info *mci,
+ unsigned int csrow,
+ unsigned int channel0,
+ unsigned int channel1,
+ char *msg);
+extern void edac_mc_handle_fbd_ce(struct mem_ctl_info *mci,
+ unsigned int csrow,
+ unsigned int channel,
+ char *msg);
/*
* This kmalloc's and initializes all the structures.