linux/drivers/edac/i7core_edac.c

// SPDX-License-Identifier: GPL-2.0-only
/* Intel i7 core/Nehalem Memory Controller kernel module
 *
 * This driver supports the memory controllers found on the Intel
 * processor families i7core, i7core 7xx/8xx, i5core, Xeon 35xx,
 * Xeon 55xx and Xeon 56xx also known as Nehalem, Nehalem-EP, Lynnfield
 * and Westmere-EP.
 *
 * Copyright (c) 2009-2010 by:
 *	 Mauro Carvalho Chehab
 *
 * Red Hat Inc. https://www.redhat.com
 *
 * Forked and adapted from the i5400_edac driver
 *
 * Based on the following public Intel datasheets:
 * Intel Core i7 Processor Extreme Edition and Intel Core i7 Processor
 * Datasheet, Volume 2:
 *	http://download.intel.com/design/processor/datashts/320835.pdf
 * Intel Xeon Processor 5500 Series Datasheet Volume 2
 *	http://www.intel.com/Assets/PDF/datasheet/321322.pdf
 * also available at:
 * 	http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/dmi.h>
#include <linux/edac.h>
#include <linux/mmzone.h>
#include <linux/smp.h>
#include <asm/mce.h>
#include <asm/processor.h>
#include <asm/div64.h>

#include "edac_module.h"

/* Static vars */
static LIST_HEAD(i7core_edac_list);
static DEFINE_MUTEX(i7core_edac_lock);
static int probed;

static int use_pci_fixup;
module_param(use_pci_fixup, int, 0444);
MODULE_PARM_DESC();
/*
 * This is used for Nehalem-EP and Nehalem-EX devices, where the non-core
 * registers start at bus 255, and are not reported by BIOS.
 * We currently find devices with only 2 sockets. In order to support more QPI
 * Quick Path Interconnect, just increment this number.
 */
#define MAX_SOCKET_BUSES


/*
 * Alter this version for the module when modifications are made
 */
#define I7CORE_REVISION
#define EDAC_MOD_STR

/*
 * Debug macros
 */
#define i7core_printk(level, fmt, arg...)

#define i7core_mc_printk(mci, level, fmt, arg...)

/*
 * i7core Memory Controller Registers
 */

	/* OFFSETS for Device 0 Function 0 */

#define MC_CFG_CONTROL
  #define MC_CFG_UNLOCK
  #define MC_CFG_LOCK

	/* OFFSETS for Device 3 Function 0 */

#define MC_CONTROL
#define MC_STATUS
#define MC_MAX_DOD

/*
 * OFFSETS for Device 3 Function 4, as indicated on Xeon 5500 datasheet:
 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
 */

#define MC_TEST_ERR_RCV1
  #define DIMM2_COR_ERR(r)

#define MC_TEST_ERR_RCV0
  #define DIMM1_COR_ERR(r)
  #define DIMM0_COR_ERR(r)

/* OFFSETS for Device 3 Function 2, as indicated on Xeon 5500 datasheet */
#define MC_SSRCONTROL
  #define SSR_MODE_DISABLE
  #define SSR_MODE_ENABLE
  #define SSR_MODE_MASK

#define MC_SCRUB_CONTROL
  #define STARTSCRUB
  #define SCRUBINTERVAL_MASK

#define MC_COR_ECC_CNT_0
#define MC_COR_ECC_CNT_1
#define MC_COR_ECC_CNT_2
#define MC_COR_ECC_CNT_3
#define MC_COR_ECC_CNT_4
#define MC_COR_ECC_CNT_5

#define DIMM_TOP_COR_ERR(r)
#define DIMM_BOT_COR_ERR(r)


	/* OFFSETS for Devices 4,5 and 6 Function 0 */

#define MC_CHANNEL_DIMM_INIT_PARAMS
  #define THREE_DIMMS_PRESENT
  #define SINGLE_QUAD_RANK_PRESENT
  #define QUAD_RANK_PRESENT
  #define REGISTERED_DIMM

