linux/drivers/edac/i7300_edac.c

// SPDX-License-Identifier: GPL-2.0-only
/*
 * Intel 7300 class Memory Controllers kernel module (Clarksboro)
 *
 * Copyright (c) 2010 by:
 *	 Mauro Carvalho Chehab
 *
 * Red Hat Inc. https://www.redhat.com
 *
 * Intel 7300 Chipset Memory Controller Hub (MCH) - Datasheet
 *	http://www.intel.com/Assets/PDF/datasheet/318082.pdf
 *
 * TODO: The chipset allow checking for PCI Express errors also. Currently,
 *	 the driver covers only memory error errors
 *
 * This driver uses "csrows" EDAC attribute to represent DIMM slot#
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/slab.h>
#include <linux/edac.h>
#include <linux/mmzone.h>

#include "edac_module.h"

/*
 * Alter this version for the I7300 module when modifications are made
 */
#define I7300_REVISION

#define EDAC_MOD_STR

#define i7300_printk(level, fmt, arg...)

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

/***********************************************
 * i7300 Limit constants Structs and static vars
 ***********************************************/

/*
 * Memory topology is organized as:
 *	Branch 0 - 2 channels: channels 0 and 1 (FDB0 PCI dev 21.0)
 *	Branch 1 - 2 channels: channels 2 and 3 (FDB1 PCI dev 22.0)
 * Each channel can have to 8 DIMM sets (called as SLOTS)
 * Slots should generally be filled in pairs
 *	Except on Single Channel mode of operation
 *		just slot 0/channel0 filled on this mode
 *	On normal operation mode, the two channels on a branch should be
 *		filled together for the same SLOT#
 * When in mirrored mode, Branch 1 replicate memory at Branch 0, so, the four
 *		channels on both branches should be filled
 */

/* Limits for i7300 */
#define MAX_SLOTS
#define MAX_BRANCHES
#define MAX_CH_PER_BRANCH
#define MAX_CHANNELS
#define MAX_MIR

#define to_channel(ch, branch)

#define to_csrow(slot, ch, branch)

/* Device name and register DID (Device ID) */
struct i7300_dev_info {};

/* Table of devices attributes supported by this driver */
static const struct i7300_dev_info i7300_devs[] =;

struct i7300_dimm_info {};

/* driver private data structure */
struct i7300_pvt {};

/* FIXME: Why do we need to have this static? */
static struct edac_pci_ctl_info *i7300_pci;

/***************************************************
 * i7300 Register definitions for memory enumeration
 ***************************************************/

/*
 * Device 16,
 * Function 0: System Address (not documented)
 * Function 1: Memory Branch Map, Control, Errors Register
 */

	/* OFFSETS for Function 0 */
#define AMBASE
#define MAXCH
#define MAXDIMMPERCH

	/* OFFSETS for Function 1 */
#define MC_SETTINGS
  #define IS_MIRRORED(mc)
  #define IS_ECC_ENABLED(mc)
  #define IS_RETRY_ENABLED(mc)
  #define IS_SCRBALGO_ENHANCED(mc)

#define MC_SETTINGS_A
  #define IS_SINGLE_MODE(mca)

#define TOLM

#define MIR0
#define MIR1
#define MIR2

/*
 * Note: Other Intel EDAC drivers use AMBPRESENT to identify if the available
 * memory. From datasheet item 7.3.1 (FB-DIMM technology & organization), it
 * seems that we cannot use this information directly for the same usage.
 * Each memory slot may have up to 2 AMB interfaces, one for income and another
 * for outcome interface to the next slot.
 * For now, the driver just stores the AMB present registers, but rely only at
 * the MTR info to detect memory.
 * Datasheet is also not clear about how to map each AMBPRESENT registers to
 * one of the 4 available channels.
 */
#define AMBPRESENT_0
#define AMBPRESENT_1

static const u16 mtr_regs[MAX_SLOTS] =;

/*
 * Defines to extract the vaious fields from the
 *	MTRx - Memory Technology Registers
 */
