/*
* Intel 5100 Memory Controllers kernel module
*
* This file may be distributed under the terms of the
* GNU General Public License.
*
* This module is based on the following document:
*
* Intel 5100X Chipset Memory Controller Hub (MCH) - Datasheet
* http://download.intel.com/design/chipsets/datashts/318378.pdf
*
* The intel 5100 has two independent channels. EDAC core currently
* can not reflect this configuration so instead the chip-select
* rows for each respective channel are laid out one after another,
* the first half belonging to channel 0, the second half belonging
* to channel 1.
*
* This driver is for DDR2 DIMMs, and it uses chip select to select among the
* several ranks. However, instead of showing memories as ranks, it outputs
* them as DIMM's. An internal table creates the association between ranks
* and DIMM's.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/edac.h>
#include <linux/delay.h>
#include <linux/mmzone.h>
#include <linux/debugfs.h>
#include "edac_core.h"
/* register addresses */
/* device 16, func 1 */
#define I5100_MC 0x40 /* Memory Control Register */
#define I5100_MC_SCRBEN_MASK (1 << 7)
#define I5100_MC_SCRBDONE_MASK (1 << 4)
#define I5100_MS 0x44 /* Memory Status Register */
#define I5100_SPDDATA 0x48 /* Serial Presence Detect Status Reg */
#define I5100_SPDCMD 0x4c /* Serial Presence Detect Command Reg */
#define I5100_TOLM 0x6c /* Top of Low Memory */
#define I5100_MIR0 0x80 /* Memory Interleave Range 0 */
#define I5100_MIR1 0x84 /* Memory Interleave Range 1 */
#define I5100_AMIR_0 0x8c /* Adjusted Memory Interleave Range 0 */
#define I5100_AMIR_1 0x90 /* Adjusted Memory Interleave Range 1 */
#define I5100_FERR_NF_MEM 0xa0 /* MC First Non Fatal Errors */
#define I5100_FERR_NF_MEM_M16ERR_MASK (1 << 16)
#define I5100_FERR_NF_MEM_M15ERR_MASK (1 << 15)
#define I5100_FERR_NF_MEM_M14ERR_MASK (1 << 14)
#define I5100_FERR_NF_MEM_M12ERR_MASK (1 << 12)
#define I5100_FERR_NF_MEM_M11ERR_MASK (1 << 11)
#define I5100_FERR_NF_MEM_M10ERR_MASK (1 << 10)
#define I5100_FERR_NF_MEM_M6ERR_MASK (1 << 6)
#define I5100_FERR_NF_MEM_M5ERR_MASK (1 << 5)
#define I5100_FERR_NF_MEM_M4ERR_MASK (1 << 4)
#define I5100_FERR_NF_MEM_M1ERR_MASK (1 << 1)
#define I5100_FERR_NF_MEM_ANY_MASK \
(I5100_FERR_NF_MEM_M16ERR_MASK | \
I5100_FERR_NF_MEM_M15ERR_MASK | \
I5100_FERR_NF_MEM_M14ERR_MASK | \
I5100_FERR_NF_MEM_M12ERR_MASK | \
I5100_FERR_NF_MEM_M11ERR_MASK | \
I5100_FERR_NF_MEM_M10ERR_MASK | \
I5100_FERR_NF_MEM_M6ERR_MASK | \
I5100_FERR_NF_MEM_M5ERR_MASK | \
I5100_FERR_NF_MEM_M4ERR_MASK | \
I5100_FERR_NF_MEM_M1ERR_MASK)
#define I5100_NERR_NF_MEM 0xa4 /* MC Next Non-Fatal Errors */
#define I5100_EMASK_MEM 0xa8 /* MC Error Mask Register */
#define I5100_MEM0EINJMSK0 0x200 /* Injection Mask0 Register Channel 0 */
#define I5100_MEM1EINJMSK0 0x208 /* Injection Mask0 Register Channel 1 */
#define I5100_MEMXEINJMSK0_EINJEN (1 << 27)
#define I5100_MEM0EINJMSK1 0x204 /* Injection Mask1 Register Channel 0 */
#define I5100_MEM1EINJMSK1 0x206 /* Injection Mask1 Register Channel 1 */
/* Device 19, Function 0 */
#define I5100_DINJ0 0x9a
/* device 21 and 22, func 0 */
#define I5100_MTR_0 0x154 /* Memory Technology Registers 0-3 */
#define I5100_DMIR 0x15c /* DIMM Interleave Range */
#define I5100_VALIDLOG 0x18c /* Valid Log Markers */
#define I5100_NRECMEMA 0x190 /* Non-Recoverable Memory Error Log Reg A */
#define I5100_NRECMEMB 0x194 /* Non-Recoverable Memory Error Log Reg B */
#define I5100_REDMEMA 0x198 /* Recoverable Memory Data Error Log Reg A */
#define I5100_REDMEMB 0x19c /* Recoverable Memory Data Error Log Reg B */
#define I5100_RECMEMA 0x1a0 /* Recoverable Memory Error Log Reg A */
#define I5100_RECMEMB 0x1a4 /* Recoverable Memory Error Log Reg B */
#define I5100_MTR_4 0x1b0 /* Memory Technology Registers 4,5 */
/* bit field accessors */
static inline u32 i5100_mc_scrben(u32 mc)
{
return mc >> 7 & 1;
}
static inline u32 i5100_mc_errdeten(u32 mc)
{
return mc >> 5 & 1;
}
static inline u32 i5100_mc_scrbdone(u32 mc)
{
return mc >> 4 & 1;
}
static inline u16 i5100_spddata_rdo(u16 a)
{
return a >> 15 & 1;
}
static inline u16 i5100_spddata_sbe(u16 a)
{
return a >> 13 & 1;
}
static inline u16 i5100_spddata_busy(u16 a)
{
return a >> 12 & 1;
}
static inline u16 i5100_spddata_data(u16 a)
{
return a & ((1 << 8) - 1);
}
static inline u32 i5100_spdcmd_create(u32 dti, u32 ckovrd, u32 sa, u32 ba,
u32 data, u32 cmd)
{
return ((dti & ((1 << 4