#define MC_CHANNEL_MAPPER
  #define RDLCH(r, ch)
  #define WRLCH(r, ch)

#define MC_CHANNEL_RANK_PRESENT
  #define RANK_PRESENT_MASK

#define MC_CHANNEL_ADDR_MATCH
#define MC_CHANNEL_ERROR_MASK
#define MC_CHANNEL_ERROR_INJECT
  #define INJECT_ADDR_PARITY
  #define INJECT_ECC
  #define MASK_CACHELINE
  #define MASK_FULL_CACHELINE
  #define MASK_MSB32_CACHELINE
  #define MASK_LSB32_CACHELINE
  #define NO_MASK_CACHELINE
  #define REPEAT_EN

	/* OFFSETS for Devices 4,5 and 6 Function 1 */

#define MC_DOD_CH_DIMM0
#define MC_DOD_CH_DIMM1
#define MC_DOD_CH_DIMM2
  #define RANKOFFSET_MASK
  #define RANKOFFSET(x)
  #define DIMM_PRESENT_MASK
  #define DIMM_PRESENT(x)
  #define MC_DOD_NUMBANK_MASK
  #define MC_DOD_NUMBANK(x)
  #define MC_DOD_NUMRANK_MASK
  #define MC_DOD_NUMRANK(x)
  #define MC_DOD_NUMROW_MASK
  #define MC_DOD_NUMROW(x)
  #define MC_DOD_NUMCOL_MASK
  #define MC_DOD_NUMCOL(x)

#define MC_RANK_PRESENT

#define MC_SAG_CH_0
#define MC_SAG_CH_1
#define MC_SAG_CH_2
#define MC_SAG_CH_3
#define MC_SAG_CH_4
#define MC_SAG_CH_5
#define MC_SAG_CH_6
#define MC_SAG_CH_7

#define MC_RIR_LIMIT_CH_0
#define MC_RIR_LIMIT_CH_1
#define MC_RIR_LIMIT_CH_2
#define MC_RIR_LIMIT_CH_3
#define MC_RIR_LIMIT_CH_4
#define MC_RIR_LIMIT_CH_5
#define MC_RIR_LIMIT_CH_6
#define MC_RIR_LIMIT_CH_7
#define MC_RIR_LIMIT_MASK

#define MC_RIR_WAY_CH
  #define MC_RIR_WAY_OFFSET_MASK
  #define MC_RIR_WAY_RANK_MASK

/*
 * i7core structs
 */

#define NUM_CHANS
#define MAX_DIMMS
#define MAX_MCR_FUNC
#define MAX_CHAN_FUNC

struct i7core_info {};


struct i7core_inject {};

struct i7core_channel {};

struct pci_id_descr {};

struct pci_id_table {};

struct i7core_dev {};

struct i7core_pvt {};

#define PCI_DESCR(device, function, device_id)

static const struct pci_id_descr pci_dev_descr_i7core_nehalem[] =;

static const struct pci_id_descr pci_dev_descr_lynnfield[] =;

static const struct pci_id_descr pci_dev_descr_i7core_westmere[] =;

#define PCI_ID_TABLE_ENTRY(A)
static const struct pci_id_table pci_dev_table[] =;

/*
 *	pci_device_id	table for which devices we are looking for
 */
static const struct pci_device_id i7core_pci_tbl[] =;

/****************************************************************************
			Ancillary status routines
 ****************************************************************************/

	/* MC_CONTROL bits */
#define CH_ACTIVE(pvt, ch)
#define ECCx8(pvt)

	/* MC_STATUS bits */
#define ECC_ENABLED(pvt)
#define CH_DISABLED(pvt, ch)

	/* MC_MAX_DOD read functions */
static inline int numdimms(u32 dimms)
{}

