18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Pondicherry2 memory controller. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2016, Intel Corporation. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * [Derived from sb_edac.c] 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Translation of system physical addresses to DIMM addresses 108c2ecf20Sopenharmony_ci * is a two stage process: 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * First the Pondicherry 2 memory controller handles slice and channel interleaving 138c2ecf20Sopenharmony_ci * in "sys2pmi()". This is (almost) completley common between platforms. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Then a platform specific dunit (DIMM unit) completes the process to provide DIMM, 168c2ecf20Sopenharmony_ci * rank, bank, row and column using the appropriate "dunit_ops" functions/parameters. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/init.h> 218c2ecf20Sopenharmony_ci#include <linux/pci.h> 228c2ecf20Sopenharmony_ci#include <linux/pci_ids.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci#include <linux/delay.h> 258c2ecf20Sopenharmony_ci#include <linux/edac.h> 268c2ecf20Sopenharmony_ci#include <linux/mmzone.h> 278c2ecf20Sopenharmony_ci#include <linux/smp.h> 288c2ecf20Sopenharmony_ci#include <linux/bitmap.h> 298c2ecf20Sopenharmony_ci#include <linux/math64.h> 308c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 318c2ecf20Sopenharmony_ci#include <asm/cpu_device_id.h> 328c2ecf20Sopenharmony_ci#include <asm/intel-family.h> 338c2ecf20Sopenharmony_ci#include <asm/processor.h> 348c2ecf20Sopenharmony_ci#include <asm/mce.h> 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#include "edac_mc.h" 378c2ecf20Sopenharmony_ci#include "edac_module.h" 388c2ecf20Sopenharmony_ci#include "pnd2_edac.h" 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define EDAC_MOD_STR "pnd2_edac" 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define APL_NUM_CHANNELS 4 438c2ecf20Sopenharmony_ci#define DNV_NUM_CHANNELS 2 448c2ecf20Sopenharmony_ci#define DNV_MAX_DIMMS 2 /* Max DIMMs per channel */ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cienum type { 478c2ecf20Sopenharmony_ci APL, 488c2ecf20Sopenharmony_ci DNV, /* All requests go to PMI CH0 on each slice (CH1 disabled) */ 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistruct dram_addr { 528c2ecf20Sopenharmony_ci int chan; 538c2ecf20Sopenharmony_ci int dimm; 548c2ecf20Sopenharmony_ci int rank; 558c2ecf20Sopenharmony_ci int bank; 568c2ecf20Sopenharmony_ci int row; 578c2ecf20Sopenharmony_ci int col; 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistruct pnd2_pvt { 618c2ecf20Sopenharmony_ci int dimm_geom[APL_NUM_CHANNELS]; 628c2ecf20Sopenharmony_ci u64 tolm, tohm; 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* 668c2ecf20Sopenharmony_ci * System address space is divided into multiple regions with 678c2ecf20Sopenharmony_ci * different interleave rules in each. The as0/as1 regions 688c2ecf20Sopenharmony_ci * have no interleaving at all. The as2 region is interleaved 698c2ecf20Sopenharmony_ci * between two channels. The mot region is magic and may overlap 708c2ecf20Sopenharmony_ci * other regions, with its interleave rules taking precedence. 718c2ecf20Sopenharmony_ci * Addresses not in any of these regions are interleaved across 728c2ecf20Sopenharmony_ci * all four channels. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_cistatic struct region { 758c2ecf20Sopenharmony_ci u64 base; 768c2ecf20Sopenharmony_ci u64 limit; 778c2ecf20Sopenharmony_ci u8 enabled; 788c2ecf20Sopenharmony_ci} mot, as0, as1, as2; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic struct dunit_ops { 818c2ecf20Sopenharmony_ci char *name; 828c2ecf20Sopenharmony_ci enum type type; 838c2ecf20Sopenharmony_ci int pmiaddr_shift; 848c2ecf20Sopenharmony_ci int pmiidx_shift; 858c2ecf20Sopenharmony_ci int channels; 868c2ecf20Sopenharmony_ci int dimms_per_channel; 878c2ecf20Sopenharmony_ci int (*rd_reg)(int port, int off, int op, void *data, size_t sz, char *name); 888c2ecf20Sopenharmony_ci int (*get_registers)(void); 898c2ecf20Sopenharmony_ci int (*check_ecc)(void); 908c2ecf20Sopenharmony_ci void (*mk_region)(char *name, struct region *rp, void *asym); 918c2ecf20Sopenharmony_ci void (*get_dimm_config)(struct mem_ctl_info *mci); 928c2ecf20Sopenharmony_ci int (*pmi2mem)(struct mem_ctl_info *mci, u64 pmiaddr, u32 pmiidx, 938c2ecf20Sopenharmony_ci struct dram_addr *daddr, char *msg); 948c2ecf20Sopenharmony_ci} *ops; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic struct mem_ctl_info *pnd2_mci; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#define PND2_MSG_SIZE 256 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* Debug macros */ 1018c2ecf20Sopenharmony_ci#define pnd2_printk(level, fmt, arg...) \ 1028c2ecf20Sopenharmony_ci edac_printk(level, "pnd2", fmt, ##arg) 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci#define pnd2_mc_printk(mci, level, fmt, arg...) \ 1058c2ecf20Sopenharmony_ci edac_mc_chipset_printk(mci, level, "pnd2", fmt, ##arg) 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci#define MOT_CHAN_INTLV_BIT_1SLC_2CH 12 1088c2ecf20Sopenharmony_ci#define MOT_CHAN_INTLV_BIT_2SLC_2CH 13 1098c2ecf20Sopenharmony_ci#define SELECTOR_DISABLED (-1) 1108c2ecf20Sopenharmony_ci#define _4GB (1ul << 32) 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci#define PMI_ADDRESS_WIDTH 31 1138c2ecf20Sopenharmony_ci#define PND_MAX_PHYS_BIT 39 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci#define APL_ASYMSHIFT 28 1168c2ecf20Sopenharmony_ci#define DNV_ASYMSHIFT 31 1178c2ecf20Sopenharmony_ci#define CH_HASH_MASK_LSB 6 1188c2ecf20Sopenharmony_ci#define SLICE_HASH_MASK_LSB 6 1198c2ecf20Sopenharmony_ci#define MOT_SLC_INTLV_BIT 12 1208c2ecf20Sopenharmony_ci#define LOG2_PMI_ADDR_GRANULARITY 5 1218c2ecf20Sopenharmony_ci#define MOT_SHIFT 24 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci#define GET_BITFIELD(v, lo, hi) (((v) & GENMASK_ULL(hi, lo)) >> (lo)) 1248c2ecf20Sopenharmony_ci#define U64_LSHIFT(val, s) ((u64)(val) << (s)) 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci/* 1278c2ecf20Sopenharmony_ci * On Apollo Lake we access memory controller registers via a 1288c2ecf20Sopenharmony_ci * side-band mailbox style interface in a hidden PCI device 1298c2ecf20Sopenharmony_ci * configuration space. 1308c2ecf20Sopenharmony_ci */ 1318c2ecf20Sopenharmony_cistatic struct pci_bus *p2sb_bus; 1328c2ecf20Sopenharmony_ci#define P2SB_DEVFN PCI_DEVFN(0xd, 0) 1338c2ecf20Sopenharmony_ci#define P2SB_ADDR_OFF 0xd0 1348c2ecf20Sopenharmony_ci#define P2SB_DATA_OFF 0xd4 1358c2ecf20Sopenharmony_ci#define P2SB_STAT_OFF 0xd8 1368c2ecf20Sopenharmony_ci#define P2SB_ROUT_OFF 0xda 1378c2ecf20Sopenharmony_ci#define P2SB_EADD_OFF 0xdc 1388c2ecf20Sopenharmony_ci#define P2SB_HIDE_OFF 0xe1 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci#define P2SB_BUSY 1 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci#define P2SB_READ(size, off, ptr) \ 1438c2ecf20Sopenharmony_ci pci_bus_read_config_##size(p2sb_bus, P2SB_DEVFN, off, ptr) 1448c2ecf20Sopenharmony_ci#define P2SB_WRITE(size, off, val) \ 1458c2ecf20Sopenharmony_ci pci_bus_write_config_##size(p2sb_bus, P2SB_DEVFN, off, val) 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic bool p2sb_is_busy(u16 *status) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci P2SB_READ(word, P2SB_STAT_OFF, status); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return !!(*status & P2SB_BUSY); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int _apl_rd_reg(int port, int off, int op, u32 *data) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci int retries = 0xff, ret; 1578c2ecf20Sopenharmony_ci u16 status; 1588c2ecf20Sopenharmony_ci u8 hidden; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* Unhide the P2SB device, if it's hidden */ 1618c2ecf20Sopenharmony_ci P2SB_READ(byte, P2SB_HIDE_OFF, &hidden); 1628c2ecf20Sopenharmony_ci if (hidden) 1638c2ecf20Sopenharmony_ci P2SB_WRITE(byte, P2SB_HIDE_OFF, 0); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (p2sb_is_busy(&status)) { 1668c2ecf20Sopenharmony_ci ret = -EAGAIN; 1678c2ecf20Sopenharmony_ci goto out; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci P2SB_WRITE(dword, P2SB_ADDR_OFF, (port << 24) | off); 1718c2ecf20Sopenharmony_ci P2SB_WRITE(dword, P2SB_DATA_OFF, 0); 1728c2ecf20Sopenharmony_ci P2SB_WRITE(dword, P2SB_EADD_OFF, 0); 1738c2ecf20Sopenharmony_ci P2SB_WRITE(word, P2SB_ROUT_OFF, 0); 1748c2ecf20Sopenharmony_ci P2SB_WRITE(word, P2SB_STAT_OFF, (op << 8) | P2SB_BUSY); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci while (p2sb_is_busy(&status)) { 1778c2ecf20Sopenharmony_ci if (retries-- == 0) { 1788c2ecf20Sopenharmony_ci ret = -EBUSY; 1798c2ecf20Sopenharmony_ci goto out; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci P2SB_READ(dword, P2SB_DATA_OFF, data); 1848c2ecf20Sopenharmony_ci ret = (status >> 1) & 0x3; 1858c2ecf20Sopenharmony_ciout: 1868c2ecf20Sopenharmony_ci /* Hide the P2SB device, if it was hidden before */ 1878c2ecf20Sopenharmony_ci if (hidden) 1888c2ecf20Sopenharmony_ci P2SB_WRITE(byte, P2SB_HIDE_OFF, hidden); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return ret; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic int apl_rd_reg(int port, int off, int op, void *data, size_t sz, char *name) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci int ret = 0; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci edac_dbg(2, "Read %s port=%x off=%x op=%x\n", name, port, off, op); 1988c2ecf20Sopenharmony_ci switch (sz) { 1998c2ecf20Sopenharmony_ci case 8: 2008c2ecf20Sopenharmony_ci ret = _apl_rd_reg(port, off + 4, op, (u32 *)(data + 4)); 2018c2ecf20Sopenharmony_ci fallthrough; 2028c2ecf20Sopenharmony_ci case 4: 2038c2ecf20Sopenharmony_ci ret |= _apl_rd_reg(port, off, op, (u32 *)data); 2048c2ecf20Sopenharmony_ci pnd2_printk(KERN_DEBUG, "%s=%x%08x ret=%d\n", name, 2058c2ecf20Sopenharmony_ci sz == 8 ? *((u32 *)(data + 4)) : 0, *((u32 *)data), ret); 2068c2ecf20Sopenharmony_ci break; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return ret; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic u64 get_mem_ctrl_hub_base_addr(void) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct b_cr_mchbar_lo_pci lo; 2158c2ecf20Sopenharmony_ci struct b_cr_mchbar_hi_pci hi; 2168c2ecf20Sopenharmony_ci struct pci_dev *pdev; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x1980, NULL); 2198c2ecf20Sopenharmony_ci if (pdev) { 2208c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, 0x48, (u32 *)&lo); 2218c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, 0x4c, (u32 *)&hi); 2228c2ecf20Sopenharmony_ci pci_dev_put(pdev); 2238c2ecf20Sopenharmony_ci } else { 2248c2ecf20Sopenharmony_ci return 0; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (!lo.enable) { 2288c2ecf20Sopenharmony_ci edac_dbg(2, "MMIO via memory controller hub base address is disabled!\n"); 2298c2ecf20Sopenharmony_ci return 0; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci return U64_LSHIFT(hi.base, 32) | U64_LSHIFT(lo.base, 15); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic u64 get_sideband_reg_base_addr(void) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct pci_dev *pdev; 2388c2ecf20Sopenharmony_ci u32 hi, lo; 2398c2ecf20Sopenharmony_ci u8 hidden; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x19dd, NULL); 2428c2ecf20Sopenharmony_ci if (pdev) { 2438c2ecf20Sopenharmony_ci /* Unhide the P2SB device, if it's hidden */ 2448c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, 0xe1, &hidden); 2458c2ecf20Sopenharmony_ci if (hidden) 2468c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, 0xe1, 0); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, 0x10, &lo); 2498c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, 0x14, &hi); 2508c2ecf20Sopenharmony_ci lo &= 0xfffffff0; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* Hide the P2SB device, if it was hidden before */ 2538c2ecf20Sopenharmony_ci if (hidden) 2548c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, 0xe1, hidden); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci pci_dev_put(pdev); 2578c2ecf20Sopenharmony_ci return (U64_LSHIFT(hi, 32) | U64_LSHIFT(lo, 0)); 2588c2ecf20Sopenharmony_ci } else { 2598c2ecf20Sopenharmony_ci return 0xfd000000; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci#define DNV_MCHBAR_SIZE 0x8000 2648c2ecf20Sopenharmony_ci#define DNV_SB_PORT_SIZE 0x10000 2658c2ecf20Sopenharmony_cistatic int dnv_rd_reg(int port, int off, int op, void *data, size_t sz, char *name) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci struct pci_dev *pdev; 2688c2ecf20Sopenharmony_ci char *base; 2698c2ecf20Sopenharmony_ci u64 addr; 2708c2ecf20Sopenharmony_ci unsigned long size; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (op == 4) { 2738c2ecf20Sopenharmony_ci pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x1980, NULL); 2748c2ecf20Sopenharmony_ci if (!pdev) 2758c2ecf20Sopenharmony_ci return -ENODEV; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, off, data); 2788c2ecf20Sopenharmony_ci pci_dev_put(pdev); 2798c2ecf20Sopenharmony_ci } else { 2808c2ecf20Sopenharmony_ci /* MMIO via memory controller hub base address */ 2818c2ecf20Sopenharmony_ci if (op == 0 && port == 0x4c) { 2828c2ecf20Sopenharmony_ci addr = get_mem_ctrl_hub_base_addr(); 2838c2ecf20Sopenharmony_ci if (!addr) 2848c2ecf20Sopenharmony_ci return -ENODEV; 2858c2ecf20Sopenharmony_ci size = DNV_MCHBAR_SIZE; 2868c2ecf20Sopenharmony_ci } else { 2878c2ecf20Sopenharmony_ci /* MMIO via sideband register base address */ 2888c2ecf20Sopenharmony_ci addr = get_sideband_reg_base_addr(); 2898c2ecf20Sopenharmony_ci if (!addr) 2908c2ecf20Sopenharmony_ci return -ENODEV; 2918c2ecf20Sopenharmony_ci addr += (port << 16); 2928c2ecf20Sopenharmony_ci size = DNV_SB_PORT_SIZE; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci base = ioremap((resource_size_t)addr, size); 2968c2ecf20Sopenharmony_ci if (!base) 2978c2ecf20Sopenharmony_ci return -ENODEV; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (sz == 8) 3008c2ecf20Sopenharmony_ci *(u32 *)(data + 4) = *(u32 *)(base + off + 4); 3018c2ecf20Sopenharmony_ci *(u32 *)data = *(u32 *)(base + off); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci iounmap(base); 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci edac_dbg(2, "Read %s=%.8x_%.8x\n", name, 3078c2ecf20Sopenharmony_ci (sz == 8) ? *(u32 *)(data + 4) : 0, *(u32 *)data); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci return 0; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci#define RD_REGP(regp, regname, port) \ 3138c2ecf20Sopenharmony_ci ops->rd_reg(port, \ 3148c2ecf20Sopenharmony_ci regname##_offset, \ 3158c2ecf20Sopenharmony_ci regname##_r_opcode, \ 3168c2ecf20Sopenharmony_ci regp, sizeof(struct regname), \ 3178c2ecf20Sopenharmony_ci #regname) 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci#define RD_REG(regp, regname) \ 3208c2ecf20Sopenharmony_ci ops->rd_reg(regname ## _port, \ 3218c2ecf20Sopenharmony_ci regname##_offset, \ 3228c2ecf20Sopenharmony_ci regname##_r_opcode, \ 3238c2ecf20Sopenharmony_ci regp, sizeof(struct regname), \ 3248c2ecf20Sopenharmony_ci #regname) 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic u64 top_lm, top_hm; 3278c2ecf20Sopenharmony_cistatic bool two_slices; 3288c2ecf20Sopenharmony_cistatic bool two_channels; /* Both PMI channels in one slice enabled */ 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic u8 sym_chan_mask; 3318c2ecf20Sopenharmony_cistatic u8 asym_chan_mask; 3328c2ecf20Sopenharmony_cistatic u8 chan_mask; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic int slice_selector = -1; 3358c2ecf20Sopenharmony_cistatic int chan_selector = -1; 3368c2ecf20Sopenharmony_cistatic u64 slice_hash_mask; 3378c2ecf20Sopenharmony_cistatic u64 chan_hash_mask; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic void mk_region(char *name, struct region *rp, u64 base, u64 limit) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci rp->enabled = 1; 3428c2ecf20Sopenharmony_ci rp->base = base; 3438c2ecf20Sopenharmony_ci rp->limit = limit; 3448c2ecf20Sopenharmony_ci edac_dbg(2, "Region:%s [%llx, %llx]\n", name, base, limit); 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic void mk_region_mask(char *name, struct region *rp, u64 base, u64 mask) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci if (mask == 0) { 3508c2ecf20Sopenharmony_ci pr_info(FW_BUG "MOT mask cannot be zero\n"); 3518c2ecf20Sopenharmony_ci return; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci if (mask != GENMASK_ULL(PND_MAX_PHYS_BIT, __ffs(mask))) { 3548c2ecf20Sopenharmony_ci pr_info(FW_BUG "MOT mask not power of two\n"); 3558c2ecf20Sopenharmony_ci return; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci if (base & ~mask) { 3588c2ecf20Sopenharmony_ci pr_info(FW_BUG "MOT region base/mask alignment error\n"); 3598c2ecf20Sopenharmony_ci return; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci rp->base = base; 3628c2ecf20Sopenharmony_ci rp->limit = (base | ~mask) & GENMASK_ULL(PND_MAX_PHYS_BIT, 0); 3638c2ecf20Sopenharmony_ci rp->enabled = 1; 3648c2ecf20Sopenharmony_ci edac_dbg(2, "Region:%s [%llx, %llx]\n", name, base, rp->limit); 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic bool in_region(struct region *rp, u64 addr) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci if (!rp->enabled) 3708c2ecf20Sopenharmony_ci return false; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci return rp->base <= addr && addr <= rp->limit; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic int gen_sym_mask(struct b_cr_slice_channel_hash *p) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci int mask = 0; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (!p->slice_0_mem_disabled) 3808c2ecf20Sopenharmony_ci mask |= p->sym_slice0_channel_enabled; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (!p->slice_1_disabled) 3838c2ecf20Sopenharmony_ci mask |= p->sym_slice1_channel_enabled << 2; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (p->ch_1_disabled || p->enable_pmi_dual_data_mode) 3868c2ecf20Sopenharmony_ci mask &= 0x5; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci return mask; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic int gen_asym_mask(struct b_cr_slice_channel_hash *p, 3928c2ecf20Sopenharmony_ci struct b_cr_asym_mem_region0_mchbar *as0, 3938c2ecf20Sopenharmony_ci struct b_cr_asym_mem_region1_mchbar *as1, 3948c2ecf20Sopenharmony_ci struct b_cr_asym_2way_mem_region_mchbar *as2way) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci const int intlv[] = { 0x5, 0xA, 0x3, 0xC }; 3978c2ecf20Sopenharmony_ci int mask = 0; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (as2way->asym_2way_interleave_enable) 4008c2ecf20Sopenharmony_ci mask = intlv[as2way->asym_2way_intlv_mode]; 4018c2ecf20Sopenharmony_ci if (as0->slice0_asym_enable) 4028c2ecf20Sopenharmony_ci mask |= (1 << as0->slice0_asym_channel_select); 4038c2ecf20Sopenharmony_ci if (as1->slice1_asym_enable) 4048c2ecf20Sopenharmony_ci mask |= (4 << as1->slice1_asym_channel_select); 4058c2ecf20Sopenharmony_ci if (p->slice_0_mem_disabled) 4068c2ecf20Sopenharmony_ci mask &= 0xc; 4078c2ecf20Sopenharmony_ci if (p->slice_1_disabled) 4088c2ecf20Sopenharmony_ci mask &= 0x3; 4098c2ecf20Sopenharmony_ci if (p->ch_1_disabled || p->enable_pmi_dual_data_mode) 4108c2ecf20Sopenharmony_ci mask &= 0x5; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci return mask; 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic struct b_cr_tolud_pci tolud; 4168c2ecf20Sopenharmony_cistatic struct b_cr_touud_lo_pci touud_lo; 4178c2ecf20Sopenharmony_cistatic struct b_cr_touud_hi_pci touud_hi; 4188c2ecf20Sopenharmony_cistatic struct b_cr_asym_mem_region0_mchbar asym0; 4198c2ecf20Sopenharmony_cistatic struct b_cr_asym_mem_region1_mchbar asym1; 4208c2ecf20Sopenharmony_cistatic struct b_cr_asym_2way_mem_region_mchbar asym_2way; 4218c2ecf20Sopenharmony_cistatic struct b_cr_mot_out_base_mchbar mot_base; 4228c2ecf20Sopenharmony_cistatic struct b_cr_mot_out_mask_mchbar mot_mask; 4238c2ecf20Sopenharmony_cistatic struct b_cr_slice_channel_hash chash; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci/* Apollo Lake dunit */ 4268c2ecf20Sopenharmony_ci/* 4278c2ecf20Sopenharmony_ci * Validated on board with just two DIMMs in the [0] and [2] positions 4288c2ecf20Sopenharmony_ci * in this array. Other port number matches documentation, but caution 4298c2ecf20Sopenharmony_ci * advised. 4308c2ecf20Sopenharmony_ci */ 4318c2ecf20Sopenharmony_cistatic const int apl_dports[APL_NUM_CHANNELS] = { 0x18, 0x10, 0x11, 0x19 }; 4328c2ecf20Sopenharmony_cistatic struct d_cr_drp0 drp0[APL_NUM_CHANNELS]; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci/* Denverton dunit */ 4358c2ecf20Sopenharmony_cistatic const int dnv_dports[DNV_NUM_CHANNELS] = { 0x10, 0x12 }; 4368c2ecf20Sopenharmony_cistatic struct d_cr_dsch dsch; 4378c2ecf20Sopenharmony_cistatic struct d_cr_ecc_ctrl ecc_ctrl[DNV_NUM_CHANNELS]; 4388c2ecf20Sopenharmony_cistatic struct d_cr_drp drp[DNV_NUM_CHANNELS]; 4398c2ecf20Sopenharmony_cistatic struct d_cr_dmap dmap[DNV_NUM_CHANNELS]; 4408c2ecf20Sopenharmony_cistatic struct d_cr_dmap1 dmap1[DNV_NUM_CHANNELS]; 4418c2ecf20Sopenharmony_cistatic struct d_cr_dmap2 dmap2[DNV_NUM_CHANNELS]; 4428c2ecf20Sopenharmony_cistatic struct d_cr_dmap3 dmap3[DNV_NUM_CHANNELS]; 4438c2ecf20Sopenharmony_cistatic struct d_cr_dmap4 dmap4[DNV_NUM_CHANNELS]; 4448c2ecf20Sopenharmony_cistatic struct d_cr_dmap5 dmap5[DNV_NUM_CHANNELS]; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic void apl_mk_region(char *name, struct region *rp, void *asym) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci struct b_cr_asym_mem_region0_mchbar *a = asym; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci mk_region(name, rp, 4518c2ecf20Sopenharmony_ci U64_LSHIFT(a->slice0_asym_base, APL_ASYMSHIFT), 4528c2ecf20Sopenharmony_ci U64_LSHIFT(a->slice0_asym_limit, APL_ASYMSHIFT) + 4538c2ecf20Sopenharmony_ci GENMASK_ULL(APL_ASYMSHIFT - 1, 0)); 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic void dnv_mk_region(char *name, struct region *rp, void *asym) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci struct b_cr_asym_mem_region_denverton *a = asym; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci mk_region(name, rp, 4618c2ecf20Sopenharmony_ci U64_LSHIFT(a->slice_asym_base, DNV_ASYMSHIFT), 4628c2ecf20Sopenharmony_ci U64_LSHIFT(a->slice_asym_limit, DNV_ASYMSHIFT) + 4638c2ecf20Sopenharmony_ci GENMASK_ULL(DNV_ASYMSHIFT - 1, 0)); 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic int apl_get_registers(void) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci int ret = -ENODEV; 4698c2ecf20Sopenharmony_ci int i; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (RD_REG(&asym_2way, b_cr_asym_2way_mem_region_mchbar)) 4728c2ecf20Sopenharmony_ci return -ENODEV; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci /* 4758c2ecf20Sopenharmony_ci * RD_REGP() will fail for unpopulated or non-existent 4768c2ecf20Sopenharmony_ci * DIMM slots. Return success if we find at least one DIMM. 4778c2ecf20Sopenharmony_ci */ 4788c2ecf20Sopenharmony_ci for (i = 0; i < APL_NUM_CHANNELS; i++) 4798c2ecf20Sopenharmony_ci if (!RD_REGP(&drp0[i], d_cr_drp0, apl_dports[i])) 4808c2ecf20Sopenharmony_ci ret = 0; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci return ret; 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic int dnv_get_registers(void) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci int i; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (RD_REG(&dsch, d_cr_dsch)) 4908c2ecf20Sopenharmony_ci return -ENODEV; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci for (i = 0; i < DNV_NUM_CHANNELS; i++) 4938c2ecf20Sopenharmony_ci if (RD_REGP(&ecc_ctrl[i], d_cr_ecc_ctrl, dnv_dports[i]) || 4948c2ecf20Sopenharmony_ci RD_REGP(&drp[i], d_cr_drp, dnv_dports[i]) || 4958c2ecf20Sopenharmony_ci RD_REGP(&dmap[i], d_cr_dmap, dnv_dports[i]) || 4968c2ecf20Sopenharmony_ci RD_REGP(&dmap1[i], d_cr_dmap1, dnv_dports[i]) || 4978c2ecf20Sopenharmony_ci RD_REGP(&dmap2[i], d_cr_dmap2, dnv_dports[i]) || 4988c2ecf20Sopenharmony_ci RD_REGP(&dmap3[i], d_cr_dmap3, dnv_dports[i]) || 4998c2ecf20Sopenharmony_ci RD_REGP(&dmap4[i], d_cr_dmap4, dnv_dports[i]) || 5008c2ecf20Sopenharmony_ci RD_REGP(&dmap5[i], d_cr_dmap5, dnv_dports[i])) 5018c2ecf20Sopenharmony_ci return -ENODEV; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci return 0; 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci/* 5078c2ecf20Sopenharmony_ci * Read all the h/w config registers once here (they don't 5088c2ecf20Sopenharmony_ci * change at run time. Figure out which address ranges have 5098c2ecf20Sopenharmony_ci * which interleave characteristics. 5108c2ecf20Sopenharmony_ci */ 5118c2ecf20Sopenharmony_cistatic int get_registers(void) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci const int intlv[] = { 10, 11, 12, 12 }; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci if (RD_REG(&tolud, b_cr_tolud_pci) || 5168c2ecf20Sopenharmony_ci RD_REG(&touud_lo, b_cr_touud_lo_pci) || 5178c2ecf20Sopenharmony_ci RD_REG(&touud_hi, b_cr_touud_hi_pci) || 5188c2ecf20Sopenharmony_ci RD_REG(&asym0, b_cr_asym_mem_region0_mchbar) || 5198c2ecf20Sopenharmony_ci RD_REG(&asym1, b_cr_asym_mem_region1_mchbar) || 5208c2ecf20Sopenharmony_ci RD_REG(&mot_base, b_cr_mot_out_base_mchbar) || 5218c2ecf20Sopenharmony_ci RD_REG(&mot_mask, b_cr_mot_out_mask_mchbar) || 5228c2ecf20Sopenharmony_ci RD_REG(&chash, b_cr_slice_channel_hash)) 5238c2ecf20Sopenharmony_ci return -ENODEV; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (ops->get_registers()) 5268c2ecf20Sopenharmony_ci return -ENODEV; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (ops->type == DNV) { 5298c2ecf20Sopenharmony_ci /* PMI channel idx (always 0) for asymmetric region */ 5308c2ecf20Sopenharmony_ci asym0.slice0_asym_channel_select = 0; 5318c2ecf20Sopenharmony_ci asym1.slice1_asym_channel_select = 0; 5328c2ecf20Sopenharmony_ci /* PMI channel bitmap (always 1) for symmetric region */ 5338c2ecf20Sopenharmony_ci chash.sym_slice0_channel_enabled = 0x1; 5348c2ecf20Sopenharmony_ci chash.sym_slice1_channel_enabled = 0x1; 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if (asym0.slice0_asym_enable) 5388c2ecf20Sopenharmony_ci ops->mk_region("as0", &as0, &asym0); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (asym1.slice1_asym_enable) 5418c2ecf20Sopenharmony_ci ops->mk_region("as1", &as1, &asym1); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (asym_2way.asym_2way_interleave_enable) { 5448c2ecf20Sopenharmony_ci mk_region("as2way", &as2, 5458c2ecf20Sopenharmony_ci U64_LSHIFT(asym_2way.asym_2way_base, APL_ASYMSHIFT), 5468c2ecf20Sopenharmony_ci U64_LSHIFT(asym_2way.asym_2way_limit, APL_ASYMSHIFT) + 5478c2ecf20Sopenharmony_ci GENMASK_ULL(APL_ASYMSHIFT - 1, 0)); 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci if (mot_base.imr_en) { 5518c2ecf20Sopenharmony_ci mk_region_mask("mot", &mot, 5528c2ecf20Sopenharmony_ci U64_LSHIFT(mot_base.mot_out_base, MOT_SHIFT), 5538c2ecf20Sopenharmony_ci U64_LSHIFT(mot_mask.mot_out_mask, MOT_SHIFT)); 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci top_lm = U64_LSHIFT(tolud.tolud, 20); 5578c2ecf20Sopenharmony_ci top_hm = U64_LSHIFT(touud_hi.touud, 32) | U64_LSHIFT(touud_lo.touud, 20); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci two_slices = !chash.slice_1_disabled && 5608c2ecf20Sopenharmony_ci !chash.slice_0_mem_disabled && 5618c2ecf20Sopenharmony_ci (chash.sym_slice0_channel_enabled != 0) && 5628c2ecf20Sopenharmony_ci (chash.sym_slice1_channel_enabled != 0); 5638c2ecf20Sopenharmony_ci two_channels = !chash.ch_1_disabled && 5648c2ecf20Sopenharmony_ci !chash.enable_pmi_dual_data_mode && 5658c2ecf20Sopenharmony_ci ((chash.sym_slice0_channel_enabled == 3) || 5668c2ecf20Sopenharmony_ci (chash.sym_slice1_channel_enabled == 3)); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci sym_chan_mask = gen_sym_mask(&chash); 5698c2ecf20Sopenharmony_ci asym_chan_mask = gen_asym_mask(&chash, &asym0, &asym1, &asym_2way); 5708c2ecf20Sopenharmony_ci chan_mask = sym_chan_mask | asym_chan_mask; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci if (two_slices && !two_channels) { 5738c2ecf20Sopenharmony_ci if (chash.hvm_mode) 5748c2ecf20Sopenharmony_ci slice_selector = 29; 5758c2ecf20Sopenharmony_ci else 5768c2ecf20Sopenharmony_ci slice_selector = intlv[chash.interleave_mode]; 5778c2ecf20Sopenharmony_ci } else if (!two_slices && two_channels) { 5788c2ecf20Sopenharmony_ci if (chash.hvm_mode) 5798c2ecf20Sopenharmony_ci chan_selector = 29; 5808c2ecf20Sopenharmony_ci else 5818c2ecf20Sopenharmony_ci chan_selector = intlv[chash.interleave_mode]; 5828c2ecf20Sopenharmony_ci } else if (two_slices && two_channels) { 5838c2ecf20Sopenharmony_ci if (chash.hvm_mode) { 5848c2ecf20Sopenharmony_ci slice_selector = 29; 5858c2ecf20Sopenharmony_ci chan_selector = 30; 5868c2ecf20Sopenharmony_ci } else { 5878c2ecf20Sopenharmony_ci slice_selector = intlv[chash.interleave_mode]; 5888c2ecf20Sopenharmony_ci chan_selector = intlv[chash.interleave_mode] + 1; 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci if (two_slices) { 5938c2ecf20Sopenharmony_ci if (!chash.hvm_mode) 5948c2ecf20Sopenharmony_ci slice_hash_mask = chash.slice_hash_mask << SLICE_HASH_MASK_LSB; 5958c2ecf20Sopenharmony_ci if (!two_channels) 5968c2ecf20Sopenharmony_ci slice_hash_mask |= BIT_ULL(slice_selector); 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if (two_channels) { 6008c2ecf20Sopenharmony_ci if (!chash.hvm_mode) 6018c2ecf20Sopenharmony_ci chan_hash_mask = chash.ch_hash_mask << CH_HASH_MASK_LSB; 6028c2ecf20Sopenharmony_ci if (!two_slices) 6038c2ecf20Sopenharmony_ci chan_hash_mask |= BIT_ULL(chan_selector); 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci return 0; 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci/* Get a contiguous memory address (remove the MMIO gap) */ 6108c2ecf20Sopenharmony_cistatic u64 remove_mmio_gap(u64 sys) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci return (sys < _4GB) ? sys : sys - (_4GB - top_lm); 6138c2ecf20Sopenharmony_ci} 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci/* Squeeze out one address bit, shift upper part down to fill gap */ 6168c2ecf20Sopenharmony_cistatic void remove_addr_bit(u64 *addr, int bitidx) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci u64 mask; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci if (bitidx == -1) 6218c2ecf20Sopenharmony_ci return; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci mask = (1ull << bitidx) - 1; 6248c2ecf20Sopenharmony_ci *addr = ((*addr >> 1) & ~mask) | (*addr & mask); 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci/* XOR all the bits from addr specified in mask */ 6288c2ecf20Sopenharmony_cistatic int hash_by_mask(u64 addr, u64 mask) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci u64 result = addr & mask; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci result = (result >> 32) ^ result; 6338c2ecf20Sopenharmony_ci result = (result >> 16) ^ result; 6348c2ecf20Sopenharmony_ci result = (result >> 8) ^ result; 6358c2ecf20Sopenharmony_ci result = (result >> 4) ^ result; 6368c2ecf20Sopenharmony_ci result = (result >> 2) ^ result; 6378c2ecf20Sopenharmony_ci result = (result >> 1) ^ result; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci return (int)result & 1; 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci/* 6438c2ecf20Sopenharmony_ci * First stage decode. Take the system address and figure out which 6448c2ecf20Sopenharmony_ci * second stage will deal with it based on interleave modes. 6458c2ecf20Sopenharmony_ci */ 6468c2ecf20Sopenharmony_cistatic int sys2pmi(const u64 addr, u32 *pmiidx, u64 *pmiaddr, char *msg) 6478c2ecf20Sopenharmony_ci{ 6488c2ecf20Sopenharmony_ci u64 contig_addr, contig_base, contig_offset, contig_base_adj; 6498c2ecf20Sopenharmony_ci int mot_intlv_bit = two_slices ? MOT_CHAN_INTLV_BIT_2SLC_2CH : 6508c2ecf20Sopenharmony_ci MOT_CHAN_INTLV_BIT_1SLC_2CH; 6518c2ecf20Sopenharmony_ci int slice_intlv_bit_rm = SELECTOR_DISABLED; 6528c2ecf20Sopenharmony_ci int chan_intlv_bit_rm = SELECTOR_DISABLED; 6538c2ecf20Sopenharmony_ci /* Determine if address is in the MOT region. */ 6548c2ecf20Sopenharmony_ci bool mot_hit = in_region(&mot, addr); 6558c2ecf20Sopenharmony_ci /* Calculate the number of symmetric regions enabled. */ 6568c2ecf20Sopenharmony_ci int sym_channels = hweight8(sym_chan_mask); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci /* 6598c2ecf20Sopenharmony_ci * The amount we need to shift the asym base can be determined by the 6608c2ecf20Sopenharmony_ci * number of enabled symmetric channels. 6618c2ecf20Sopenharmony_ci * NOTE: This can only work because symmetric memory is not supposed 6628c2ecf20Sopenharmony_ci * to do a 3-way interleave. 6638c2ecf20Sopenharmony_ci */ 6648c2ecf20Sopenharmony_ci int sym_chan_shift = sym_channels >> 1; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci /* Give up if address is out of range, or in MMIO gap */ 6678c2ecf20Sopenharmony_ci if (addr >= (1ul << PND_MAX_PHYS_BIT) || 6688c2ecf20Sopenharmony_ci (addr >= top_lm && addr < _4GB) || addr >= top_hm) { 6698c2ecf20Sopenharmony_ci snprintf(msg, PND2_MSG_SIZE, "Error address 0x%llx is not DRAM", addr); 6708c2ecf20Sopenharmony_ci return -EINVAL; 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci /* Get a contiguous memory address (remove the MMIO gap) */ 6748c2ecf20Sopenharmony_ci contig_addr = remove_mmio_gap(addr); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci if (in_region(&as0, addr)) { 6778c2ecf20Sopenharmony_ci *pmiidx = asym0.slice0_asym_channel_select; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci contig_base = remove_mmio_gap(as0.base); 6808c2ecf20Sopenharmony_ci contig_offset = contig_addr - contig_base; 6818c2ecf20Sopenharmony_ci contig_base_adj = (contig_base >> sym_chan_shift) * 6828c2ecf20Sopenharmony_ci ((chash.sym_slice0_channel_enabled >> (*pmiidx & 1)) & 1); 6838c2ecf20Sopenharmony_ci contig_addr = contig_offset + ((sym_channels > 0) ? contig_base_adj : 0ull); 6848c2ecf20Sopenharmony_ci } else if (in_region(&as1, addr)) { 6858c2ecf20Sopenharmony_ci *pmiidx = 2u + asym1.slice1_asym_channel_select; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci contig_base = remove_mmio_gap(as1.base); 6888c2ecf20Sopenharmony_ci contig_offset = contig_addr - contig_base; 6898c2ecf20Sopenharmony_ci contig_base_adj = (contig_base >> sym_chan_shift) * 6908c2ecf20Sopenharmony_ci ((chash.sym_slice1_channel_enabled >> (*pmiidx & 1)) & 1); 6918c2ecf20Sopenharmony_ci contig_addr = contig_offset + ((sym_channels > 0) ? contig_base_adj : 0ull); 6928c2ecf20Sopenharmony_ci } else if (in_region(&as2, addr) && (asym_2way.asym_2way_intlv_mode == 0x3ul)) { 6938c2ecf20Sopenharmony_ci bool channel1; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci mot_intlv_bit = MOT_CHAN_INTLV_BIT_1SLC_2CH; 6968c2ecf20Sopenharmony_ci *pmiidx = (asym_2way.asym_2way_intlv_mode & 1) << 1; 6978c2ecf20Sopenharmony_ci channel1 = mot_hit ? ((bool)((addr >> mot_intlv_bit) & 1)) : 6988c2ecf20Sopenharmony_ci hash_by_mask(contig_addr, chan_hash_mask); 6998c2ecf20Sopenharmony_ci *pmiidx |= (u32)channel1; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci contig_base = remove_mmio_gap(as2.base); 7028c2ecf20Sopenharmony_ci chan_intlv_bit_rm = mot_hit ? mot_intlv_bit : chan_selector; 7038c2ecf20Sopenharmony_ci contig_offset = contig_addr - contig_base; 7048c2ecf20Sopenharmony_ci remove_addr_bit(&contig_offset, chan_intlv_bit_rm); 7058c2ecf20Sopenharmony_ci contig_addr = (contig_base >> sym_chan_shift) + contig_offset; 7068c2ecf20Sopenharmony_ci } else { 7078c2ecf20Sopenharmony_ci /* Otherwise we're in normal, boring symmetric mode. */ 7088c2ecf20Sopenharmony_ci *pmiidx = 0u; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci if (two_slices) { 7118c2ecf20Sopenharmony_ci bool slice1; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci if (mot_hit) { 7148c2ecf20Sopenharmony_ci slice_intlv_bit_rm = MOT_SLC_INTLV_BIT; 7158c2ecf20Sopenharmony_ci slice1 = (addr >> MOT_SLC_INTLV_BIT) & 1; 7168c2ecf20Sopenharmony_ci } else { 7178c2ecf20Sopenharmony_ci slice_intlv_bit_rm = slice_selector; 7188c2ecf20Sopenharmony_ci slice1 = hash_by_mask(addr, slice_hash_mask); 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci *pmiidx = (u32)slice1 << 1; 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci if (two_channels) { 7258c2ecf20Sopenharmony_ci bool channel1; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci mot_intlv_bit = two_slices ? MOT_CHAN_INTLV_BIT_2SLC_2CH : 7288c2ecf20Sopenharmony_ci MOT_CHAN_INTLV_BIT_1SLC_2CH; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci if (mot_hit) { 7318c2ecf20Sopenharmony_ci chan_intlv_bit_rm = mot_intlv_bit; 7328c2ecf20Sopenharmony_ci channel1 = (addr >> mot_intlv_bit) & 1; 7338c2ecf20Sopenharmony_ci } else { 7348c2ecf20Sopenharmony_ci chan_intlv_bit_rm = chan_selector; 7358c2ecf20Sopenharmony_ci channel1 = hash_by_mask(contig_addr, chan_hash_mask); 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci *pmiidx |= (u32)channel1; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci /* Remove the chan_selector bit first */ 7438c2ecf20Sopenharmony_ci remove_addr_bit(&contig_addr, chan_intlv_bit_rm); 7448c2ecf20Sopenharmony_ci /* Remove the slice bit (we remove it second because it must be lower */ 7458c2ecf20Sopenharmony_ci remove_addr_bit(&contig_addr, slice_intlv_bit_rm); 7468c2ecf20Sopenharmony_ci *pmiaddr = contig_addr; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci return 0; 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci/* Translate PMI address to memory (rank, row, bank, column) */ 7528c2ecf20Sopenharmony_ci#define C(n) (0x10 | (n)) /* column */ 7538c2ecf20Sopenharmony_ci#define B(n) (0x20 | (n)) /* bank */ 7548c2ecf20Sopenharmony_ci#define R(n) (0x40 | (n)) /* row */ 7558c2ecf20Sopenharmony_ci#define RS (0x80) /* rank */ 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci/* addrdec values */ 7588c2ecf20Sopenharmony_ci#define AMAP_1KB 0 7598c2ecf20Sopenharmony_ci#define AMAP_2KB 1 7608c2ecf20Sopenharmony_ci#define AMAP_4KB 2 7618c2ecf20Sopenharmony_ci#define AMAP_RSVD 3 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci/* dden values */ 7648c2ecf20Sopenharmony_ci#define DEN_4Gb 0 7658c2ecf20Sopenharmony_ci#define DEN_8Gb 2 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci/* dwid values */ 7688c2ecf20Sopenharmony_ci#define X8 0 7698c2ecf20Sopenharmony_ci#define X16 1 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_cistatic struct dimm_geometry { 7728c2ecf20Sopenharmony_ci u8 addrdec; 7738c2ecf20Sopenharmony_ci u8 dden; 7748c2ecf20Sopenharmony_ci u8 dwid; 7758c2ecf20Sopenharmony_ci u8 rowbits, colbits; 7768c2ecf20Sopenharmony_ci u16 bits[PMI_ADDRESS_WIDTH]; 7778c2ecf20Sopenharmony_ci} dimms[] = { 7788c2ecf20Sopenharmony_ci { 7798c2ecf20Sopenharmony_ci .addrdec = AMAP_1KB, .dden = DEN_4Gb, .dwid = X16, 7808c2ecf20Sopenharmony_ci .rowbits = 15, .colbits = 10, 7818c2ecf20Sopenharmony_ci .bits = { 7828c2ecf20Sopenharmony_ci C(2), C(3), C(4), C(5), C(6), B(0), B(1), B(2), R(0), 7838c2ecf20Sopenharmony_ci R(1), R(2), R(3), R(4), R(5), R(6), R(7), R(8), R(9), 7848c2ecf20Sopenharmony_ci R(10), C(7), C(8), C(9), R(11), RS, R(12), R(13), R(14), 7858c2ecf20Sopenharmony_ci 0, 0, 0, 0 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci }, 7888c2ecf20Sopenharmony_ci { 7898c2ecf20Sopenharmony_ci .addrdec = AMAP_1KB, .dden = DEN_4Gb, .dwid = X8, 7908c2ecf20Sopenharmony_ci .rowbits = 16, .colbits = 10, 7918c2ecf20Sopenharmony_ci .bits = { 7928c2ecf20Sopenharmony_ci C(2), C(3), C(4), C(5), C(6), B(0), B(1), B(2), R(0), 7938c2ecf20Sopenharmony_ci R(1), R(2), R(3), R(4), R(5), R(6), R(7), R(8), R(9), 7948c2ecf20Sopenharmony_ci R(10), C(7), C(8), C(9), R(11), RS, R(12), R(13), R(14), 7958c2ecf20Sopenharmony_ci R(15), 0, 0, 0 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci }, 7988c2ecf20Sopenharmony_ci { 7998c2ecf20Sopenharmony_ci .addrdec = AMAP_1KB, .dden = DEN_8Gb, .dwid = X16, 8008c2ecf20Sopenharmony_ci .rowbits = 16, .colbits = 10, 8018c2ecf20Sopenharmony_ci .bits = { 8028c2ecf20Sopenharmony_ci C(2), C(3), C(4), C(5), C(6), B(0), B(1), B(2), R(0), 8038c2ecf20Sopenharmony_ci R(1), R(2), R(3), R(4), R(5), R(6), R(7), R(8), R(9), 8048c2ecf20Sopenharmony_ci R(10), C(7), C(8), C(9), R(11), RS, R(12), R(13), R(14), 8058c2ecf20Sopenharmony_ci R(15), 0, 0, 0 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci }, 8088c2ecf20Sopenharmony_ci { 8098c2ecf20Sopenharmony_ci .addrdec = AMAP_1KB, .dden = DEN_8Gb, .dwid = X8, 8108c2ecf20Sopenharmony_ci .rowbits = 16, .colbits = 11, 8118c2ecf20Sopenharmony_ci .bits = { 8128c2ecf20Sopenharmony_ci C(2), C(3), C(4), C(5), C(6), B(0), B(1), B(2), R(0), 8138c2ecf20Sopenharmony_ci R(1), R(2), R(3), R(4), R(5), R(6), R(7), R(8), R(9), 8148c2ecf20Sopenharmony_ci R(10), C(7), C(8), C(9), R(11), RS, C(11), R(12), R(13), 8158c2ecf20Sopenharmony_ci R(14), R(15), 0, 0 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci }, 8188c2ecf20Sopenharmony_ci { 8198c2ecf20Sopenharmony_ci .addrdec = AMAP_2KB, .dden = DEN_4Gb, .dwid = X16, 8208c2ecf20Sopenharmony_ci .rowbits = 15, .colbits = 10, 8218c2ecf20Sopenharmony_ci .bits = { 8228c2ecf20Sopenharmony_ci C(2), C(3), C(4), C(5), C(6), C(7), B(0), B(1), B(2), 8238c2ecf20Sopenharmony_ci R(0), R(1), R(2), R(3), R(4), R(5), R(6), R(7), R(8), 8248c2ecf20Sopenharmony_ci R(9), R(10), C(8), C(9), R(11), RS, R(12), R(13), R(14), 8258c2ecf20Sopenharmony_ci 0, 0, 0, 0 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci }, 8288c2ecf20Sopenharmony_ci { 8298c2ecf20Sopenharmony_ci .addrdec = AMAP_2KB, .dden = DEN_4Gb, .dwid = X8, 8308c2ecf20Sopenharmony_ci .rowbits = 16, .colbits = 10, 8318c2ecf20Sopenharmony_ci .bits = { 8328c2ecf20Sopenharmony_ci C(2), C(3), C(4), C(5), C(6), C(7), B(0), B(1), B(2), 8338c2ecf20Sopenharmony_ci R(0), R(1), R(2), R(3), R(4), R(5), R(6), R(7), R(8), 8348c2ecf20Sopenharmony_ci R(9), R(10), C(8), C(9), R(11), RS, R(12), R(13), R(14), 8358c2ecf20Sopenharmony_ci R(15), 0, 0, 0 8368c2ecf20Sopenharmony_ci } 8378c2ecf20Sopenharmony_ci }, 8388c2ecf20Sopenharmony_ci { 8398c2ecf20Sopenharmony_ci .addrdec = AMAP_2KB, .dden = DEN_8Gb, .dwid = X16, 8408c2ecf20Sopenharmony_ci .rowbits = 16, .colbits = 10, 8418c2ecf20Sopenharmony_ci .bits = { 8428c2ecf20Sopenharmony_ci C(2), C(3), C(4), C(5), C(6), C(7), B(0), B(1), B(2), 8438c2ecf20Sopenharmony_ci R(0), R(1), R(2), R(3), R(4), R(5), R(6), R(7), R(8), 8448c2ecf20Sopenharmony_ci R(9), R(10), C(8), C(9), R(11), RS, R(12), R(13), R(14), 8458c2ecf20Sopenharmony_ci R(15), 0, 0, 0 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci }, 8488c2ecf20Sopenharmony_ci { 8498c2ecf20Sopenharmony_ci .addrdec = AMAP_2KB, .dden = DEN_8Gb, .dwid = X8, 8508c2ecf20Sopenharmony_ci .rowbits = 16, .colbits = 11, 8518c2ecf20Sopenharmony_ci .bits = { 8528c2ecf20Sopenharmony_ci C(2), C(3), C(4), C(5), C(6), C(7), B(0), B(1), B(2), 8538c2ecf20Sopenharmony_ci R(0), R(1), R(2), R(3), R(4), R(5), R(6), R(7), R(8), 8548c2ecf20Sopenharmony_ci R(9), R(10), C(8), C(9), R(11), RS, C(11), R(12), R(13), 8558c2ecf20Sopenharmony_ci R(14), R(15), 0, 0 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci }, 8588c2ecf20Sopenharmony_ci { 8598c2ecf20Sopenharmony_ci .addrdec = AMAP_4KB, .dden = DEN_4Gb, .dwid = X16, 8608c2ecf20Sopenharmony_ci .rowbits = 15, .colbits = 10, 8618c2ecf20Sopenharmony_ci .bits = { 8628c2ecf20Sopenharmony_ci C(2), C(3), C(4), C(5), C(6), C(7), C(8), B(0), B(1), 8638c2ecf20Sopenharmony_ci B(2), R(0), R(1), R(2), R(3), R(4), R(5), R(6), R(7), 8648c2ecf20Sopenharmony_ci R(8), R(9), R(10), C(9), R(11), RS, R(12), R(13), R(14), 8658c2ecf20Sopenharmony_ci 0, 0, 0, 0 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci }, 8688c2ecf20Sopenharmony_ci { 8698c2ecf20Sopenharmony_ci .addrdec = AMAP_4KB, .dden = DEN_4Gb, .dwid = X8, 8708c2ecf20Sopenharmony_ci .rowbits = 16, .colbits = 10, 8718c2ecf20Sopenharmony_ci .bits = { 8728c2ecf20Sopenharmony_ci C(2), C(3), C(4), C(5), C(6), C(7), C(8), B(0), B(1), 8738c2ecf20Sopenharmony_ci B(2), R(0), R(1), R(2), R(3), R(4), R(5), R(6), R(7), 8748c2ecf20Sopenharmony_ci R(8), R(9), R(10), C(9), R(11), RS, R(12), R(13), R(14), 8758c2ecf20Sopenharmony_ci R(15), 0, 0, 0 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci }, 8788c2ecf20Sopenharmony_ci { 8798c2ecf20Sopenharmony_ci .addrdec = AMAP_4KB, .dden = DEN_8Gb, .dwid = X16, 8808c2ecf20Sopenharmony_ci .rowbits = 16, .colbits = 10, 8818c2ecf20Sopenharmony_ci .bits = { 8828c2ecf20Sopenharmony_ci C(2), C(3), C(4), C(5), C(6), C(7), C(8), B(0), B(1), 8838c2ecf20Sopenharmony_ci B(2), R(0), R(1), R(2), R(3), R(4), R(5), R(6), R(7), 8848c2ecf20Sopenharmony_ci R(8), R(9), R(10), C(9), R(11), RS, R(12), R(13), R(14), 8858c2ecf20Sopenharmony_ci R(15), 0, 0, 0 8868c2ecf20Sopenharmony_ci } 8878c2ecf20Sopenharmony_ci }, 8888c2ecf20Sopenharmony_ci { 8898c2ecf20Sopenharmony_ci .addrdec = AMAP_4KB, .dden = DEN_8Gb, .dwid = X8, 8908c2ecf20Sopenharmony_ci .rowbits = 16, .colbits = 11, 8918c2ecf20Sopenharmony_ci .bits = { 8928c2ecf20Sopenharmony_ci C(2), C(3), C(4), C(5), C(6), C(7), C(8), B(0), B(1), 8938c2ecf20Sopenharmony_ci B(2), R(0), R(1), R(2), R(3), R(4), R(5), R(6), R(7), 8948c2ecf20Sopenharmony_ci R(8), R(9), R(10), C(9), R(11), RS, C(11), R(12), R(13), 8958c2ecf20Sopenharmony_ci R(14), R(15), 0, 0 8968c2ecf20Sopenharmony_ci } 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci}; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_cistatic int bank_hash(u64 pmiaddr, int idx, int shft) 9018c2ecf20Sopenharmony_ci{ 9028c2ecf20Sopenharmony_ci int bhash = 0; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci switch (idx) { 9058c2ecf20Sopenharmony_ci case 0: 9068c2ecf20Sopenharmony_ci bhash ^= ((pmiaddr >> (12 + shft)) ^ (pmiaddr >> (9 + shft))) & 1; 9078c2ecf20Sopenharmony_ci break; 9088c2ecf20Sopenharmony_ci case 1: 9098c2ecf20Sopenharmony_ci bhash ^= (((pmiaddr >> (10 + shft)) ^ (pmiaddr >> (8 + shft))) & 1) << 1; 9108c2ecf20Sopenharmony_ci bhash ^= ((pmiaddr >> 22) & 1) << 1; 9118c2ecf20Sopenharmony_ci break; 9128c2ecf20Sopenharmony_ci case 2: 9138c2ecf20Sopenharmony_ci bhash ^= (((pmiaddr >> (13 + shft)) ^ (pmiaddr >> (11 + shft))) & 1) << 2; 9148c2ecf20Sopenharmony_ci break; 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci return bhash; 9188c2ecf20Sopenharmony_ci} 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_cistatic int rank_hash(u64 pmiaddr) 9218c2ecf20Sopenharmony_ci{ 9228c2ecf20Sopenharmony_ci return ((pmiaddr >> 16) ^ (pmiaddr >> 10)) & 1; 9238c2ecf20Sopenharmony_ci} 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci/* Second stage decode. Compute rank, bank, row & column. */ 9268c2ecf20Sopenharmony_cistatic int apl_pmi2mem(struct mem_ctl_info *mci, u64 pmiaddr, u32 pmiidx, 9278c2ecf20Sopenharmony_ci struct dram_addr *daddr, char *msg) 9288c2ecf20Sopenharmony_ci{ 9298c2ecf20Sopenharmony_ci struct d_cr_drp0 *cr_drp0 = &drp0[pmiidx]; 9308c2ecf20Sopenharmony_ci struct pnd2_pvt *pvt = mci->pvt_info; 9318c2ecf20Sopenharmony_ci int g = pvt->dimm_geom[pmiidx]; 9328c2ecf20Sopenharmony_ci struct dimm_geometry *d = &dimms[g]; 9338c2ecf20Sopenharmony_ci int column = 0, bank = 0, row = 0, rank = 0; 9348c2ecf20Sopenharmony_ci int i, idx, type, skiprs = 0; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci for (i = 0; i < PMI_ADDRESS_WIDTH; i++) { 9378c2ecf20Sopenharmony_ci int bit = (pmiaddr >> i) & 1; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci if (i + skiprs >= PMI_ADDRESS_WIDTH) { 9408c2ecf20Sopenharmony_ci snprintf(msg, PND2_MSG_SIZE, "Bad dimm_geometry[] table\n"); 9418c2ecf20Sopenharmony_ci return -EINVAL; 9428c2ecf20Sopenharmony_ci } 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci type = d->bits[i + skiprs] & ~0xf; 9458c2ecf20Sopenharmony_ci idx = d->bits[i + skiprs] & 0xf; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci /* 9488c2ecf20Sopenharmony_ci * On single rank DIMMs ignore the rank select bit 9498c2ecf20Sopenharmony_ci * and shift remainder of "bits[]" down one place. 9508c2ecf20Sopenharmony_ci */ 9518c2ecf20Sopenharmony_ci if (type == RS && (cr_drp0->rken0 + cr_drp0->rken1) == 1) { 9528c2ecf20Sopenharmony_ci skiprs = 1; 9538c2ecf20Sopenharmony_ci type = d->bits[i + skiprs] & ~0xf; 9548c2ecf20Sopenharmony_ci idx = d->bits[i + skiprs] & 0xf; 9558c2ecf20Sopenharmony_ci } 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci switch (type) { 9588c2ecf20Sopenharmony_ci case C(0): 9598c2ecf20Sopenharmony_ci column |= (bit << idx); 9608c2ecf20Sopenharmony_ci break; 9618c2ecf20Sopenharmony_ci case B(0): 9628c2ecf20Sopenharmony_ci bank |= (bit << idx); 9638c2ecf20Sopenharmony_ci if (cr_drp0->bahen) 9648c2ecf20Sopenharmony_ci bank ^= bank_hash(pmiaddr, idx, d->addrdec); 9658c2ecf20Sopenharmony_ci break; 9668c2ecf20Sopenharmony_ci case R(0): 9678c2ecf20Sopenharmony_ci row |= (bit << idx); 9688c2ecf20Sopenharmony_ci break; 9698c2ecf20Sopenharmony_ci case RS: 9708c2ecf20Sopenharmony_ci rank = bit; 9718c2ecf20Sopenharmony_ci if (cr_drp0->rsien) 9728c2ecf20Sopenharmony_ci rank ^= rank_hash(pmiaddr); 9738c2ecf20Sopenharmony_ci break; 9748c2ecf20Sopenharmony_ci default: 9758c2ecf20Sopenharmony_ci if (bit) { 9768c2ecf20Sopenharmony_ci snprintf(msg, PND2_MSG_SIZE, "Bad translation\n"); 9778c2ecf20Sopenharmony_ci return -EINVAL; 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci goto done; 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_cidone: 9848c2ecf20Sopenharmony_ci daddr->col = column; 9858c2ecf20Sopenharmony_ci daddr->bank = bank; 9868c2ecf20Sopenharmony_ci daddr->row = row; 9878c2ecf20Sopenharmony_ci daddr->rank = rank; 9888c2ecf20Sopenharmony_ci daddr->dimm = 0; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci return 0; 9918c2ecf20Sopenharmony_ci} 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci/* Pluck bit "in" from pmiaddr and return value shifted to bit "out" */ 9948c2ecf20Sopenharmony_ci#define dnv_get_bit(pmi, in, out) ((int)(((pmi) >> (in)) & 1u) << (out)) 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_cistatic int dnv_pmi2mem(struct mem_ctl_info *mci, u64 pmiaddr, u32 pmiidx, 9978c2ecf20Sopenharmony_ci struct dram_addr *daddr, char *msg) 9988c2ecf20Sopenharmony_ci{ 9998c2ecf20Sopenharmony_ci /* Rank 0 or 1 */ 10008c2ecf20Sopenharmony_ci daddr->rank = dnv_get_bit(pmiaddr, dmap[pmiidx].rs0 + 13, 0); 10018c2ecf20Sopenharmony_ci /* Rank 2 or 3 */ 10028c2ecf20Sopenharmony_ci daddr->rank |= dnv_get_bit(pmiaddr, dmap[pmiidx].rs1 + 13, 1); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci /* 10058c2ecf20Sopenharmony_ci * Normally ranks 0,1 are DIMM0, and 2,3 are DIMM1, but we 10068c2ecf20Sopenharmony_ci * flip them if DIMM1 is larger than DIMM0. 10078c2ecf20Sopenharmony_ci */ 10088c2ecf20Sopenharmony_ci daddr->dimm = (daddr->rank >= 2) ^ drp[pmiidx].dimmflip; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci daddr->bank = dnv_get_bit(pmiaddr, dmap[pmiidx].ba0 + 6, 0); 10118c2ecf20Sopenharmony_ci daddr->bank |= dnv_get_bit(pmiaddr, dmap[pmiidx].ba1 + 6, 1); 10128c2ecf20Sopenharmony_ci daddr->bank |= dnv_get_bit(pmiaddr, dmap[pmiidx].bg0 + 6, 2); 10138c2ecf20Sopenharmony_ci if (dsch.ddr4en) 10148c2ecf20Sopenharmony_ci daddr->bank |= dnv_get_bit(pmiaddr, dmap[pmiidx].bg1 + 6, 3); 10158c2ecf20Sopenharmony_ci if (dmap1[pmiidx].bxor) { 10168c2ecf20Sopenharmony_ci if (dsch.ddr4en) { 10178c2ecf20Sopenharmony_ci daddr->bank ^= dnv_get_bit(pmiaddr, dmap3[pmiidx].row6 + 6, 0); 10188c2ecf20Sopenharmony_ci daddr->bank ^= dnv_get_bit(pmiaddr, dmap3[pmiidx].row7 + 6, 1); 10198c2ecf20Sopenharmony_ci if (dsch.chan_width == 0) 10208c2ecf20Sopenharmony_ci /* 64/72 bit dram channel width */ 10218c2ecf20Sopenharmony_ci daddr->bank ^= dnv_get_bit(pmiaddr, dmap5[pmiidx].ca3 + 6, 2); 10228c2ecf20Sopenharmony_ci else 10238c2ecf20Sopenharmony_ci /* 32/40 bit dram channel width */ 10248c2ecf20Sopenharmony_ci daddr->bank ^= dnv_get_bit(pmiaddr, dmap5[pmiidx].ca4 + 6, 2); 10258c2ecf20Sopenharmony_ci daddr->bank ^= dnv_get_bit(pmiaddr, dmap2[pmiidx].row2 + 6, 3); 10268c2ecf20Sopenharmony_ci } else { 10278c2ecf20Sopenharmony_ci daddr->bank ^= dnv_get_bit(pmiaddr, dmap2[pmiidx].row2 + 6, 0); 10288c2ecf20Sopenharmony_ci daddr->bank ^= dnv_get_bit(pmiaddr, dmap3[pmiidx].row6 + 6, 1); 10298c2ecf20Sopenharmony_ci if (dsch.chan_width == 0) 10308c2ecf20Sopenharmony_ci daddr->bank ^= dnv_get_bit(pmiaddr, dmap5[pmiidx].ca3 + 6, 2); 10318c2ecf20Sopenharmony_ci else 10328c2ecf20Sopenharmony_ci daddr->bank ^= dnv_get_bit(pmiaddr, dmap5[pmiidx].ca4 + 6, 2); 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci } 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci daddr->row = dnv_get_bit(pmiaddr, dmap2[pmiidx].row0 + 6, 0); 10378c2ecf20Sopenharmony_ci daddr->row |= dnv_get_bit(pmiaddr, dmap2[pmiidx].row1 + 6, 1); 10388c2ecf20Sopenharmony_ci daddr->row |= dnv_get_bit(pmiaddr, dmap2[pmiidx].row2 + 6, 2); 10398c2ecf20Sopenharmony_ci daddr->row |= dnv_get_bit(pmiaddr, dmap2[pmiidx].row3 + 6, 3); 10408c2ecf20Sopenharmony_ci daddr->row |= dnv_get_bit(pmiaddr, dmap2[pmiidx].row4 + 6, 4); 10418c2ecf20Sopenharmony_ci daddr->row |= dnv_get_bit(pmiaddr, dmap2[pmiidx].row5 + 6, 5); 10428c2ecf20Sopenharmony_ci daddr->row |= dnv_get_bit(pmiaddr, dmap3[pmiidx].row6 + 6, 6); 10438c2ecf20Sopenharmony_ci daddr->row |= dnv_get_bit(pmiaddr, dmap3[pmiidx].row7 + 6, 7); 10448c2ecf20Sopenharmony_ci daddr->row |= dnv_get_bit(pmiaddr, dmap3[pmiidx].row8 + 6, 8); 10458c2ecf20Sopenharmony_ci daddr->row |= dnv_get_bit(pmiaddr, dmap3[pmiidx].row9 + 6, 9); 10468c2ecf20Sopenharmony_ci daddr->row |= dnv_get_bit(pmiaddr, dmap3[pmiidx].row10 + 6, 10); 10478c2ecf20Sopenharmony_ci daddr->row |= dnv_get_bit(pmiaddr, dmap3[pmiidx].row11 + 6, 11); 10488c2ecf20Sopenharmony_ci daddr->row |= dnv_get_bit(pmiaddr, dmap4[pmiidx].row12 + 6, 12); 10498c2ecf20Sopenharmony_ci daddr->row |= dnv_get_bit(pmiaddr, dmap4[pmiidx].row13 + 6, 13); 10508c2ecf20Sopenharmony_ci if (dmap4[pmiidx].row14 != 31) 10518c2ecf20Sopenharmony_ci daddr->row |= dnv_get_bit(pmiaddr, dmap4[pmiidx].row14 + 6, 14); 10528c2ecf20Sopenharmony_ci if (dmap4[pmiidx].row15 != 31) 10538c2ecf20Sopenharmony_ci daddr->row |= dnv_get_bit(pmiaddr, dmap4[pmiidx].row15 + 6, 15); 10548c2ecf20Sopenharmony_ci if (dmap4[pmiidx].row16 != 31) 10558c2ecf20Sopenharmony_ci daddr->row |= dnv_get_bit(pmiaddr, dmap4[pmiidx].row16 + 6, 16); 10568c2ecf20Sopenharmony_ci if (dmap4[pmiidx].row17 != 31) 10578c2ecf20Sopenharmony_ci daddr->row |= dnv_get_bit(pmiaddr, dmap4[pmiidx].row17 + 6, 17); 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci daddr->col = dnv_get_bit(pmiaddr, dmap5[pmiidx].ca3 + 6, 3); 10608c2ecf20Sopenharmony_ci daddr->col |= dnv_get_bit(pmiaddr, dmap5[pmiidx].ca4 + 6, 4); 10618c2ecf20Sopenharmony_ci daddr->col |= dnv_get_bit(pmiaddr, dmap5[pmiidx].ca5 + 6, 5); 10628c2ecf20Sopenharmony_ci daddr->col |= dnv_get_bit(pmiaddr, dmap5[pmiidx].ca6 + 6, 6); 10638c2ecf20Sopenharmony_ci daddr->col |= dnv_get_bit(pmiaddr, dmap5[pmiidx].ca7 + 6, 7); 10648c2ecf20Sopenharmony_ci daddr->col |= dnv_get_bit(pmiaddr, dmap5[pmiidx].ca8 + 6, 8); 10658c2ecf20Sopenharmony_ci daddr->col |= dnv_get_bit(pmiaddr, dmap5[pmiidx].ca9 + 6, 9); 10668c2ecf20Sopenharmony_ci if (!dsch.ddr4en && dmap1[pmiidx].ca11 != 0x3f) 10678c2ecf20Sopenharmony_ci daddr->col |= dnv_get_bit(pmiaddr, dmap1[pmiidx].ca11 + 13, 11); 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci return 0; 10708c2ecf20Sopenharmony_ci} 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_cistatic int check_channel(int ch) 10738c2ecf20Sopenharmony_ci{ 10748c2ecf20Sopenharmony_ci if (drp0[ch].dramtype != 0) { 10758c2ecf20Sopenharmony_ci pnd2_printk(KERN_INFO, "Unsupported DIMM in channel %d\n", ch); 10768c2ecf20Sopenharmony_ci return 1; 10778c2ecf20Sopenharmony_ci } else if (drp0[ch].eccen == 0) { 10788c2ecf20Sopenharmony_ci pnd2_printk(KERN_INFO, "ECC disabled on channel %d\n", ch); 10798c2ecf20Sopenharmony_ci return 1; 10808c2ecf20Sopenharmony_ci } 10818c2ecf20Sopenharmony_ci return 0; 10828c2ecf20Sopenharmony_ci} 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_cistatic int apl_check_ecc_active(void) 10858c2ecf20Sopenharmony_ci{ 10868c2ecf20Sopenharmony_ci int i, ret = 0; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci /* Check dramtype and ECC mode for each present DIMM */ 10898c2ecf20Sopenharmony_ci for (i = 0; i < APL_NUM_CHANNELS; i++) 10908c2ecf20Sopenharmony_ci if (chan_mask & BIT(i)) 10918c2ecf20Sopenharmony_ci ret += check_channel(i); 10928c2ecf20Sopenharmony_ci return ret ? -EINVAL : 0; 10938c2ecf20Sopenharmony_ci} 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci#define DIMMS_PRESENT(d) ((d)->rken0 + (d)->rken1 + (d)->rken2 + (d)->rken3) 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_cistatic int check_unit(int ch) 10988c2ecf20Sopenharmony_ci{ 10998c2ecf20Sopenharmony_ci struct d_cr_drp *d = &drp[ch]; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci if (DIMMS_PRESENT(d) && !ecc_ctrl[ch].eccen) { 11028c2ecf20Sopenharmony_ci pnd2_printk(KERN_INFO, "ECC disabled on channel %d\n", ch); 11038c2ecf20Sopenharmony_ci return 1; 11048c2ecf20Sopenharmony_ci } 11058c2ecf20Sopenharmony_ci return 0; 11068c2ecf20Sopenharmony_ci} 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_cistatic int dnv_check_ecc_active(void) 11098c2ecf20Sopenharmony_ci{ 11108c2ecf20Sopenharmony_ci int i, ret = 0; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci for (i = 0; i < DNV_NUM_CHANNELS; i++) 11138c2ecf20Sopenharmony_ci ret += check_unit(i); 11148c2ecf20Sopenharmony_ci return ret ? -EINVAL : 0; 11158c2ecf20Sopenharmony_ci} 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_cistatic int get_memory_error_data(struct mem_ctl_info *mci, u64 addr, 11188c2ecf20Sopenharmony_ci struct dram_addr *daddr, char *msg) 11198c2ecf20Sopenharmony_ci{ 11208c2ecf20Sopenharmony_ci u64 pmiaddr; 11218c2ecf20Sopenharmony_ci u32 pmiidx; 11228c2ecf20Sopenharmony_ci int ret; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci ret = sys2pmi(addr, &pmiidx, &pmiaddr, msg); 11258c2ecf20Sopenharmony_ci if (ret) 11268c2ecf20Sopenharmony_ci return ret; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci pmiaddr >>= ops->pmiaddr_shift; 11298c2ecf20Sopenharmony_ci /* pmi channel idx to dimm channel idx */ 11308c2ecf20Sopenharmony_ci pmiidx >>= ops->pmiidx_shift; 11318c2ecf20Sopenharmony_ci daddr->chan = pmiidx; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci ret = ops->pmi2mem(mci, pmiaddr, pmiidx, daddr, msg); 11348c2ecf20Sopenharmony_ci if (ret) 11358c2ecf20Sopenharmony_ci return ret; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci edac_dbg(0, "SysAddr=%llx PmiAddr=%llx Channel=%d DIMM=%d Rank=%d Bank=%d Row=%d Column=%d\n", 11388c2ecf20Sopenharmony_ci addr, pmiaddr, daddr->chan, daddr->dimm, daddr->rank, daddr->bank, daddr->row, daddr->col); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci return 0; 11418c2ecf20Sopenharmony_ci} 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_cistatic void pnd2_mce_output_error(struct mem_ctl_info *mci, const struct mce *m, 11448c2ecf20Sopenharmony_ci struct dram_addr *daddr) 11458c2ecf20Sopenharmony_ci{ 11468c2ecf20Sopenharmony_ci enum hw_event_mc_err_type tp_event; 11478c2ecf20Sopenharmony_ci char *optype, msg[PND2_MSG_SIZE]; 11488c2ecf20Sopenharmony_ci bool ripv = m->mcgstatus & MCG_STATUS_RIPV; 11498c2ecf20Sopenharmony_ci bool overflow = m->status & MCI_STATUS_OVER; 11508c2ecf20Sopenharmony_ci bool uc_err = m->status & MCI_STATUS_UC; 11518c2ecf20Sopenharmony_ci bool recov = m->status & MCI_STATUS_S; 11528c2ecf20Sopenharmony_ci u32 core_err_cnt = GET_BITFIELD(m->status, 38, 52); 11538c2ecf20Sopenharmony_ci u32 mscod = GET_BITFIELD(m->status, 16, 31); 11548c2ecf20Sopenharmony_ci u32 errcode = GET_BITFIELD(m->status, 0, 15); 11558c2ecf20Sopenharmony_ci u32 optypenum = GET_BITFIELD(m->status, 4, 6); 11568c2ecf20Sopenharmony_ci int rc; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci tp_event = uc_err ? (ripv ? HW_EVENT_ERR_UNCORRECTED : HW_EVENT_ERR_FATAL) : 11598c2ecf20Sopenharmony_ci HW_EVENT_ERR_CORRECTED; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci /* 11628c2ecf20Sopenharmony_ci * According with Table 15-9 of the Intel Architecture spec vol 3A, 11638c2ecf20Sopenharmony_ci * memory errors should fit in this mask: 11648c2ecf20Sopenharmony_ci * 000f 0000 1mmm cccc (binary) 11658c2ecf20Sopenharmony_ci * where: 11668c2ecf20Sopenharmony_ci * f = Correction Report Filtering Bit. If 1, subsequent errors 11678c2ecf20Sopenharmony_ci * won't be shown 11688c2ecf20Sopenharmony_ci * mmm = error type 11698c2ecf20Sopenharmony_ci * cccc = channel 11708c2ecf20Sopenharmony_ci * If the mask doesn't match, report an error to the parsing logic 11718c2ecf20Sopenharmony_ci */ 11728c2ecf20Sopenharmony_ci if (!((errcode & 0xef80) == 0x80)) { 11738c2ecf20Sopenharmony_ci optype = "Can't parse: it is not a mem"; 11748c2ecf20Sopenharmony_ci } else { 11758c2ecf20Sopenharmony_ci switch (optypenum) { 11768c2ecf20Sopenharmony_ci case 0: 11778c2ecf20Sopenharmony_ci optype = "generic undef request error"; 11788c2ecf20Sopenharmony_ci break; 11798c2ecf20Sopenharmony_ci case 1: 11808c2ecf20Sopenharmony_ci optype = "memory read error"; 11818c2ecf20Sopenharmony_ci break; 11828c2ecf20Sopenharmony_ci case 2: 11838c2ecf20Sopenharmony_ci optype = "memory write error"; 11848c2ecf20Sopenharmony_ci break; 11858c2ecf20Sopenharmony_ci case 3: 11868c2ecf20Sopenharmony_ci optype = "addr/cmd error"; 11878c2ecf20Sopenharmony_ci break; 11888c2ecf20Sopenharmony_ci case 4: 11898c2ecf20Sopenharmony_ci optype = "memory scrubbing error"; 11908c2ecf20Sopenharmony_ci break; 11918c2ecf20Sopenharmony_ci default: 11928c2ecf20Sopenharmony_ci optype = "reserved"; 11938c2ecf20Sopenharmony_ci break; 11948c2ecf20Sopenharmony_ci } 11958c2ecf20Sopenharmony_ci } 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci /* Only decode errors with an valid address (ADDRV) */ 11988c2ecf20Sopenharmony_ci if (!(m->status & MCI_STATUS_ADDRV)) 11998c2ecf20Sopenharmony_ci return; 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci rc = get_memory_error_data(mci, m->addr, daddr, msg); 12028c2ecf20Sopenharmony_ci if (rc) 12038c2ecf20Sopenharmony_ci goto address_error; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci snprintf(msg, sizeof(msg), 12068c2ecf20Sopenharmony_ci "%s%s err_code:%04x:%04x channel:%d DIMM:%d rank:%d row:%d bank:%d col:%d", 12078c2ecf20Sopenharmony_ci overflow ? " OVERFLOW" : "", (uc_err && recov) ? " recoverable" : "", mscod, 12088c2ecf20Sopenharmony_ci errcode, daddr->chan, daddr->dimm, daddr->rank, daddr->row, daddr->bank, daddr->col); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci edac_dbg(0, "%s\n", msg); 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci /* Call the helper to output message */ 12138c2ecf20Sopenharmony_ci edac_mc_handle_error(tp_event, mci, core_err_cnt, m->addr >> PAGE_SHIFT, 12148c2ecf20Sopenharmony_ci m->addr & ~PAGE_MASK, 0, daddr->chan, daddr->dimm, -1, optype, msg); 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci return; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ciaddress_error: 12198c2ecf20Sopenharmony_ci edac_mc_handle_error(tp_event, mci, core_err_cnt, 0, 0, 0, -1, -1, -1, msg, ""); 12208c2ecf20Sopenharmony_ci} 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_cistatic void apl_get_dimm_config(struct mem_ctl_info *mci) 12238c2ecf20Sopenharmony_ci{ 12248c2ecf20Sopenharmony_ci struct pnd2_pvt *pvt = mci->pvt_info; 12258c2ecf20Sopenharmony_ci struct dimm_info *dimm; 12268c2ecf20Sopenharmony_ci struct d_cr_drp0 *d; 12278c2ecf20Sopenharmony_ci u64 capacity; 12288c2ecf20Sopenharmony_ci int i, g; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci for (i = 0; i < APL_NUM_CHANNELS; i++) { 12318c2ecf20Sopenharmony_ci if (!(chan_mask & BIT(i))) 12328c2ecf20Sopenharmony_ci continue; 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci dimm = edac_get_dimm(mci, i, 0, 0); 12358c2ecf20Sopenharmony_ci if (!dimm) { 12368c2ecf20Sopenharmony_ci edac_dbg(0, "No allocated DIMM for channel %d\n", i); 12378c2ecf20Sopenharmony_ci continue; 12388c2ecf20Sopenharmony_ci } 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci d = &drp0[i]; 12418c2ecf20Sopenharmony_ci for (g = 0; g < ARRAY_SIZE(dimms); g++) 12428c2ecf20Sopenharmony_ci if (dimms[g].addrdec == d->addrdec && 12438c2ecf20Sopenharmony_ci dimms[g].dden == d->dden && 12448c2ecf20Sopenharmony_ci dimms[g].dwid == d->dwid) 12458c2ecf20Sopenharmony_ci break; 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci if (g == ARRAY_SIZE(dimms)) { 12488c2ecf20Sopenharmony_ci edac_dbg(0, "Channel %d: unrecognized DIMM\n", i); 12498c2ecf20Sopenharmony_ci continue; 12508c2ecf20Sopenharmony_ci } 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci pvt->dimm_geom[i] = g; 12538c2ecf20Sopenharmony_ci capacity = (d->rken0 + d->rken1) * 8 * (1ul << dimms[g].rowbits) * 12548c2ecf20Sopenharmony_ci (1ul << dimms[g].colbits); 12558c2ecf20Sopenharmony_ci edac_dbg(0, "Channel %d: %lld MByte DIMM\n", i, capacity >> (20 - 3)); 12568c2ecf20Sopenharmony_ci dimm->nr_pages = MiB_TO_PAGES(capacity >> (20 - 3)); 12578c2ecf20Sopenharmony_ci dimm->grain = 32; 12588c2ecf20Sopenharmony_ci dimm->dtype = (d->dwid == 0) ? DEV_X8 : DEV_X16; 12598c2ecf20Sopenharmony_ci dimm->mtype = MEM_DDR3; 12608c2ecf20Sopenharmony_ci dimm->edac_mode = EDAC_SECDED; 12618c2ecf20Sopenharmony_ci snprintf(dimm->label, sizeof(dimm->label), "Slice#%d_Chan#%d", i / 2, i % 2); 12628c2ecf20Sopenharmony_ci } 12638c2ecf20Sopenharmony_ci} 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_cistatic const int dnv_dtypes[] = { 12668c2ecf20Sopenharmony_ci DEV_X8, DEV_X4, DEV_X16, DEV_UNKNOWN 12678c2ecf20Sopenharmony_ci}; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_cistatic void dnv_get_dimm_config(struct mem_ctl_info *mci) 12708c2ecf20Sopenharmony_ci{ 12718c2ecf20Sopenharmony_ci int i, j, ranks_of_dimm[DNV_MAX_DIMMS], banks, rowbits, colbits, memtype; 12728c2ecf20Sopenharmony_ci struct dimm_info *dimm; 12738c2ecf20Sopenharmony_ci struct d_cr_drp *d; 12748c2ecf20Sopenharmony_ci u64 capacity; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci if (dsch.ddr4en) { 12778c2ecf20Sopenharmony_ci memtype = MEM_DDR4; 12788c2ecf20Sopenharmony_ci banks = 16; 12798c2ecf20Sopenharmony_ci colbits = 10; 12808c2ecf20Sopenharmony_ci } else { 12818c2ecf20Sopenharmony_ci memtype = MEM_DDR3; 12828c2ecf20Sopenharmony_ci banks = 8; 12838c2ecf20Sopenharmony_ci } 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci for (i = 0; i < DNV_NUM_CHANNELS; i++) { 12868c2ecf20Sopenharmony_ci if (dmap4[i].row14 == 31) 12878c2ecf20Sopenharmony_ci rowbits = 14; 12888c2ecf20Sopenharmony_ci else if (dmap4[i].row15 == 31) 12898c2ecf20Sopenharmony_ci rowbits = 15; 12908c2ecf20Sopenharmony_ci else if (dmap4[i].row16 == 31) 12918c2ecf20Sopenharmony_ci rowbits = 16; 12928c2ecf20Sopenharmony_ci else if (dmap4[i].row17 == 31) 12938c2ecf20Sopenharmony_ci rowbits = 17; 12948c2ecf20Sopenharmony_ci else 12958c2ecf20Sopenharmony_ci rowbits = 18; 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci if (memtype == MEM_DDR3) { 12988c2ecf20Sopenharmony_ci if (dmap1[i].ca11 != 0x3f) 12998c2ecf20Sopenharmony_ci colbits = 12; 13008c2ecf20Sopenharmony_ci else 13018c2ecf20Sopenharmony_ci colbits = 10; 13028c2ecf20Sopenharmony_ci } 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci d = &drp[i]; 13058c2ecf20Sopenharmony_ci /* DIMM0 is present if rank0 and/or rank1 is enabled */ 13068c2ecf20Sopenharmony_ci ranks_of_dimm[0] = d->rken0 + d->rken1; 13078c2ecf20Sopenharmony_ci /* DIMM1 is present if rank2 and/or rank3 is enabled */ 13088c2ecf20Sopenharmony_ci ranks_of_dimm[1] = d->rken2 + d->rken3; 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci for (j = 0; j < DNV_MAX_DIMMS; j++) { 13118c2ecf20Sopenharmony_ci if (!ranks_of_dimm[j]) 13128c2ecf20Sopenharmony_ci continue; 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci dimm = edac_get_dimm(mci, i, j, 0); 13158c2ecf20Sopenharmony_ci if (!dimm) { 13168c2ecf20Sopenharmony_ci edac_dbg(0, "No allocated DIMM for channel %d DIMM %d\n", i, j); 13178c2ecf20Sopenharmony_ci continue; 13188c2ecf20Sopenharmony_ci } 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci capacity = ranks_of_dimm[j] * banks * (1ul << rowbits) * (1ul << colbits); 13218c2ecf20Sopenharmony_ci edac_dbg(0, "Channel %d DIMM %d: %lld MByte DIMM\n", i, j, capacity >> (20 - 3)); 13228c2ecf20Sopenharmony_ci dimm->nr_pages = MiB_TO_PAGES(capacity >> (20 - 3)); 13238c2ecf20Sopenharmony_ci dimm->grain = 32; 13248c2ecf20Sopenharmony_ci dimm->dtype = dnv_dtypes[j ? d->dimmdwid0 : d->dimmdwid1]; 13258c2ecf20Sopenharmony_ci dimm->mtype = memtype; 13268c2ecf20Sopenharmony_ci dimm->edac_mode = EDAC_SECDED; 13278c2ecf20Sopenharmony_ci snprintf(dimm->label, sizeof(dimm->label), "Chan#%d_DIMM#%d", i, j); 13288c2ecf20Sopenharmony_ci } 13298c2ecf20Sopenharmony_ci } 13308c2ecf20Sopenharmony_ci} 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_cistatic int pnd2_register_mci(struct mem_ctl_info **ppmci) 13338c2ecf20Sopenharmony_ci{ 13348c2ecf20Sopenharmony_ci struct edac_mc_layer layers[2]; 13358c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 13368c2ecf20Sopenharmony_ci struct pnd2_pvt *pvt; 13378c2ecf20Sopenharmony_ci int rc; 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci rc = ops->check_ecc(); 13408c2ecf20Sopenharmony_ci if (rc < 0) 13418c2ecf20Sopenharmony_ci return rc; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci /* Allocate a new MC control structure */ 13448c2ecf20Sopenharmony_ci layers[0].type = EDAC_MC_LAYER_CHANNEL; 13458c2ecf20Sopenharmony_ci layers[0].size = ops->channels; 13468c2ecf20Sopenharmony_ci layers[0].is_virt_csrow = false; 13478c2ecf20Sopenharmony_ci layers[1].type = EDAC_MC_LAYER_SLOT; 13488c2ecf20Sopenharmony_ci layers[1].size = ops->dimms_per_channel; 13498c2ecf20Sopenharmony_ci layers[1].is_virt_csrow = true; 13508c2ecf20Sopenharmony_ci mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(*pvt)); 13518c2ecf20Sopenharmony_ci if (!mci) 13528c2ecf20Sopenharmony_ci return -ENOMEM; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci pvt = mci->pvt_info; 13558c2ecf20Sopenharmony_ci memset(pvt, 0, sizeof(*pvt)); 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci mci->mod_name = EDAC_MOD_STR; 13588c2ecf20Sopenharmony_ci mci->dev_name = ops->name; 13598c2ecf20Sopenharmony_ci mci->ctl_name = "Pondicherry2"; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci /* Get dimm basic config and the memory layout */ 13628c2ecf20Sopenharmony_ci ops->get_dimm_config(mci); 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci if (edac_mc_add_mc(mci)) { 13658c2ecf20Sopenharmony_ci edac_dbg(0, "MC: failed edac_mc_add_mc()\n"); 13668c2ecf20Sopenharmony_ci edac_mc_free(mci); 13678c2ecf20Sopenharmony_ci return -EINVAL; 13688c2ecf20Sopenharmony_ci } 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci *ppmci = mci; 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci return 0; 13738c2ecf20Sopenharmony_ci} 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_cistatic void pnd2_unregister_mci(struct mem_ctl_info *mci) 13768c2ecf20Sopenharmony_ci{ 13778c2ecf20Sopenharmony_ci if (unlikely(!mci || !mci->pvt_info)) { 13788c2ecf20Sopenharmony_ci pnd2_printk(KERN_ERR, "Couldn't find mci handler\n"); 13798c2ecf20Sopenharmony_ci return; 13808c2ecf20Sopenharmony_ci } 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci /* Remove MC sysfs nodes */ 13838c2ecf20Sopenharmony_ci edac_mc_del_mc(NULL); 13848c2ecf20Sopenharmony_ci edac_dbg(1, "%s: free mci struct\n", mci->ctl_name); 13858c2ecf20Sopenharmony_ci edac_mc_free(mci); 13868c2ecf20Sopenharmony_ci} 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci/* 13898c2ecf20Sopenharmony_ci * Callback function registered with core kernel mce code. 13908c2ecf20Sopenharmony_ci * Called once for each logged error. 13918c2ecf20Sopenharmony_ci */ 13928c2ecf20Sopenharmony_cistatic int pnd2_mce_check_error(struct notifier_block *nb, unsigned long val, void *data) 13938c2ecf20Sopenharmony_ci{ 13948c2ecf20Sopenharmony_ci struct mce *mce = (struct mce *)data; 13958c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 13968c2ecf20Sopenharmony_ci struct dram_addr daddr; 13978c2ecf20Sopenharmony_ci char *type; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci mci = pnd2_mci; 14008c2ecf20Sopenharmony_ci if (!mci || (mce->kflags & MCE_HANDLED_CEC)) 14018c2ecf20Sopenharmony_ci return NOTIFY_DONE; 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci /* 14048c2ecf20Sopenharmony_ci * Just let mcelog handle it if the error is 14058c2ecf20Sopenharmony_ci * outside the memory controller. A memory error 14068c2ecf20Sopenharmony_ci * is indicated by bit 7 = 1 and bits = 8-11,13-15 = 0. 14078c2ecf20Sopenharmony_ci * bit 12 has an special meaning. 14088c2ecf20Sopenharmony_ci */ 14098c2ecf20Sopenharmony_ci if ((mce->status & 0xefff) >> 7 != 1) 14108c2ecf20Sopenharmony_ci return NOTIFY_DONE; 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci if (mce->mcgstatus & MCG_STATUS_MCIP) 14138c2ecf20Sopenharmony_ci type = "Exception"; 14148c2ecf20Sopenharmony_ci else 14158c2ecf20Sopenharmony_ci type = "Event"; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci pnd2_mc_printk(mci, KERN_INFO, "HANDLING MCE MEMORY ERROR\n"); 14188c2ecf20Sopenharmony_ci pnd2_mc_printk(mci, KERN_INFO, "CPU %u: Machine Check %s: %llx Bank %u: %llx\n", 14198c2ecf20Sopenharmony_ci mce->extcpu, type, mce->mcgstatus, mce->bank, mce->status); 14208c2ecf20Sopenharmony_ci pnd2_mc_printk(mci, KERN_INFO, "TSC %llx ", mce->tsc); 14218c2ecf20Sopenharmony_ci pnd2_mc_printk(mci, KERN_INFO, "ADDR %llx ", mce->addr); 14228c2ecf20Sopenharmony_ci pnd2_mc_printk(mci, KERN_INFO, "MISC %llx ", mce->misc); 14238c2ecf20Sopenharmony_ci pnd2_mc_printk(mci, KERN_INFO, "PROCESSOR %u:%x TIME %llu SOCKET %u APIC %x\n", 14248c2ecf20Sopenharmony_ci mce->cpuvendor, mce->cpuid, mce->time, mce->socketid, mce->apicid); 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci pnd2_mce_output_error(mci, mce, &daddr); 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci /* Advice mcelog that the error were handled */ 14298c2ecf20Sopenharmony_ci mce->kflags |= MCE_HANDLED_EDAC; 14308c2ecf20Sopenharmony_ci return NOTIFY_OK; 14318c2ecf20Sopenharmony_ci} 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_cistatic struct notifier_block pnd2_mce_dec = { 14348c2ecf20Sopenharmony_ci .notifier_call = pnd2_mce_check_error, 14358c2ecf20Sopenharmony_ci .priority = MCE_PRIO_EDAC, 14368c2ecf20Sopenharmony_ci}; 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_DEBUG 14398c2ecf20Sopenharmony_ci/* 14408c2ecf20Sopenharmony_ci * Write an address to this file to exercise the address decode 14418c2ecf20Sopenharmony_ci * logic in this driver. 14428c2ecf20Sopenharmony_ci */ 14438c2ecf20Sopenharmony_cistatic u64 pnd2_fake_addr; 14448c2ecf20Sopenharmony_ci#define PND2_BLOB_SIZE 1024 14458c2ecf20Sopenharmony_cistatic char pnd2_result[PND2_BLOB_SIZE]; 14468c2ecf20Sopenharmony_cistatic struct dentry *pnd2_test; 14478c2ecf20Sopenharmony_cistatic struct debugfs_blob_wrapper pnd2_blob = { 14488c2ecf20Sopenharmony_ci .data = pnd2_result, 14498c2ecf20Sopenharmony_ci .size = 0 14508c2ecf20Sopenharmony_ci}; 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_cistatic int debugfs_u64_set(void *data, u64 val) 14538c2ecf20Sopenharmony_ci{ 14548c2ecf20Sopenharmony_ci struct dram_addr daddr; 14558c2ecf20Sopenharmony_ci struct mce m; 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci *(u64 *)data = val; 14588c2ecf20Sopenharmony_ci m.mcgstatus = 0; 14598c2ecf20Sopenharmony_ci /* ADDRV + MemRd + Unknown channel */ 14608c2ecf20Sopenharmony_ci m.status = MCI_STATUS_ADDRV + 0x9f; 14618c2ecf20Sopenharmony_ci m.addr = val; 14628c2ecf20Sopenharmony_ci pnd2_mce_output_error(pnd2_mci, &m, &daddr); 14638c2ecf20Sopenharmony_ci snprintf(pnd2_blob.data, PND2_BLOB_SIZE, 14648c2ecf20Sopenharmony_ci "SysAddr=%llx Channel=%d DIMM=%d Rank=%d Bank=%d Row=%d Column=%d\n", 14658c2ecf20Sopenharmony_ci m.addr, daddr.chan, daddr.dimm, daddr.rank, daddr.bank, daddr.row, daddr.col); 14668c2ecf20Sopenharmony_ci pnd2_blob.size = strlen(pnd2_blob.data); 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci return 0; 14698c2ecf20Sopenharmony_ci} 14708c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_u64_wo, NULL, debugfs_u64_set, "%llu\n"); 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_cistatic void setup_pnd2_debug(void) 14738c2ecf20Sopenharmony_ci{ 14748c2ecf20Sopenharmony_ci pnd2_test = edac_debugfs_create_dir("pnd2_test"); 14758c2ecf20Sopenharmony_ci edac_debugfs_create_file("pnd2_debug_addr", 0200, pnd2_test, 14768c2ecf20Sopenharmony_ci &pnd2_fake_addr, &fops_u64_wo); 14778c2ecf20Sopenharmony_ci debugfs_create_blob("pnd2_debug_results", 0400, pnd2_test, &pnd2_blob); 14788c2ecf20Sopenharmony_ci} 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_cistatic void teardown_pnd2_debug(void) 14818c2ecf20Sopenharmony_ci{ 14828c2ecf20Sopenharmony_ci debugfs_remove_recursive(pnd2_test); 14838c2ecf20Sopenharmony_ci} 14848c2ecf20Sopenharmony_ci#else 14858c2ecf20Sopenharmony_cistatic void setup_pnd2_debug(void) {} 14868c2ecf20Sopenharmony_cistatic void teardown_pnd2_debug(void) {} 14878c2ecf20Sopenharmony_ci#endif /* CONFIG_EDAC_DEBUG */ 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_cistatic int pnd2_probe(void) 14918c2ecf20Sopenharmony_ci{ 14928c2ecf20Sopenharmony_ci int rc; 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci edac_dbg(2, "\n"); 14958c2ecf20Sopenharmony_ci rc = get_registers(); 14968c2ecf20Sopenharmony_ci if (rc) 14978c2ecf20Sopenharmony_ci return rc; 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci return pnd2_register_mci(&pnd2_mci); 15008c2ecf20Sopenharmony_ci} 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_cistatic void pnd2_remove(void) 15038c2ecf20Sopenharmony_ci{ 15048c2ecf20Sopenharmony_ci edac_dbg(0, "\n"); 15058c2ecf20Sopenharmony_ci pnd2_unregister_mci(pnd2_mci); 15068c2ecf20Sopenharmony_ci} 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_cistatic struct dunit_ops apl_ops = { 15098c2ecf20Sopenharmony_ci .name = "pnd2/apl", 15108c2ecf20Sopenharmony_ci .type = APL, 15118c2ecf20Sopenharmony_ci .pmiaddr_shift = LOG2_PMI_ADDR_GRANULARITY, 15128c2ecf20Sopenharmony_ci .pmiidx_shift = 0, 15138c2ecf20Sopenharmony_ci .channels = APL_NUM_CHANNELS, 15148c2ecf20Sopenharmony_ci .dimms_per_channel = 1, 15158c2ecf20Sopenharmony_ci .rd_reg = apl_rd_reg, 15168c2ecf20Sopenharmony_ci .get_registers = apl_get_registers, 15178c2ecf20Sopenharmony_ci .check_ecc = apl_check_ecc_active, 15188c2ecf20Sopenharmony_ci .mk_region = apl_mk_region, 15198c2ecf20Sopenharmony_ci .get_dimm_config = apl_get_dimm_config, 15208c2ecf20Sopenharmony_ci .pmi2mem = apl_pmi2mem, 15218c2ecf20Sopenharmony_ci}; 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_cistatic struct dunit_ops dnv_ops = { 15248c2ecf20Sopenharmony_ci .name = "pnd2/dnv", 15258c2ecf20Sopenharmony_ci .type = DNV, 15268c2ecf20Sopenharmony_ci .pmiaddr_shift = 0, 15278c2ecf20Sopenharmony_ci .pmiidx_shift = 1, 15288c2ecf20Sopenharmony_ci .channels = DNV_NUM_CHANNELS, 15298c2ecf20Sopenharmony_ci .dimms_per_channel = 2, 15308c2ecf20Sopenharmony_ci .rd_reg = dnv_rd_reg, 15318c2ecf20Sopenharmony_ci .get_registers = dnv_get_registers, 15328c2ecf20Sopenharmony_ci .check_ecc = dnv_check_ecc_active, 15338c2ecf20Sopenharmony_ci .mk_region = dnv_mk_region, 15348c2ecf20Sopenharmony_ci .get_dimm_config = dnv_get_dimm_config, 15358c2ecf20Sopenharmony_ci .pmi2mem = dnv_pmi2mem, 15368c2ecf20Sopenharmony_ci}; 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_cistatic const struct x86_cpu_id pnd2_cpuids[] = { 15398c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT, &apl_ops), 15408c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT_D, &dnv_ops), 15418c2ecf20Sopenharmony_ci { } 15428c2ecf20Sopenharmony_ci}; 15438c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(x86cpu, pnd2_cpuids); 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_cistatic int __init pnd2_init(void) 15468c2ecf20Sopenharmony_ci{ 15478c2ecf20Sopenharmony_ci const struct x86_cpu_id *id; 15488c2ecf20Sopenharmony_ci const char *owner; 15498c2ecf20Sopenharmony_ci int rc; 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci edac_dbg(2, "\n"); 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci owner = edac_get_owner(); 15548c2ecf20Sopenharmony_ci if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR))) 15558c2ecf20Sopenharmony_ci return -EBUSY; 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci if (cpu_feature_enabled(X86_FEATURE_HYPERVISOR)) 15588c2ecf20Sopenharmony_ci return -ENODEV; 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci id = x86_match_cpu(pnd2_cpuids); 15618c2ecf20Sopenharmony_ci if (!id) 15628c2ecf20Sopenharmony_ci return -ENODEV; 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci ops = (struct dunit_ops *)id->driver_data; 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci if (ops->type == APL) { 15678c2ecf20Sopenharmony_ci p2sb_bus = pci_find_bus(0, 0); 15688c2ecf20Sopenharmony_ci if (!p2sb_bus) 15698c2ecf20Sopenharmony_ci return -ENODEV; 15708c2ecf20Sopenharmony_ci } 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci /* Ensure that the OPSTATE is set correctly for POLL or NMI */ 15738c2ecf20Sopenharmony_ci opstate_init(); 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci rc = pnd2_probe(); 15768c2ecf20Sopenharmony_ci if (rc < 0) { 15778c2ecf20Sopenharmony_ci pnd2_printk(KERN_ERR, "Failed to register device with error %d.\n", rc); 15788c2ecf20Sopenharmony_ci return rc; 15798c2ecf20Sopenharmony_ci } 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci if (!pnd2_mci) 15828c2ecf20Sopenharmony_ci return -ENODEV; 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci mce_register_decode_chain(&pnd2_mce_dec); 15858c2ecf20Sopenharmony_ci setup_pnd2_debug(); 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci return 0; 15888c2ecf20Sopenharmony_ci} 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_cistatic void __exit pnd2_exit(void) 15918c2ecf20Sopenharmony_ci{ 15928c2ecf20Sopenharmony_ci edac_dbg(2, "\n"); 15938c2ecf20Sopenharmony_ci teardown_pnd2_debug(); 15948c2ecf20Sopenharmony_ci mce_unregister_decode_chain(&pnd2_mce_dec); 15958c2ecf20Sopenharmony_ci pnd2_remove(); 15968c2ecf20Sopenharmony_ci} 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_cimodule_init(pnd2_init); 15998c2ecf20Sopenharmony_cimodule_exit(pnd2_exit); 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_cimodule_param(edac_op_state, int, 0444); 16028c2ecf20Sopenharmony_ciMODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI"); 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 16058c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tony Luck"); 16068c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MC Driver for Intel SoC using Pondicherry memory controller"); 1607