#define MTR_DIMMS_PRESENT(mtr)
#define MTR_DIMMS_ETHROTTLE(mtr)
#define MTR_DRAM_WIDTH(mtr)
#define MTR_DRAM_BANKS(mtr)
#define MTR_DIMM_RANKS(mtr)
#define MTR_DIMM_ROWS(mtr)
#define MTR_DRAM_BANKS_ADDR_BITS
#define MTR_DIMM_ROWS_ADDR_BITS(mtr)
#define MTR_DIMM_COLS(mtr)
#define MTR_DIMM_COLS_ADDR_BITS(mtr)

/************************************************
 * i7300 Register definitions for error detection
 ************************************************/

/*
 * Device 16.1: FBD Error Registers
 */
#define FERR_FAT_FBD
static const char *ferr_fat_fbd_name[] =;
#define GET_FBD_FAT_IDX(fbderr)
#define FERR_FAT_FBD_ERR_MASK

#define FERR_NF_FBD
static const char *ferr_nf_fbd_name[] =;
#define GET_FBD_NF_IDX(fbderr)
#define FERR_NF_FBD_ERR_MASK

#define EMASK_FBD
#define EMASK_FBD_ERR_MASK

/*
 * Device 16.2: Global Error Registers
 */

#define FERR_GLOBAL_HI
static const char *ferr_global_hi_name[] =;
#define ferr_global_hi_is_fatal(errno)

#define FERR_GLOBAL_LO
static const char *ferr_global_lo_name[] =;
#define ferr_global_lo_is_fatal(errno)

#define NRECMEMA
  #define NRECMEMA_BANK(v)
  #define NRECMEMA_RANK(v)

#define NRECMEMB
  #define NRECMEMB_IS_WR(v)
  #define NRECMEMB_CAS(v)
  #define NRECMEMB_RAS(v)

#define REDMEMA

#define REDMEMB

#define RECMEMA
  #define RECMEMA_BANK(v)
  #define RECMEMA_RANK(v)

#define RECMEMB
  #define RECMEMB_IS_WR(v)
  #define RECMEMB_CAS(v)
  #define RECMEMB_RAS(v)

/********************************************
 * i7300 Functions related to error detection
 ********************************************/

/**
 * get_err_from_table() - Gets the error message from a table
 * @table:	table name (array of char *)
 * @size:	number of elements at the table
 * @pos:	position of the element to be returned
 *
 * This is a small routine that gets the pos-th element of a table. If the
 * element doesn't exist (or it is empty), it returns "reserved".
 * Instead of calling it directly, the better is to call via the macro
 * GET_ERR_FROM_TABLE(), that automatically checks the table size via
 * ARRAY_SIZE() macro
 */
static const char *get_err_from_table(const char *table[], int size, int pos)
{}

#define GET_ERR_FROM_TABLE(table, pos)

/**
 * i7300_process_error_global() - Retrieve the hardware error information from
 *				  the hardware global error registers and
 *				  sends it to dmesg
 * @mci: struct mem_ctl_info pointer
 */
static void i7300_process_error_global(struct mem_ctl_info *mci)
{}

/**
 * i7300_process_fbd_error() - Retrieve the hardware error information from
 *			       the FBD error registers and sends it via
 *			       EDAC error API calls
 * @mci: struct mem_ctl_info pointer
 */
static void i7300_process_fbd_error(struct mem_ctl_info *mci)
{}

/**
 * i7300_check_error() - Calls the error checking subroutines
 * @mci: struct mem_ctl_info pointer
 */
static void i7300_check_error(struct mem_ctl_info *mci)
{
	i7300_process_error_global(mci);
	i7300_process_fbd_error(mci);
};

/**
 * i7300_clear_error() - Clears the error registers
 * @mci: struct mem_ctl_info pointer
 */
static void i7300_clear_error(struct mem_ctl_info *mci)
{}

