mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-13 01:08:50 +00:00
i7core_edac: Add scrubbing support
Add scrubbing support to i7core_edac, tested on intel Xeon L5638. Signed-off-by: Samuel Gabrielsson <samuel.gabrielsson@gmail.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
parent
ddeb3547d4
commit
e8b6a12710
@ -78,6 +78,8 @@ MODULE_PARM_DESC(use_pci_fixup, "Enable PCI fixup to seek for hidden devices");
|
|||||||
/* OFFSETS for Device 0 Function 0 */
|
/* OFFSETS for Device 0 Function 0 */
|
||||||
|
|
||||||
#define MC_CFG_CONTROL 0x90
|
#define MC_CFG_CONTROL 0x90
|
||||||
|
#define MC_CFG_UNLOCK 0x02
|
||||||
|
#define MC_CFG_LOCK 0x00
|
||||||
|
|
||||||
/* OFFSETS for Device 3 Function 0 */
|
/* OFFSETS for Device 3 Function 0 */
|
||||||
|
|
||||||
@ -98,6 +100,14 @@ MODULE_PARM_DESC(use_pci_fixup, "Enable PCI fixup to seek for hidden devices");
|
|||||||
#define DIMM0_COR_ERR(r) ((r) & 0x7fff)
|
#define DIMM0_COR_ERR(r) ((r) & 0x7fff)
|
||||||
|
|
||||||
/* OFFSETS for Device 3 Function 2, as inicated on Xeon 5500 datasheet */
|
/* OFFSETS for Device 3 Function 2, as inicated on Xeon 5500 datasheet */
|
||||||
|
#define MC_SSRCONTROL 0x48
|
||||||
|
#define SSR_MODE_DISABLE 0x00
|
||||||
|
#define SSR_MODE_ENABLE 0x01
|
||||||
|
#define SSR_MODE_MASK 0x03
|
||||||
|
|
||||||
|
#define MC_SCRUB_CONTROL 0x4c
|
||||||
|
#define STARTSCRUB (1 << 24)
|
||||||
|
|
||||||
#define MC_COR_ECC_CNT_0 0x80
|
#define MC_COR_ECC_CNT_0 0x80
|
||||||
#define MC_COR_ECC_CNT_1 0x84
|
#define MC_COR_ECC_CNT_1 0x84
|
||||||
#define MC_COR_ECC_CNT_2 0x88
|
#define MC_COR_ECC_CNT_2 0x88
|
||||||
@ -1901,6 +1911,116 @@ static int i7core_mce_check_error(void *priv, struct mce *mce)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* set_sdram_scrub_rate This routine sets byte/sec bandwidth scrub rate
|
||||||
|
* to hardware according to SCRUBINTERVAL formula
|
||||||
|
* found in datasheet.
|
||||||
|
*/
|
||||||
|
static int set_sdram_scrub_rate(struct mem_ctl_info *mci, u32 new_bw)
|
||||||
|
{
|
||||||
|
struct i7core_pvt *pvt = mci->pvt_info;
|
||||||
|
struct pci_dev *pdev;
|
||||||
|
const u32 cache_line_size = 64;
|
||||||
|
const u32 freq_dclk = 800*1000000;
|
||||||
|
u32 dw_scrub;
|
||||||
|
u32 dw_ssr;
|
||||||
|
|
||||||
|
/* Get data from the MC register, function 2 */
|
||||||
|
pdev = pvt->pci_mcr[2];
|
||||||
|
if (!pdev)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
pci_read_config_dword(pdev, MC_SCRUB_CONTROL, &dw_scrub);
|
||||||
|
|
||||||
|
if (new_bw == 0) {
|
||||||
|
/* Prepare to disable petrol scrub */
|
||||||
|
dw_scrub &= ~STARTSCRUB;
|
||||||
|
/* Stop the patrol scrub engine */
|
||||||
|
write_and_test(pdev, MC_SCRUB_CONTROL, dw_scrub & ~0x00ffffff);
|
||||||
|
|
||||||
|
/* Get current status of scrub rate and set bit to disable */
|
||||||
|
pci_read_config_dword(pdev, MC_SSRCONTROL, &dw_ssr);
|
||||||
|
dw_ssr &= ~SSR_MODE_MASK;
|
||||||
|
dw_ssr |= SSR_MODE_DISABLE;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Translate the desired scrub rate to a register value and
|
||||||
|
* program the cooresponding register value.
|
||||||
|
*/
|
||||||
|
dw_scrub = 0x00ffffff & (cache_line_size * freq_dclk / new_bw);
|
||||||
|
|
||||||
|
/* Start the patrol scrub engine */
|
||||||
|
pci_write_config_dword(pdev, MC_SCRUB_CONTROL,
|
||||||
|
STARTSCRUB | dw_scrub);
|
||||||
|
|
||||||
|
/* Get current status of scrub rate and set bit to enable */
|
||||||
|
pci_read_config_dword(pdev, MC_SSRCONTROL, &dw_ssr);
|
||||||
|
dw_ssr &= ~SSR_MODE_MASK;
|
||||||
|
dw_ssr |= SSR_MODE_ENABLE;
|
||||||
|
}
|
||||||
|
/* Disable or enable scrubbing */
|
||||||
|
pci_write_config_dword(pdev, MC_SSRCONTROL, dw_ssr);
|
||||||
|
|
||||||
|
return new_bw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get_sdram_scrub_rate This routine convert current scrub rate value
|
||||||
|
* into byte/sec bandwidth accourding to
|
||||||
|
* SCRUBINTERVAL formula found in datasheet.
|
||||||
|
*/
|
||||||
|
static int get_sdram_scrub_rate(struct mem_ctl_info *mci)
|
||||||
|
{
|
||||||
|
struct i7core_pvt *pvt = mci->pvt_info;
|
||||||
|
struct pci_dev *pdev;
|
||||||
|
const u32 cache_line_size = 64;
|
||||||
|
const u32 freq_dclk = 800*1000000;
|
||||||
|
u32 scrubval;
|
||||||
|
|
||||||
|
/* Get data from the MC register, function 2 */
|
||||||
|
pdev = pvt->pci_mcr[2];
|
||||||
|
if (!pdev)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
/* Get current scrub control data */
|
||||||
|
pci_read_config_dword(pdev, MC_SCRUB_CONTROL, &scrubval);
|
||||||
|
|
||||||
|
/* Mask highest 8-bits to 0 */
|
||||||
|
scrubval &= 0x00ffffff;
|
||||||
|
if (!scrubval)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Calculate scrub rate value into byte/sec bandwidth */
|
||||||
|
return 0xffffffff & (cache_line_size * freq_dclk / (u64) scrubval);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void enable_sdram_scrub_setting(struct mem_ctl_info *mci)
|
||||||
|
{
|
||||||
|
struct i7core_pvt *pvt = mci->pvt_info;
|
||||||
|
u32 pci_lock;
|
||||||
|
|
||||||
|
/* Unlock writes to pci registers */
|
||||||
|
pci_read_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, &pci_lock);
|
||||||
|
pci_lock &= ~0x3;
|
||||||
|
pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL,
|
||||||
|
pci_lock | MC_CFG_UNLOCK);
|
||||||
|
|
||||||
|
mci->set_sdram_scrub_rate = set_sdram_scrub_rate;
|
||||||
|
mci->get_sdram_scrub_rate = get_sdram_scrub_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void disable_sdram_scrub_setting(struct mem_ctl_info *mci)
|
||||||
|
{
|
||||||
|
struct i7core_pvt *pvt = mci->pvt_info;
|
||||||
|
u32 pci_lock;
|
||||||
|
|
||||||
|
/* Lock writes to pci registers */
|
||||||
|
pci_read_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, &pci_lock);
|
||||||
|
pci_lock &= ~0x3;
|
||||||
|
pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL,
|
||||||
|
pci_lock | MC_CFG_LOCK);
|
||||||
|
}
|
||||||
|
|
||||||
static void i7core_pci_ctl_create(struct i7core_pvt *pvt)
|
static void i7core_pci_ctl_create(struct i7core_pvt *pvt)
|
||||||
{
|
{
|
||||||
pvt->i7core_pci = edac_pci_create_generic_ctl(
|
pvt->i7core_pci = edac_pci_create_generic_ctl(
|
||||||
@ -1939,6 +2059,9 @@ static void i7core_unregister_mci(struct i7core_dev *i7core_dev)
|
|||||||
debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
|
debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
|
||||||
__func__, mci, &i7core_dev->pdev[0]->dev);
|
__func__, mci, &i7core_dev->pdev[0]->dev);
|
||||||
|
|
||||||
|
/* Disable scrubrate setting */
|
||||||
|
disable_sdram_scrub_setting(mci);
|
||||||
|
|
||||||
/* Disable MCE NMI handler */
|
/* Disable MCE NMI handler */
|
||||||
edac_mce_unregister(&pvt->edac_mce);
|
edac_mce_unregister(&pvt->edac_mce);
|
||||||
|
|
||||||
@ -2012,6 +2135,9 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev)
|
|||||||
/* Set the function pointer to an actual operation function */
|
/* Set the function pointer to an actual operation function */
|
||||||
mci->edac_check = i7core_check_error;
|
mci->edac_check = i7core_check_error;
|
||||||
|
|
||||||
|
/* Enable scrubrate setting */
|
||||||
|
enable_sdram_scrub_setting(mci);
|
||||||
|
|
||||||
/* add this new MC control structure to EDAC's list of MCs */
|
/* add this new MC control structure to EDAC's list of MCs */
|
||||||
if (unlikely(edac_mc_add_mc(mci))) {
|
if (unlikely(edac_mc_add_mc(mci))) {
|
||||||
debugf0("MC: " __FILE__
|
debugf0("MC: " __FILE__
|
||||||
|
Loading…
x
Reference in New Issue
Block a user