diff options
Diffstat (limited to 'drivers/edac')
-rw-r--r-- | drivers/edac/e752x_edac.c | 40 | ||||
-rw-r--r-- | drivers/edac/edac_mc.c | 175 | ||||
-rw-r--r-- | drivers/edac/edac_mc.h | 28 |
3 files changed, 222 insertions, 21 deletions
diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c index c82bc0ed7f14..8bcc887692ab 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, | |||
285 | if (!pvt->map_type) | 285 | if (!pvt->map_type) |
286 | row = 7 - row; | 286 | row = 7 - row; |
287 | 287 | ||
288 | edac_mc_handle_ce(mci, page, 0, sec1_syndrome, row, channel, | 288 | /* e752x mc reads 34:6 of the DRAM linear address */ |
289 | "e752x CE"); | 289 | edac_mc_handle_ce(mci, page, offset_in_page(sec1_add << 4), |
290 | sec1_syndrome, row, channel, "e752x CE"); | ||
290 | } | 291 | } |
291 | 292 | ||
292 | static inline void process_ce(struct mem_ctl_info *mci, u16 error_one, | 293 | 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, | |||
319 | ((block_page >> 1) & 3) : | 320 | ((block_page >> 1) & 3) : |
320 | edac_mc_find_csrow_by_page(mci, block_page); | 321 | edac_mc_find_csrow_by_page(mci, block_page); |
321 | 322 | ||
322 | edac_mc_handle_ue(mci, block_page, 0, row, | 323 | /* e752x mc reads 34:6 of the DRAM linear address */ |
323 | "e752x UE from Read"); | 324 | edac_mc_handle_ue(mci, block_page, |
325 | offset_in_page(error_2b << 4), | ||
326 | row, "e752x UE from Read"); | ||
324 | } | 327 | } |
325 | if (error_one & 0x0404) { | 328 | if (error_one & 0x0404) { |
326 | error_2b = scrb_add; | 329 | error_2b = scrb_add; |
@@ -333,8 +336,10 @@ static void do_process_ue(struct mem_ctl_info *mci, u16 error_one, | |||
333 | ((block_page >> 1) & 3) : | 336 | ((block_page >> 1) & 3) : |
334 | edac_mc_find_csrow_by_page(mci, block_page); | 337 | edac_mc_find_csrow_by_page(mci, block_page); |
335 | 338 | ||
336 | edac_mc_handle_ue(mci, block_page, 0, row, | 339 | /* e752x mc reads 34:6 of the DRAM linear address */ |
337 | "e752x UE from Scruber"); | 340 | edac_mc_handle_ue(mci, block_page, |
341 | offset_in_page(error_2b << 4), | ||
342 | row, "e752x UE from Scruber"); | ||
338 | } | 343 | } |
339 | } | 344 | } |
340 | 345 | ||
@@ -556,17 +561,17 @@ static void e752x_check_sysbus(struct e752x_error_info *info, | |||
556 | error32 = (stat32 >> 16) & 0x3ff; | 561 | error32 = (stat32 >> 16) & 0x3ff; |
557 | stat32 = stat32 & 0x3ff; | 562 | stat32 = stat32 & 0x3ff; |
558 | 563 | ||
559 | if(stat32 & 0x083) | 564 | if(stat32 & 0x087) |
560 | sysbus_error(1, stat32 & 0x083, error_found, handle_error); | 565 | sysbus_error(1, stat32 & 0x087, error_found, handle_error); |
561 | 566 | ||
562 | if(stat32 & 0x37c) | 567 | if(stat32 & 0x378) |
563 | sysbus_error(0, stat32 & 0x37c, error_found, handle_error); | 568 | sysbus_error(0, stat32 & 0x378, error_found, handle_error); |
564 | 569 | ||
565 | if(error32 & 0x083) | 570 | if(error32 & 0x087) |
566 | sysbus_error(1, error32 & 0x083, error_found, handle_error); | 571 | sysbus_error(1, error32 & 0x087, error_found, handle_error); |
567 | 572 | ||
568 | if(error32 & 0x37c) | 573 | if(error32 & 0x378) |
569 | sysbus_error(0, error32 & 0x37c, error_found, handle_error); | 574 | sysbus_error(0, error32 & 0x378, error_found, handle_error); |
570 | } | 575 | } |
571 | 576 | ||
572 | static void e752x_check_membuf (struct e752x_error_info *info, | 577 | 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, | |||
782 | u8 value; | 787 | u8 value; |
783 | u32 dra, drc, cumul_size; | 788 | u32 dra, drc, cumul_size; |
784 | 789 | ||
785 | pci_read_config_dword(pdev, E752X_DRA, &dra); | 790 | dra = 0; |
791 | for (index=0; index < 4; index++) { | ||
792 | u8 dra_reg; | ||
793 | pci_read_config_byte(pdev, E752X_DRA+index, &dra_reg); | ||
794 | dra |= dra_reg << (index * 8); | ||
795 | } | ||
786 | pci_read_config_dword(pdev, E752X_DRC, &drc); | 796 | pci_read_config_dword(pdev, E752X_DRC, &drc); |
787 | drc_chan = dual_channel_active(ddrcsr); | 797 | drc_chan = dual_channel_active(ddrcsr); |
788 | drc_drbg = drc_chan + 1; /* 128 in dual mode, 64 in single */ | 798 | 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 1b4fc9221803..7b622300d0e5 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, | |||
927 | return count; | 927 | return count; |
928 | } | 928 | } |
929 | 929 | ||
930 | /* memory scrubbing */ | ||
931 | static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci, | ||
932 | const char *data, size_t count) | ||
933 | { | ||
934 | u32 bandwidth = -1; | ||
935 | |||
936 | if (mci->set_sdram_scrub_rate) { | ||
937 | |||
938 | memctrl_int_store(&bandwidth, data, count); | ||
939 | |||
940 | if (!(*mci->set_sdram_scrub_rate)(mci, &bandwidth)) { | ||
941 | edac_printk(KERN_DEBUG, EDAC_MC, | ||
942 | "Scrub rate set successfully, applied: %d\n", | ||
943 | bandwidth); | ||
944 | } else { | ||
945 | /* FIXME: error codes maybe? */ | ||
946 | edac_printk(KERN_DEBUG, EDAC_MC, | ||
947 | "Scrub rate set FAILED, could not apply: %d\n", | ||
948 | bandwidth); | ||
949 | } | ||
950 | } else { | ||
951 | /* FIXME: produce "not implemented" ERROR for user-side. */ | ||
952 | edac_printk(KERN_WARNING, EDAC_MC, | ||
953 | "Memory scrubbing 'set'control is not implemented!\n"); | ||
954 | } | ||
955 | return count; | ||
956 | } | ||
957 | |||
958 | static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data) | ||
959 | { | ||
960 | u32 bandwidth = -1; | ||
961 | |||
962 | if (mci->get_sdram_scrub_rate) { | ||
963 | if (!(*mci->get_sdram_scrub_rate)(mci, &bandwidth)) { | ||
964 | edac_printk(KERN_DEBUG, EDAC_MC, | ||
965 | "Scrub rate successfully, fetched: %d\n", | ||
966 | bandwidth); | ||
967 | } else { | ||
968 | /* FIXME: error codes maybe? */ | ||
969 | edac_printk(KERN_DEBUG, EDAC_MC, | ||
970 | "Scrub rate fetch FAILED, got: %d\n", | ||
971 | bandwidth); | ||
972 | } | ||
973 | } else { | ||
974 | /* FIXME: produce "not implemented" ERROR for user-side. */ | ||
975 | edac_printk(KERN_WARNING, EDAC_MC, | ||
976 | "Memory scrubbing 'get' control is not implemented!\n"); | ||
977 | } | ||
978 | return sprintf(data, "%d\n", bandwidth); | ||
979 | } | ||
980 | |||
930 | /* default attribute files for the MCI object */ | 981 | /* default attribute files for the MCI object */ |
931 | static ssize_t mci_ue_count_show(struct mem_ctl_info *mci, char *data) | 982 | static ssize_t mci_ue_count_show(struct mem_ctl_info *mci, char *data) |
932 | { | 983 | { |
@@ -1033,6 +1084,9 @@ MCIDEV_ATTR(ce_noinfo_count,S_IRUGO,mci_ce_noinfo_show,NULL); | |||
1033 | MCIDEV_ATTR(ue_count,S_IRUGO,mci_ue_count_show,NULL); | 1084 | MCIDEV_ATTR(ue_count,S_IRUGO,mci_ue_count_show,NULL); |
1034 | MCIDEV_ATTR(ce_count,S_IRUGO,mci_ce_count_show,NULL); | 1085 | MCIDEV_ATTR(ce_count,S_IRUGO,mci_ce_count_show,NULL); |
1035 | 1086 | ||
1087 | /* memory scrubber attribute file */ | ||
1088 | MCIDEV_ATTR(sdram_scrub_rate,S_IRUGO|S_IWUSR,mci_sdram_scrub_rate_show,mci_sdram_scrub_rate_store); | ||
1089 | |||
1036 | static struct mcidev_attribute *mci_attr[] = { | 1090 | static struct mcidev_attribute *mci_attr[] = { |
1037 | &mci_attr_reset_counters, | 1091 | &mci_attr_reset_counters, |
1038 | &mci_attr_mc_name, | 1092 | &mci_attr_mc_name, |
@@ -1042,6 +1096,7 @@ static struct mcidev_attribute *mci_attr[] = { | |||
1042 | &mci_attr_ce_noinfo_count, | 1096 | &mci_attr_ce_noinfo_count, |
1043 | &mci_attr_ue_count, | 1097 | &mci_attr_ue_count, |
1044 | &mci_attr_ce_count, | 1098 | &mci_attr_ce_count, |
1099 | &mci_attr_sdram_scrub_rate, | ||
1045 | NULL | 1100 | NULL |
1046 | }; | 1101 | }; |
1047 | 1102 | ||
@@ -1442,11 +1497,11 @@ int edac_mc_add_mc(struct mem_ctl_info *mci, int mc_idx) | |||
1442 | /* set load time so that error rate can be tracked */ | 1497 | /* set load time so that error rate can be tracked */ |
1443 | mci->start_time = jiffies; | 1498 | mci->start_time = jiffies; |
1444 | 1499 | ||
1445 | if (edac_create_sysfs_mci_device(mci)) { | 1500 | if (edac_create_sysfs_mci_device(mci)) { |
1446 | edac_mc_printk(mci, KERN_WARNING, | 1501 | edac_mc_printk(mci, KERN_WARNING, |
1447 | "failed to create sysfs device\n"); | 1502 | "failed to create sysfs device\n"); |
1448 | goto fail1; | 1503 | goto fail1; |
1449 | } | 1504 | } |
1450 | 1505 | ||
1451 | /* Report action taken */ | 1506 | /* Report action taken */ |
1452 | edac_mc_printk(mci, KERN_INFO, "Giving out device to %s %s: DEV %s\n", | 1507 | 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) | |||
1703 | EXPORT_SYMBOL_GPL(edac_mc_handle_ue_no_info); | 1758 | EXPORT_SYMBOL_GPL(edac_mc_handle_ue_no_info); |
1704 | 1759 | ||
1705 | 1760 | ||
1761 | /************************************************************* | ||
1762 | * On Fully Buffered DIMM modules, this help function is | ||
1763 | * called to process UE events | ||
1764 | */ | ||
1765 | void edac_mc_handle_fbd_ue(struct mem_ctl_info *mci, | ||
1766 | unsigned int csrow, | ||
1767 | unsigned int channela, | ||
1768 | unsigned int channelb, | ||
1769 | char *msg) | ||
1770 | { | ||
1771 | int len = EDAC_MC_LABEL_LEN * 4; | ||
1772 | char labels[len + 1]; | ||
1773 | char *pos = labels; | ||
1774 | int chars; | ||
1775 | |||
1776 | if (csrow >= mci->nr_csrows) { | ||
1777 | /* something is wrong */ | ||
1778 | edac_mc_printk(mci, KERN_ERR, | ||
1779 | "INTERNAL ERROR: row out of range (%d >= %d)\n", | ||
1780 | csrow, mci->nr_csrows); | ||
1781 | edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR"); | ||
1782 | return; | ||
1783 | } | ||
1784 | |||
1785 | if (channela >= mci->csrows[csrow].nr_channels) { | ||
1786 | /* something is wrong */ | ||
1787 | edac_mc_printk(mci, KERN_ERR, | ||
1788 | "INTERNAL ERROR: channel-a out of range " | ||
1789 | "(%d >= %d)\n", | ||
1790 | channela, mci->csrows[csrow].nr_channels); | ||
1791 | edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR"); | ||
1792 | return; | ||
1793 | } | ||
1794 | |||
1795 | if (channelb >= mci->csrows[csrow].nr_channels) { | ||
1796 | /* something is wrong */ | ||
1797 | edac_mc_printk(mci, KERN_ERR, | ||
1798 | "INTERNAL ERROR: channel-b out of range " | ||
1799 | "(%d >= %d)\n", | ||
1800 | channelb, mci->csrows[csrow].nr_channels); | ||
1801 | edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR"); | ||
1802 | return; | ||
1803 | } | ||
1804 | |||
1805 | mci->ue_count++; | ||
1806 | mci->csrows[csrow].ue_count++; | ||
1807 | |||
1808 | /* Generate the DIMM labels from the specified channels */ | ||
1809 | chars = snprintf(pos, len + 1, "%s", | ||
1810 | mci->csrows[csrow].channels[channela].label); | ||
1811 | len -= chars; pos += chars; | ||
1812 | chars = snprintf(pos, len + 1, "-%s", | ||
1813 | mci->csrows[csrow].channels[channelb].label); | ||
1814 | |||
1815 | if (log_ue) | ||
1816 | edac_mc_printk(mci, KERN_EMERG, | ||
1817 | "UE row %d, channel-a= %d channel-b= %d " | ||
1818 | "labels \"%s\": %s\n", csrow, channela, channelb, | ||
1819 | labels, msg); | ||
1820 | |||
1821 | if (panic_on_ue) | ||
1822 | panic("UE row %d, channel-a= %d channel-b= %d " | ||
1823 | "labels \"%s\": %s\n", csrow, channela, | ||
1824 | channelb, labels, msg); | ||
1825 | } | ||
1826 | EXPORT_SYMBOL(edac_mc_handle_fbd_ue); | ||
1827 | |||
1828 | /************************************************************* | ||
1829 | * On Fully Buffered DIMM modules, this help function is | ||
1830 | * called to process CE events | ||
1831 | */ | ||
1832 | void edac_mc_handle_fbd_ce(struct mem_ctl_info *mci, | ||
1833 | unsigned int csrow, | ||
1834 | unsigned int channel, | ||
1835 | char *msg) | ||
1836 | { | ||
1837 | |||
1838 | /* Ensure boundary values */ | ||
1839 | if (csrow >= mci->nr_csrows) { | ||
1840 | /* something is wrong */ | ||
1841 | edac_mc_printk(mci, KERN_ERR, | ||
1842 | "INTERNAL ERROR: row out of range (%d >= %d)\n", | ||
1843 | csrow, mci->nr_csrows); | ||
1844 | edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR"); | ||
1845 | return; | ||
1846 | } | ||
1847 | if (channel >= mci->csrows[csrow].nr_channels) { | ||
1848 | /* something is wrong */ | ||
1849 | edac_mc_printk(mci, KERN_ERR, | ||
1850 | "INTERNAL ERROR: channel out of range (%d >= %d)\n", | ||
1851 | channel, mci->csrows[csrow].nr_channels); | ||
1852 | edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR"); | ||
1853 | return; | ||
1854 | } | ||
1855 | |||
1856 | if (log_ce) | ||
1857 | /* FIXME - put in DIMM location */ | ||
1858 | edac_mc_printk(mci, KERN_WARNING, | ||
1859 | "CE row %d, channel %d, label \"%s\": %s\n", | ||
1860 | csrow, channel, | ||
1861 | mci->csrows[csrow].channels[channel].label, | ||
1862 | msg); | ||
1863 | |||
1864 | mci->ce_count++; | ||
1865 | mci->csrows[csrow].ce_count++; | ||
1866 | mci->csrows[csrow].channels[channel].ce_count++; | ||
1867 | } | ||
1868 | EXPORT_SYMBOL(edac_mc_handle_fbd_ce); | ||
1869 | |||
1870 | |||
1706 | /* | 1871 | /* |
1707 | * Iterate over all MC instances and check for ECC, et al, errors | 1872 | * Iterate over all MC instances and check for ECC, et al, errors |
1708 | */ | 1873 | */ |
@@ -1806,7 +1971,7 @@ static void __exit edac_mc_exit(void) | |||
1806 | debugf0("%s()\n", __func__); | 1971 | debugf0("%s()\n", __func__); |
1807 | kthread_stop(edac_thread); | 1972 | kthread_stop(edac_thread); |
1808 | 1973 | ||
1809 | /* tear down the sysfs device */ | 1974 | /* tear down the sysfs device */ |
1810 | edac_sysfs_memctrl_teardown(); | 1975 | edac_sysfs_memctrl_teardown(); |
1811 | edac_sysfs_pci_teardown(); | 1976 | edac_sysfs_pci_teardown(); |
1812 | } | 1977 | } |
diff --git a/drivers/edac/edac_mc.h b/drivers/edac/edac_mc.h index a1cfd4e3c97d..713444cc4105 100644 --- a/drivers/edac/edac_mc.h +++ b/drivers/edac/edac_mc.h | |||
@@ -123,7 +123,9 @@ enum mem_type { | |||
123 | MEM_RDR, /* Registered single data rate SDRAM */ | 123 | MEM_RDR, /* Registered single data rate SDRAM */ |
124 | MEM_DDR, /* Double data rate SDRAM */ | 124 | MEM_DDR, /* Double data rate SDRAM */ |
125 | MEM_RDDR, /* Registered Double data rate SDRAM */ | 125 | MEM_RDDR, /* Registered Double data rate SDRAM */ |
126 | MEM_RMBS /* Rambus DRAM */ | 126 | MEM_RMBS, /* Rambus DRAM */ |
127 | MEM_DDR2, /* DDR2 RAM */ | ||
128 | MEM_FB_DDR2, /* fully buffered DDR2 */ | ||
127 | }; | 129 | }; |
128 | 130 | ||
129 | #define MEM_FLAG_EMPTY BIT(MEM_EMPTY) | 131 | #define MEM_FLAG_EMPTY BIT(MEM_EMPTY) |
@@ -137,6 +139,8 @@ enum mem_type { | |||
137 | #define MEM_FLAG_DDR BIT(MEM_DDR) | 139 | #define MEM_FLAG_DDR BIT(MEM_DDR) |
138 | #define MEM_FLAG_RDDR BIT(MEM_RDDR) | 140 | #define MEM_FLAG_RDDR BIT(MEM_RDDR) |
139 | #define MEM_FLAG_RMBS BIT(MEM_RMBS) | 141 | #define MEM_FLAG_RMBS BIT(MEM_RMBS) |
142 | #define MEM_FLAG_DDR2 BIT(MEM_DDR2) | ||
143 | #define MEM_FLAG_FB_DDR2 BIT(MEM_FB_DDR2) | ||
140 | 144 | ||
141 | /* chipset Error Detection and Correction capabilities and mode */ | 145 | /* chipset Error Detection and Correction capabilities and mode */ |
142 | enum edac_type { | 146 | enum edac_type { |
@@ -315,8 +319,21 @@ struct mem_ctl_info { | |||
315 | unsigned long scrub_cap; /* chipset scrub capabilities */ | 319 | unsigned long scrub_cap; /* chipset scrub capabilities */ |
316 | enum scrub_type scrub_mode; /* current scrub mode */ | 320 | enum scrub_type scrub_mode; /* current scrub mode */ |
317 | 321 | ||
322 | /* Translates sdram memory scrub rate given in bytes/sec to the | ||
323 | internal representation and configures whatever else needs | ||
324 | to be configured. | ||
325 | */ | ||
326 | int (*set_sdram_scrub_rate) (struct mem_ctl_info *mci, u32 *bw); | ||
327 | |||
328 | /* Get the current sdram memory scrub rate from the internal | ||
329 | representation and converts it to the closest matching | ||
330 | bandwith in bytes/sec. | ||
331 | */ | ||
332 | int (*get_sdram_scrub_rate) (struct mem_ctl_info *mci, u32 *bw); | ||
333 | |||
318 | /* pointer to edac checking routine */ | 334 | /* pointer to edac checking routine */ |
319 | void (*edac_check) (struct mem_ctl_info * mci); | 335 | void (*edac_check) (struct mem_ctl_info * mci); |
336 | |||
320 | /* | 337 | /* |
321 | * Remaps memory pages: controller pages to physical pages. | 338 | * Remaps memory pages: controller pages to physical pages. |
322 | * For most MC's, this will be NULL. | 339 | * For most MC's, this will be NULL. |
@@ -441,6 +458,15 @@ extern void edac_mc_handle_ue(struct mem_ctl_info *mci, | |||
441 | int row, const char *msg); | 458 | int row, const char *msg); |
442 | extern void edac_mc_handle_ue_no_info(struct mem_ctl_info *mci, | 459 | extern void edac_mc_handle_ue_no_info(struct mem_ctl_info *mci, |
443 | const char *msg); | 460 | const char *msg); |
461 | extern void edac_mc_handle_fbd_ue(struct mem_ctl_info *mci, | ||
462 | unsigned int csrow, | ||
463 | unsigned int channel0, | ||
464 | unsigned int channel1, | ||
465 | char *msg); | ||
466 | extern void edac_mc_handle_fbd_ce(struct mem_ctl_info *mci, | ||
467 | unsigned int csrow, | ||
468 | unsigned int channel, | ||
469 | char *msg); | ||
444 | 470 | ||
445 | /* | 471 | /* |
446 | * This kmalloc's and initializes all the structures. | 472 | * This kmalloc's and initializes all the structures. |