/**
 * i7300_enable_error_reporting() - Enable the memory reporting logic at the
 *				    hardware
 * @mci: struct mem_ctl_info pointer
 */
static void i7300_enable_error_reporting(struct mem_ctl_info *mci)
{}

/************************************************
 * i7300 Functions related to memory enumberation
 ************************************************/

/**
 * decode_mtr() - Decodes the MTR descriptor, filling the edac structs
 * @pvt: pointer to the private data struct used by i7300 driver
 * @slot: DIMM slot (0 to 7)
 * @ch: Channel number within the branch (0 or 1)
 * @branch: Branch number (0 or 1)
 * @dinfo: Pointer to DIMM info where dimm size is stored
 * @dimm: Pointer to the struct dimm_info that corresponds to that element
 */
static int decode_mtr(struct i7300_pvt *pvt,
		      int slot, int ch, int branch,
		      struct i7300_dimm_info *dinfo,
		      struct dimm_info *dimm)
{}

/**
 * print_dimm_size() - Prints dump of the memory organization
 * @pvt: pointer to the private data struct used by i7300 driver
 *
 * Useful for debug. If debug is disabled, this routine do nothing
 */
static void print_dimm_size(struct i7300_pvt *pvt)
{}

/**
 * i7300_init_csrows() - Initialize the 'csrows' table within
 *			 the mci control structure with the
 *			 addressing of memory.
 * @mci: struct mem_ctl_info pointer
 */
static int i7300_init_csrows(struct mem_ctl_info *mci)
{}

/**
 * decode_mir() - Decodes Memory Interleave Register (MIR) info
 * @mir_no: number of the MIR register to decode
 * @mir: array with the MIR data cached on the driver
 */
static void decode_mir(int mir_no, u16 mir[MAX_MIR])
{}

/**
 * i7300_get_mc_regs() - Get the contents of the MC enumeration registers
 * @mci: struct mem_ctl_info pointer
 *
 * Data read is cached internally for its usage when needed
 */
static int i7300_get_mc_regs(struct mem_ctl_info *mci)
{}

/*************************************************
 * i7300 Functions related to device probe/release
 *************************************************/

/**
 * i7300_put_devices() - Release the PCI devices
 * @mci: struct mem_ctl_info pointer
 */
static void i7300_put_devices(struct mem_ctl_info *mci)
{}

/**
 * i7300_get_devices() - Find and perform 'get' operation on the MCH's
 *			 device/functions we want to reference for this driver
 * @mci: struct mem_ctl_info pointer
 *
 * Access and prepare the several devices for usage:
 * I7300 devices used by this driver:
 *    Device 16, functions 0,1 and 2:	PCI_DEVICE_ID_INTEL_I7300_MCH_ERR
 *    Device 21 function 0:		PCI_DEVICE_ID_INTEL_I7300_MCH_FB0
 *    Device 22 function 0:		PCI_DEVICE_ID_INTEL_I7300_MCH_FB1
 */
static int i7300_get_devices(struct mem_ctl_info *mci)
{}

/**
 * i7300_init_one() - Probe for one instance of the device
 * @pdev: struct pci_dev pointer
 * @id: struct pci_device_id pointer - currently unused
 */
static int i7300_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
{}

/**
 * i7300_remove_one() - Remove the driver
 * @pdev: struct pci_dev pointer
 */
static void i7300_remove_one(struct pci_dev *pdev)
{}

/*
 * pci_device_id: table for which devices we are looking for
 *
 * Has only 8086:360c PCI ID
 */
static const struct pci_device_id i7300_pci_tbl[] =;

MODULE_DEVICE_TABLE(pci, i7300_pci_tbl);

/*
 * i7300_driver: pci_driver structure for this module
 */
static struct pci_driver i7300_driver =;

/**
 * i7300_init() - Registers the driver
 */
static int __init i7300_init(void)
{}

/**
 * i7300_exit() - Unregisters the driver
 */
static void __exit i7300_exit(void)
{}

module_init();
module_exit(i7300_exit);

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

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