diff options
author | Peter Tyser <ptyser@xes-inc.com> | 2010-03-10 18:23:15 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-03-12 18:52:40 -0500 |
commit | 8004fd2ad6042ae24d3913cf5089909781db3a25 (patch) | |
tree | 420b2c1fc2241f2f3154a63fd89097c0d2778bb6 /drivers/edac | |
parent | 8de5c1a165be5624a1003ce17877299e04b2afd6 (diff) |
edac: e752x: add dram scrubbing support
Add support to scrub DRAM using the e752x integrated memory scrubbing
engine. The e7320/7520/e7525 chipsets support scrubbing at one rate while
the i3100 chipset supports a normal and fast rate.
A similar patch was originally sent back in 2008:
http://sourceforge.net/mailarchive/forum.php?thread_name=1204835866.25206.70.camel@localhost.localdomain&forum_name=bluesmoke-devel
This version has the following updates:
- Use 16-bit PCI config cycles to access MCHSCRB register
e7320/7520/e7525 docs say register is 16bits wide, i3100 says 8. I
tested 16bits on the i3100 to be safe.
- Recalcuate and round actual scrub rates
The changes have been tested on an i3100-based board.
Signed-off-by: Peter Tyser <ptyser@xes-inc.com>
Signed-off-by: Doug Thompson <dougthompson@xmission.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/edac')
-rw-r--r-- | drivers/edac/e752x_edac.c | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c index 795fcd1eec1c..243e9aacad69 100644 --- a/drivers/edac/e752x_edac.c +++ b/drivers/edac/e752x_edac.c | |||
@@ -75,6 +75,14 @@ static struct edac_pci_ctl_info *e752x_pci; | |||
75 | #define E752X_NR_CSROWS 8 /* number of csrows */ | 75 | #define E752X_NR_CSROWS 8 /* number of csrows */ |
76 | 76 | ||
77 | /* E752X register addresses - device 0 function 0 */ | 77 | /* E752X register addresses - device 0 function 0 */ |
78 | #define E752X_MCHSCRB 0x52 /* Memory Scrub register (16b) */ | ||
79 | /* | ||
80 | * 6:5 Scrub Completion Count | ||
81 | * 3:2 Scrub Rate (i3100 only) | ||
82 | * 01=fast 10=normal | ||
83 | * 1:0 Scrub Mode enable | ||
84 | * 00=off 10=on | ||
85 | */ | ||
78 | #define E752X_DRB 0x60 /* DRAM row boundary register (8b) */ | 86 | #define E752X_DRB 0x60 /* DRAM row boundary register (8b) */ |
79 | #define E752X_DRA 0x70 /* DRAM row attribute register (8b) */ | 87 | #define E752X_DRA 0x70 /* DRAM row attribute register (8b) */ |
80 | /* | 88 | /* |
@@ -240,6 +248,41 @@ static const struct e752x_dev_info e752x_devs[] = { | |||
240 | .ctl_name = "3100"}, | 248 | .ctl_name = "3100"}, |
241 | }; | 249 | }; |
242 | 250 | ||
251 | /* Valid scrub rates for the e752x/3100 hardware memory scrubber. We | ||
252 | * map the scrubbing bandwidth to a hardware register value. The 'set' | ||
253 | * operation finds the 'matching or higher value'. Note that scrubbing | ||
254 | * on the e752x can only be enabled/disabled. The 3100 supports | ||
255 | * a normal and fast mode. | ||
256 | */ | ||
257 | |||
258 | #define SDRATE_EOT 0xFFFFFFFF | ||
259 | |||
260 | struct scrubrate { | ||
261 | u32 bandwidth; /* bandwidth consumed by scrubbing in bytes/sec */ | ||
262 | u16 scrubval; /* register value for scrub rate */ | ||
263 | }; | ||
264 | |||
265 | /* Rate below assumes same performance as i3100 using PC3200 DDR2 in | ||
266 | * normal mode. e752x bridges don't support choosing normal or fast mode, | ||
267 | * so the scrubbing bandwidth value isn't all that important - scrubbing is | ||
268 | * either on or off. | ||
269 | */ | ||
270 | static const struct scrubrate scrubrates_e752x[] = { | ||
271 | {0, 0x00}, /* Scrubbing Off */ | ||
272 | {500000, 0x02}, /* Scrubbing On */ | ||
273 | {SDRATE_EOT, 0x00} /* End of Table */ | ||
274 | }; | ||
275 | |||
276 | /* Fast mode: 2 GByte PC3200 DDR2 scrubbed in 33s = 63161283 bytes/s | ||
277 | * Normal mode: 125 (32000 / 256) times slower than fast mode. | ||
278 | */ | ||
279 | static const struct scrubrate scrubrates_i3100[] = { | ||
280 | {0, 0x00}, /* Scrubbing Off */ | ||
281 | {500000, 0x0a}, /* Normal mode - 32k clocks */ | ||
282 | {62500000, 0x06}, /* Fast mode - 256 clocks */ | ||
283 | {SDRATE_EOT, 0x00} /* End of Table */ | ||
284 | }; | ||
285 | |||
243 | static unsigned long ctl_page_to_phys(struct mem_ctl_info *mci, | 286 | static unsigned long ctl_page_to_phys(struct mem_ctl_info *mci, |
244 | unsigned long page) | 287 | unsigned long page) |
245 | { | 288 | { |
@@ -915,6 +958,68 @@ static void e752x_check(struct mem_ctl_info *mci) | |||
915 | e752x_process_error_info(mci, &info, 1); | 958 | e752x_process_error_info(mci, &info, 1); |
916 | } | 959 | } |
917 | 960 | ||
961 | /* Program byte/sec bandwidth scrub rate to hardware */ | ||
962 | static int set_sdram_scrub_rate(struct mem_ctl_info *mci, u32 *new_bw) | ||
963 | { | ||
964 | const struct scrubrate *scrubrates; | ||
965 | struct e752x_pvt *pvt = (struct e752x_pvt *) mci->pvt_info; | ||
966 | struct pci_dev *pdev = pvt->dev_d0f0; | ||
967 | int i; | ||
968 | |||
969 | if (pvt->dev_info->ctl_dev == PCI_DEVICE_ID_INTEL_3100_0) | ||
970 | scrubrates = scrubrates_i3100; | ||
971 | else | ||
972 | scrubrates = scrubrates_e752x; | ||
973 | |||
974 | /* Translate the desired scrub rate to a e752x/3100 register value. | ||
975 | * Search for the bandwidth that is equal or greater than the | ||
976 | * desired rate and program the cooresponding register value. | ||
977 | */ | ||
978 | for (i = 0; scrubrates[i].bandwidth != SDRATE_EOT; i++) | ||
979 | if (scrubrates[i].bandwidth >= *new_bw) | ||
980 | break; | ||
981 | |||
982 | if (scrubrates[i].bandwidth == SDRATE_EOT) | ||
983 | return -1; | ||
984 | |||
985 | pci_write_config_word(pdev, E752X_MCHSCRB, scrubrates[i].scrubval); | ||
986 | |||
987 | return 0; | ||
988 | } | ||
989 | |||
990 | /* Convert current scrub rate value into byte/sec bandwidth */ | ||
991 | static int get_sdram_scrub_rate(struct mem_ctl_info *mci, u32 *bw) | ||
992 | { | ||
993 | const struct scrubrate *scrubrates; | ||
994 | struct e752x_pvt *pvt = (struct e752x_pvt *) mci->pvt_info; | ||
995 | struct pci_dev *pdev = pvt->dev_d0f0; | ||
996 | u16 scrubval; | ||
997 | int i; | ||
998 | |||
999 | if (pvt->dev_info->ctl_dev == PCI_DEVICE_ID_INTEL_3100_0) | ||
1000 | scrubrates = scrubrates_i3100; | ||
1001 | else | ||
1002 | scrubrates = scrubrates_e752x; | ||
1003 | |||
1004 | /* Find the bandwidth matching the memory scrubber configuration */ | ||
1005 | pci_read_config_word(pdev, E752X_MCHSCRB, &scrubval); | ||
1006 | scrubval = scrubval & 0x0f; | ||
1007 | |||
1008 | for (i = 0; scrubrates[i].bandwidth != SDRATE_EOT; i++) | ||
1009 | if (scrubrates[i].scrubval == scrubval) | ||
1010 | break; | ||
1011 | |||
1012 | if (scrubrates[i].bandwidth == SDRATE_EOT) { | ||
1013 | e752x_printk(KERN_WARNING, | ||
1014 | "Invalid sdram scrub control value: 0x%x\n", scrubval); | ||
1015 | return -1; | ||
1016 | } | ||
1017 | |||
1018 | *bw = scrubrates[i].bandwidth; | ||
1019 | |||
1020 | return 0; | ||
1021 | } | ||
1022 | |||
918 | /* Return 1 if dual channel mode is active. Else return 0. */ | 1023 | /* Return 1 if dual channel mode is active. Else return 0. */ |
919 | static inline int dual_channel_active(u16 ddrcsr) | 1024 | static inline int dual_channel_active(u16 ddrcsr) |
920 | { | 1025 | { |
@@ -1181,6 +1286,8 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx) | |||
1181 | mci->dev_name = pci_name(pdev); | 1286 | mci->dev_name = pci_name(pdev); |
1182 | mci->edac_check = e752x_check; | 1287 | mci->edac_check = e752x_check; |
1183 | mci->ctl_page_to_phys = ctl_page_to_phys; | 1288 | mci->ctl_page_to_phys = ctl_page_to_phys; |
1289 | mci->set_sdram_scrub_rate = set_sdram_scrub_rate; | ||
1290 | mci->get_sdram_scrub_rate = get_sdram_scrub_rate; | ||
1184 | 1291 | ||
1185 | /* set the map type. 1 = normal, 0 = reversed | 1292 | /* set the map type. 1 = normal, 0 = reversed |
1186 | * Must be set before e752x_init_csrows in case csrow mapping | 1293 | * Must be set before e752x_init_csrows in case csrow mapping |