linux/drivers/edac/pnd2_edac.c

// SPDX-License-Identifier: GPL-2.0-only
/*
 * Driver for Pondicherry2 memory controller.
 *
 * Copyright (c) 2016, Intel Corporation.
 *
 * [Derived from sb_edac.c]
 *
 * Translation of system physical addresses to DIMM addresses
 * is a two stage process:
 *
 * First the Pondicherry 2 memory controller handles slice and channel interleaving
 * in "sys2pmi()". This is (almost) completley common between platforms.
 *
 * Then a platform specific dunit (DIMM unit) completes the process to provide DIMM,
 * rank, bank, row and column using the appropriate "dunit_ops" functions/parameters.
 */

#include <linux/bitmap.h>
#include <linux/delay.h>
#include <linux/edac.h>
#include <linux/init.h>
#include <linux/math64.h>
#include <linux/mmzone.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/smp.h>

#include <linux/platform_data/x86/p2sb.h>

#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
#include <asm/processor.h>
#include <asm/mce.h>

#include "edac_mc.h"
#include "edac_module.h"
#include "pnd2_edac.h"

#define EDAC_MOD_STR

#define APL_NUM_CHANNELS
#define DNV_NUM_CHANNELS
#define DNV_MAX_DIMMS

enum type {};

struct dram_addr {};

struct pnd2_pvt {};

/*
 * System address space is divided into multiple regions with
 * different interleave rules in each. The as0/as1 regions
 * have no interleaving at all. The as2 region is interleaved
 * between two channels. The mot region is magic and may overlap
 * other regions, with its interleave rules taking precedence.
 * Addresses not in any of these regions are interleaved across
 * all four channels.
 */
static struct region {} mot, as0, as1, as2;

static struct dunit_ops {} *ops;

static struct mem_ctl_info *pnd2_mci;

#define PND2_MSG_SIZE

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

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

#define MOT_CHAN_INTLV_BIT_1SLC_2CH
#define MOT_CHAN_INTLV_BIT_2SLC_2CH
#define SELECTOR_DISABLED

#define PMI_ADDRESS_WIDTH
#define PND_MAX_PHYS_BIT

#define APL_ASYMSHIFT
#define DNV_ASYMSHIFT
#define CH_HASH_MASK_LSB
#define SLICE_HASH_MASK_LSB
#define MOT_SLC_INTLV_BIT
#define LOG2_PMI_ADDR_GRANULARITY
#define MOT_SHIFT

#define GET_BITFIELD(v, lo, hi)
#define U64_LSHIFT(val, s)

/*
 * On Apollo Lake we access memory controller registers via a
 * side-band mailbox style interface in a hidden PCI device
 * configuration space.
 */
static struct pci_bus	*p2sb_bus;
#define P2SB_DEVFN
#define P2SB_ADDR_OFF
#define P2SB_DATA_OFF
#define P2SB_STAT_OFF
#define P2SB_ROUT_OFF
#define P2SB_EADD_OFF
#define P2SB_HIDE_OFF

#define P2SB_BUSY

#define P2SB_READ(size, off, ptr)
#define P2SB_WRITE(size, off, val)

static bool p2sb_is_busy(u16 *status)
{}

static int _apl_rd_reg(int port, int off, int op, u32 *data)
{}

static int apl_rd_reg(int port, int off, int op, void *data, size_t sz, char *name)
{}

static u64 get_mem_ctrl_hub_base_addr(void)
{}

#define DNV_MCHBAR_SIZE
#define DNV_SB_PORT_SIZE
static int dnv_rd_reg(int port, int off, int op, void *data, size_t sz, char *name)
{}

#define RD_REGP(regp, regname, port)

#define RD_REG(regp, regname)

static u64 top_lm, top_hm;
static bool two_slices;
static bool two_channels; /* Both PMI channels in one slice enabled */

static u8 sym_chan_mask;
static u8 asym_chan_mask;
static unsigned long chan_mask;

static int slice_selector =;
static int chan_selector =;
static u64 slice_hash_mask;
static u64 chan_hash_mask;

static void mk_region(char *name, struct region *rp, u64 base, u64 limit)
{}

static void mk_region_mask(char *name, struct region *rp, u64 base, u64 mask)
{}

static bool in_region(struct region *rp, u64 addr)
{}

static int gen_sym_mask(struct b_cr_slice_channel_hash *p)
{}

static int gen_asym_mask(struct b_cr_slice_channel_hash *p,
			 struct b_cr_asym_mem_region0_mchbar *as0,
			 struct b_cr_asym_mem_region1_mchbar *as1,
			 struct b_cr_asym_2way_mem_region_mchbar *as2way)
{}

static struct b_cr_tolud_pci tolud;
static struct b_cr_touud_lo_pci touud_lo;
static struct b_cr_touud_hi_pci touud_hi;
static struct b_cr_asym_mem_region0_mchbar asym0;
static struct b_cr_asym_mem_region1_mchbar asym1;
static struct b_cr_asym_2way_mem_region_mchbar asym_2way;
static struct b_cr_mot_out_base_mchbar mot_base;
static struct b_cr_mot_out_mask_mchbar mot_mask;
static struct b_cr_slice_channel_hash chash;

/* Apollo Lake dunit */
/*
 * Validated on board with just two DIMMs in the [0] and [2] positions
 * in this array. Other port number matches documentation, but caution
 * advised.
 */
