// 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(…) …;