static inline int numrank(u32 rank)
{}

static inline int numbank(u32 bank)
{}

static inline int numrow(u32 row)
{}

static inline int numcol(u32 col)
{}

static struct i7core_dev *get_i7core_dev(u8 socket)
{}

static struct i7core_dev *alloc_i7core_dev(u8 socket,
					   const struct pci_id_table *table)
{}

static void free_i7core_dev(struct i7core_dev *i7core_dev)
{}

/****************************************************************************
			Memory check routines
 ****************************************************************************/

static int get_dimm_config(struct mem_ctl_info *mci)
{}

/****************************************************************************
			Error insertion routines
 ****************************************************************************/

#define to_mci(k)

/* The i7core has independent error injection features per channel.
   However, to have a simpler code, we don't allow enabling error injection
   on more than one channel.
   Also, since a change at an inject parameter will be applied only at enable,
   we're disabling error injection on all write calls to the sysfs nodes that
   controls the error code injection.
 */
static int disable_inject(const struct mem_ctl_info *mci)
{}

/*
 * i7core inject inject.section
 *
 *	accept and store error injection inject.section value
 *	bit 0 - refers to the lower 32-byte half cacheline
 *	bit 1 - refers to the upper 32-byte half cacheline
 */
static ssize_t i7core_inject_section_store(struct device *dev,
					   struct device_attribute *mattr,
					   const char *data, size_t count)
{}

static ssize_t i7core_inject_section_show(struct device *dev,
					  struct device_attribute *mattr,
					  char *data)
{}

/*
 * i7core inject.type
 *
 *	accept and store error injection inject.section value
 *	bit 0 - repeat enable - Enable error repetition
 *	bit 1 - inject ECC error
 *	bit 2 - inject parity error
 */
static ssize_t i7core_inject_type_store(struct device *dev,
					struct device_attribute *mattr,
					const char *data, size_t count)
{}

static ssize_t i7core_inject_type_show(struct device *dev,
				       struct device_attribute *mattr,
				       char *data)
{}

/*
 * i7core_inject_inject.eccmask_store
 *
 * The type of error (UE/CE) will depend on the inject.eccmask value:
 *   Any bits set to a 1 will flip the corresponding ECC bit
 *   Correctable errors can be injected by flipping 1 bit or the bits within
 *   a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
 *   23:16 and 31:24). Flipping bits in two symbol pairs will cause an
 *   uncorrectable error to be injected.
 */
static ssize_t i7core_inject_eccmask_store(struct device *dev,
					   struct device_attribute *mattr,
					   const char *data, size_t count)
{}

static ssize_t i7core_inject_eccmask_show(struct device *dev,
					  struct device_attribute *mattr,
					  char *data)
{}

/*
 * i7core_addrmatch
 *
 * The type of error (UE/CE) will depend on the inject.eccmask value:
 *   Any bits set to a 1 will flip the corresponding ECC bit
 *   Correctable errors can be injected by flipping 1 bit or the bits within
 *   a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
 *   23:16 and 31:24). Flipping bits in two symbol pairs will cause an
 *   uncorrectable error to be injected.
 */

#define DECLARE_ADDR_MATCH(param, limit)

#define ATTR_ADDR_MATCH(param)

DECLARE_ADDR_MATCH(channel, 3);
DECLARE_ADDR_MATCH(dimm, 3);
DECLARE_ADDR_MATCH(rank, 4);
DECLARE_ADDR_MATCH(bank, 32);
DECLARE_ADDR_MATCH(page, 0x10000);
DECLARE_ADDR_MATCH(col, 0x4000);

ATTR_ADDR_MATCH();
ATTR_ADDR_MATCH();
ATTR_ADDR_MATCH();
ATTR_ADDR_MATCH();
ATTR_ADDR_MATCH();
ATTR_ADDR_MATCH();

static int write_and_test(struct pci_dev *dev, const int where, const u32 val)
{}