static const int apl_dports[APL_NUM_CHANNELS] =;
static struct d_cr_drp0 drp0[APL_NUM_CHANNELS];

/* Denverton dunit */
static const int dnv_dports[DNV_NUM_CHANNELS] =;
static struct d_cr_dsch dsch;
static struct d_cr_ecc_ctrl ecc_ctrl[DNV_NUM_CHANNELS];
static struct d_cr_drp drp[DNV_NUM_CHANNELS];
static struct d_cr_dmap dmap[DNV_NUM_CHANNELS];
static struct d_cr_dmap1 dmap1[DNV_NUM_CHANNELS];
static struct d_cr_dmap2 dmap2[DNV_NUM_CHANNELS];
static struct d_cr_dmap3 dmap3[DNV_NUM_CHANNELS];
static struct d_cr_dmap4 dmap4[DNV_NUM_CHANNELS];
static struct d_cr_dmap5 dmap5[DNV_NUM_CHANNELS];

static void apl_mk_region(char *name, struct region *rp, void *asym)
{}

static void dnv_mk_region(char *name, struct region *rp, void *asym)
{}

static int apl_get_registers(void)
{}

static int dnv_get_registers(void)
{}

/*
 * Read all the h/w config registers once here (they don't
 * change at run time. Figure out which address ranges have
 * which interleave characteristics.
 */
static int get_registers(void)
{}

/* Get a contiguous memory address (remove the MMIO gap) */
static u64 remove_mmio_gap(u64 sys)
{}

/* Squeeze out one address bit, shift upper part down to fill gap */
static void remove_addr_bit(u64 *addr, int bitidx)
{}

/* XOR all the bits from addr specified in mask */
static int hash_by_mask(u64 addr, u64 mask)
{}

/*
 * First stage decode. Take the system address and figure out which
 * second stage will deal with it based on interleave modes.
 */
static int sys2pmi(const u64 addr, u32 *pmiidx, u64 *pmiaddr, char *msg)
{}

/* Translate PMI address to memory (rank, row, bank, column) */
#define C(n)
#define B(n)
#define R(n)
#define RS

/* addrdec values */
#define AMAP_1KB
#define AMAP_2KB
#define AMAP_4KB
#define AMAP_RSVD

/* dden values */
#define DEN_4Gb
#define DEN_8Gb

/* dwid values */
#define X8
#define X16

static struct dimm_geometry {} dimms[] =;

static int bank_hash(u64 pmiaddr, int idx, int shft)
{}

static int rank_hash(u64 pmiaddr)
{}

/* Second stage decode. Compute rank, bank, row & column. */
static int apl_pmi2mem(struct mem_ctl_info *mci, u64 pmiaddr, u32 pmiidx,
		       struct dram_addr *daddr, char *msg)
{}

/* Pluck bit "in" from pmiaddr and return value shifted to bit "out" */
#define dnv_get_bit(pmi, in, out)

static int dnv_pmi2mem(struct mem_ctl_info *mci, u64 pmiaddr, u32 pmiidx,
					   struct dram_addr *daddr, char *msg)
{}

static int check_channel(int ch)
{}

static int apl_check_ecc_active(void)
{}

#define DIMMS_PRESENT(d)

static int check_unit(int ch)
{}

static int dnv_check_ecc_active(void)
{}

static int get_memory_error_data(struct mem_ctl_info *mci, u64 addr,
								 struct dram_addr *daddr, char *msg)
{}

static void pnd2_mce_output_error(struct mem_ctl_info *mci, const struct mce *m,
				  struct dram_addr *daddr)
{}

static void apl_get_dimm_config(struct mem_ctl_info *mci)
{}

static const int dnv_dtypes[] =;

static void dnv_get_dimm_config(struct mem_ctl_info *mci)
{}

static int pnd2_register_mci(struct mem_ctl_info **ppmci)
{}

static void pnd2_unregister_mci(struct mem_ctl_info *mci)
{}

/*
 * Callback function registered with core kernel mce code.
 * Called once for each logged error.
 */
static int pnd2_mce_check_error(struct notifier_block *nb, unsigned long val, void *data)
{}

static struct notifier_block pnd2_mce_dec =;

#ifdef CONFIG_EDAC_DEBUG
/*
 * Write an address to this file to exercise the address decode
 * logic in this driver.
 */
static u64 pnd2_fake_addr;
#define PND2_BLOB_SIZE
static char pnd2_result[PND2_BLOB_SIZE];
static struct dentry *pnd2_test;
static struct debugfs_blob_wrapper pnd2_blob =;

static int debugfs_u64_set(void *data, u64 val)
{}
DEFINE_DEBUGFS_ATTRIBUTE();

static void setup_pnd2_debug(void)
{}

static void teardown_pnd2_debug(void)
{}
#else
static void setup_pnd2_debug(void)	{}
static void teardown_pnd2_debug(void)	{}
#endif /* CONFIG_EDAC_DEBUG */


static int pnd2_probe(void)
{}

static void pnd2_remove(void)
{}

static struct dunit_ops apl_ops =;

static struct dunit_ops dnv_ops =;

static const struct x86_cpu_id pnd2_cpuids[] =;
MODULE_DEVICE_TABLE(x86cpu, pnd2_cpuids);

static int __init pnd2_init(void)
{}

static void __exit pnd2_exit(void)
{}

module_init();
module_exit(pnd2_exit);

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

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