/*
 * This routine prepares the Memory Controller for error injection.
 * The error will be injected when some process tries to write to the
 * memory that matches the given criteria.
 * The criteria can be set in terms of a mask where dimm, rank, bank, page
 * and col can be specified.
 * A -1 value for any of the mask items will make the MCU to ignore
 * that matching criteria for error injection.
 *
 * It should be noticed that the error will only happen after a write operation
 * on a memory that matches the condition. if REPEAT_EN is not enabled at
 * inject mask, then it will produce just one error. Otherwise, it will repeat
 * until the injectmask would be cleaned.
 *
 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
 *    is reliable enough to check if the MC is using the
 *    three channels. However, this is not clear at the datasheet.
 */
static ssize_t i7core_inject_enable_store(struct device *dev,
					  struct device_attribute *mattr,
					  const char *data, size_t count)
{}

static ssize_t i7core_inject_enable_show(struct device *dev,
					 struct device_attribute *mattr,
					 char *data)
{}

#define DECLARE_COUNTER(param)

#define ATTR_COUNTER(param)

DECLARE_COUNTER(0);
DECLARE_COUNTER(1);
DECLARE_COUNTER(2);

ATTR_COUNTER();
ATTR_COUNTER();
ATTR_COUNTER();

/*
 * inject_addrmatch device sysfs struct
 */

static struct attribute *i7core_addrmatch_attrs[] =;

static const struct attribute_group addrmatch_grp =;

static const struct attribute_group *addrmatch_groups[] =;

static void addrmatch_release(struct device *device)
{}

static const struct device_type addrmatch_type =;

/*
 * all_channel_counts sysfs struct
 */

static struct attribute *i7core_udimm_counters_attrs[] =;

static const struct attribute_group all_channel_counts_grp =;

static const struct attribute_group *all_channel_counts_groups[] =;

static void all_channel_counts_release(struct device *device)
{}

static const struct device_type all_channel_counts_type =;

/*
 * inject sysfs attributes
 */

static DEVICE_ATTR(inject_section, S_IRUGO | S_IWUSR,
		   i7core_inject_section_show, i7core_inject_section_store);

static DEVICE_ATTR(inject_type, S_IRUGO | S_IWUSR,
		   i7core_inject_type_show, i7core_inject_type_store);


static DEVICE_ATTR(inject_eccmask, S_IRUGO | S_IWUSR,
		   i7core_inject_eccmask_show, i7core_inject_eccmask_store);

static DEVICE_ATTR(inject_enable, S_IRUGO | S_IWUSR,
		   i7core_inject_enable_show, i7core_inject_enable_store);

static struct attribute *i7core_dev_attrs[] =;

ATTRIBUTE_GROUPS();

static int i7core_create_sysfs_devices(struct mem_ctl_info *mci)
{}

static void i7core_delete_sysfs_devices(struct mem_ctl_info *mci)
{}

/****************************************************************************
	Device initialization routines: put/get, init/exit
 ****************************************************************************/

/*
 *	i7core_put_all_devices	'put' all the devices that we have
 *				reserved via 'get'
 */
static void i7core_put_devices(struct i7core_dev *i7core_dev)
{}

static void i7core_put_all_devices(void)
{}

static void __init i7core_xeon_pci_fixup(const struct pci_id_table *table)
{}

static unsigned i7core_pci_lastbus(void)
{}

/*
 *	i7core_get_all_devices	Find and perform 'get' operation on the MCH's
 *			device/functions we want to reference for this driver
 *
 *			Need to 'get' device 16 func 1 and func 2
 */
static int i7core_get_onedevice(struct pci_dev **prev,
				const struct pci_id_table *table,
				const unsigned devno,
				const unsigned last_bus)
{}

static int i7core_get_all_devices(void)
{}

static int mci_bind_devs(struct mem_ctl_info *mci,
			 struct i7core_dev *i7core_dev)
{}

/****************************************************************************
			Error check routines
 ****************************************************************************/

static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
					 const int chan,
					 const int new0,
					 const int new1,
					 const int new2)
{}

static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci)
{}

/* This function is based on the device 3 function 4 registers as described on:
 * Intel Xeon Processor 5500 Series Datasheet Volume 2
 *	http://www.intel.com/Assets/PDF/datasheet/321322.pdf
 * also available at:
 * 	http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
 */
static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci)
{}

/*
 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
 * Architectures Software Developer’s Manual Volume 3B.
 * Nehalem are defined as family 0x06, model 0x1a
 *
 * The MCA registers used here are the following ones:
 *     struct mce field	MCA Register
 *     m->status	MSR_IA32_MC8_STATUS
 *     m->addr		MSR_IA32_MC8_ADDR
 *     m->misc		MSR_IA32_MC8_MISC
 * In the case of Nehalem, the error information is masked at .status and .misc
 * fields
 */
static void i7core_mce_output_error(struct mem_ctl_info *mci,
				    const struct mce *m)
{}

/*
 *	i7core_check_error	Retrieve and process errors reported by the
 *				hardware. Called by the Core module.
 */
static void i7core_check_error(struct mem_ctl_info *mci, struct mce *m)
{}

/*
 * Check that logging is enabled and that this is the right type
 * of error for us to handle.
 */
static int i7core_mce_check_error(struct notifier_block *nb, unsigned long val,
				  void *data)
{}

static struct notifier_block i7_mce_dec =;

struct memdev_dmi_entry {} __attribute__((packed));


/*
 * Decode the DRAM Clock Frequency, be paranoid, make sure that all
 * memory devices show the same speed, and if they don't then consider
 * all speeds to be invalid.
 */
static void decode_dclk(const struct dmi_header *dh, void *_dclk_freq)
{}

/*
 * The default DCLK frequency is used as a fallback if we
 * fail to find anything reliable in the DMI. The value
 * is taken straight from the datasheet.
 */
#define DEFAULT_DCLK_FREQ

static int get_dclk_freq(void)
{}

/*
 * 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)
{}

/*
 * get_sdram_scrub_rate		This routine convert current scrub rate value
 *				into byte/sec bandwidth according to
 *				SCRUBINTERVAL formula found in datasheet.
 */
static int get_sdram_scrub_rate(struct mem_ctl_info *mci)
{}

static void enable_sdram_scrub_setting(struct mem_ctl_info *mci)
{}

static void disable_sdram_scrub_setting(struct mem_ctl_info *mci)
{}

static void i7core_pci_ctl_create(struct i7core_pvt *pvt)
{}

static void i7core_pci_ctl_release(struct i7core_pvt *pvt)
{}

static void i7core_unregister_mci(struct i7core_dev *i7core_dev)
{}

static int i7core_register_mci(struct i7core_dev *i7core_dev)
{}

/*
 *	i7core_probe	Probe for ONE instance of device to see if it is
 *			present.
 *	return:
 *		0 for FOUND a device
 *		< 0 for error code
 */

static int i7core_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{}

/*
 *	i7core_remove	destructor for one instance of device
 *
 */
static void i7core_remove(struct pci_dev *pdev)
{}

MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);

/*
 *	i7core_driver	pci_driver structure for this module
 *
 */
static struct pci_driver i7core_driver =;

/*
 *	i7core_init		Module entry function
 *			Try to initialize this module for its devices
 */
static int __init i7core_init(void)
{}

/*
 *	i7core_exit()	Module exit function
 *			Unregister the driver
 */
static void __exit i7core_exit(void)
{}

module_init();
module_exit(i7core_exit);

MODULE_LICENSE();
MODULE_AUTHOR();
MODULE_AUTHOR();
MODULE_DESCRIPTION();

module_param(edac_op_state, int, 0444);
MODULE_PARM_DESC();