18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Intel X38 Memory Controller kernel module 38c2ecf20Sopenharmony_ci * Copyright (C) 2008 Cluster Computing, Inc. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This file may be distributed under the terms of the 68c2ecf20Sopenharmony_ci * GNU General Public License. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This file is based on i3200_edac.c 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/pci.h> 158c2ecf20Sopenharmony_ci#include <linux/pci_ids.h> 168c2ecf20Sopenharmony_ci#include <linux/edac.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/io-64-nonatomic-lo-hi.h> 198c2ecf20Sopenharmony_ci#include "edac_module.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define EDAC_MOD_STR "x38_edac" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_X38_HB 0x29e0 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define X38_RANKS 8 268c2ecf20Sopenharmony_ci#define X38_RANKS_PER_CHANNEL 4 278c2ecf20Sopenharmony_ci#define X38_CHANNELS 2 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* Intel X38 register addresses - device 0 function 0 - DRAM Controller */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define X38_MCHBAR_LOW 0x48 /* MCH Memory Mapped Register BAR */ 328c2ecf20Sopenharmony_ci#define X38_MCHBAR_HIGH 0x4c 338c2ecf20Sopenharmony_ci#define X38_MCHBAR_MASK 0xfffffc000ULL /* bits 35:14 */ 348c2ecf20Sopenharmony_ci#define X38_MMR_WINDOW_SIZE 16384 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define X38_TOM 0xa0 /* Top of Memory (16b) 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * 15:10 reserved 398c2ecf20Sopenharmony_ci * 9:0 total populated physical memory 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_ci#define X38_TOM_MASK 0x3ff /* bits 9:0 */ 428c2ecf20Sopenharmony_ci#define X38_TOM_SHIFT 26 /* 64MiB grain */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define X38_ERRSTS 0xc8 /* Error Status Register (16b) 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * 15 reserved 478c2ecf20Sopenharmony_ci * 14 Isochronous TBWRR Run Behind FIFO Full 488c2ecf20Sopenharmony_ci * (ITCV) 498c2ecf20Sopenharmony_ci * 13 Isochronous TBWRR Run Behind FIFO Put 508c2ecf20Sopenharmony_ci * (ITSTV) 518c2ecf20Sopenharmony_ci * 12 reserved 528c2ecf20Sopenharmony_ci * 11 MCH Thermal Sensor Event 538c2ecf20Sopenharmony_ci * for SMI/SCI/SERR (GTSE) 548c2ecf20Sopenharmony_ci * 10 reserved 558c2ecf20Sopenharmony_ci * 9 LOCK to non-DRAM Memory Flag (LCKF) 568c2ecf20Sopenharmony_ci * 8 reserved 578c2ecf20Sopenharmony_ci * 7 DRAM Throttle Flag (DTF) 588c2ecf20Sopenharmony_ci * 6:2 reserved 598c2ecf20Sopenharmony_ci * 1 Multi-bit DRAM ECC Error Flag (DMERR) 608c2ecf20Sopenharmony_ci * 0 Single-bit DRAM ECC Error Flag (DSERR) 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci#define X38_ERRSTS_UE 0x0002 638c2ecf20Sopenharmony_ci#define X38_ERRSTS_CE 0x0001 648c2ecf20Sopenharmony_ci#define X38_ERRSTS_BITS (X38_ERRSTS_UE | X38_ERRSTS_CE) 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* Intel MMIO register space - device 0 function 0 - MMR space */ 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define X38_C0DRB 0x200 /* Channel 0 DRAM Rank Boundary (16b x 4) 708c2ecf20Sopenharmony_ci * 718c2ecf20Sopenharmony_ci * 15:10 reserved 728c2ecf20Sopenharmony_ci * 9:0 Channel 0 DRAM Rank Boundary Address 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_ci#define X38_C1DRB 0x600 /* Channel 1 DRAM Rank Boundary (16b x 4) */ 758c2ecf20Sopenharmony_ci#define X38_DRB_MASK 0x3ff /* bits 9:0 */ 768c2ecf20Sopenharmony_ci#define X38_DRB_SHIFT 26 /* 64MiB grain */ 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#define X38_C0ECCERRLOG 0x280 /* Channel 0 ECC Error Log (64b) 798c2ecf20Sopenharmony_ci * 808c2ecf20Sopenharmony_ci * 63:48 Error Column Address (ERRCOL) 818c2ecf20Sopenharmony_ci * 47:32 Error Row Address (ERRROW) 828c2ecf20Sopenharmony_ci * 31:29 Error Bank Address (ERRBANK) 838c2ecf20Sopenharmony_ci * 28:27 Error Rank Address (ERRRANK) 848c2ecf20Sopenharmony_ci * 26:24 reserved 858c2ecf20Sopenharmony_ci * 23:16 Error Syndrome (ERRSYND) 868c2ecf20Sopenharmony_ci * 15: 2 reserved 878c2ecf20Sopenharmony_ci * 1 Multiple Bit Error Status (MERRSTS) 888c2ecf20Sopenharmony_ci * 0 Correctable Error Status (CERRSTS) 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ci#define X38_C1ECCERRLOG 0x680 /* Channel 1 ECC Error Log (64b) */ 918c2ecf20Sopenharmony_ci#define X38_ECCERRLOG_CE 0x1 928c2ecf20Sopenharmony_ci#define X38_ECCERRLOG_UE 0x2 938c2ecf20Sopenharmony_ci#define X38_ECCERRLOG_RANK_BITS 0x18000000 948c2ecf20Sopenharmony_ci#define X38_ECCERRLOG_SYNDROME_BITS 0xff0000 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci#define X38_CAPID0 0xe0 /* see P.94 of spec for details */ 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int x38_channel_num; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int how_many_channel(struct pci_dev *pdev) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci unsigned char capid0_8b; /* 8th byte of CAPID0 */ 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, X38_CAPID0 + 8, &capid0_8b); 1058c2ecf20Sopenharmony_ci if (capid0_8b & 0x20) { /* check DCD: Dual Channel Disable */ 1068c2ecf20Sopenharmony_ci edac_dbg(0, "In single channel mode\n"); 1078c2ecf20Sopenharmony_ci x38_channel_num = 1; 1088c2ecf20Sopenharmony_ci } else { 1098c2ecf20Sopenharmony_ci edac_dbg(0, "In dual channel mode\n"); 1108c2ecf20Sopenharmony_ci x38_channel_num = 2; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return x38_channel_num; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic unsigned long eccerrlog_syndrome(u64 log) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci return (log & X38_ECCERRLOG_SYNDROME_BITS) >> 16; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int eccerrlog_row(int channel, u64 log) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci return ((log & X38_ECCERRLOG_RANK_BITS) >> 27) | 1248c2ecf20Sopenharmony_ci (channel * X38_RANKS_PER_CHANNEL); 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cienum x38_chips { 1288c2ecf20Sopenharmony_ci X38 = 0, 1298c2ecf20Sopenharmony_ci}; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistruct x38_dev_info { 1328c2ecf20Sopenharmony_ci const char *ctl_name; 1338c2ecf20Sopenharmony_ci}; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistruct x38_error_info { 1368c2ecf20Sopenharmony_ci u16 errsts; 1378c2ecf20Sopenharmony_ci u16 errsts2; 1388c2ecf20Sopenharmony_ci u64 eccerrlog[X38_CHANNELS]; 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic const struct x38_dev_info x38_devs[] = { 1428c2ecf20Sopenharmony_ci [X38] = { 1438c2ecf20Sopenharmony_ci .ctl_name = "x38"}, 1448c2ecf20Sopenharmony_ci}; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic struct pci_dev *mci_pdev; 1478c2ecf20Sopenharmony_cistatic int x38_registered = 1; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic void x38_clear_error_info(struct mem_ctl_info *mci) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct pci_dev *pdev; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci pdev = to_pci_dev(mci->pdev); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* 1578c2ecf20Sopenharmony_ci * Clear any error bits. 1588c2ecf20Sopenharmony_ci * (Yes, we really clear bits by writing 1 to them.) 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_ci pci_write_bits16(pdev, X38_ERRSTS, X38_ERRSTS_BITS, 1618c2ecf20Sopenharmony_ci X38_ERRSTS_BITS); 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic void x38_get_and_clear_error_info(struct mem_ctl_info *mci, 1658c2ecf20Sopenharmony_ci struct x38_error_info *info) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct pci_dev *pdev; 1688c2ecf20Sopenharmony_ci void __iomem *window = mci->pvt_info; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci pdev = to_pci_dev(mci->pdev); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* 1738c2ecf20Sopenharmony_ci * This is a mess because there is no atomic way to read all the 1748c2ecf20Sopenharmony_ci * registers at once and the registers can transition from CE being 1758c2ecf20Sopenharmony_ci * overwritten by UE. 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_ci pci_read_config_word(pdev, X38_ERRSTS, &info->errsts); 1788c2ecf20Sopenharmony_ci if (!(info->errsts & X38_ERRSTS_BITS)) 1798c2ecf20Sopenharmony_ci return; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci info->eccerrlog[0] = lo_hi_readq(window + X38_C0ECCERRLOG); 1828c2ecf20Sopenharmony_ci if (x38_channel_num == 2) 1838c2ecf20Sopenharmony_ci info->eccerrlog[1] = lo_hi_readq(window + X38_C1ECCERRLOG); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci pci_read_config_word(pdev, X38_ERRSTS, &info->errsts2); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* 1888c2ecf20Sopenharmony_ci * If the error is the same for both reads then the first set 1898c2ecf20Sopenharmony_ci * of reads is valid. If there is a change then there is a CE 1908c2ecf20Sopenharmony_ci * with no info and the second set of reads is valid and 1918c2ecf20Sopenharmony_ci * should be UE info. 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_ci if ((info->errsts ^ info->errsts2) & X38_ERRSTS_BITS) { 1948c2ecf20Sopenharmony_ci info->eccerrlog[0] = lo_hi_readq(window + X38_C0ECCERRLOG); 1958c2ecf20Sopenharmony_ci if (x38_channel_num == 2) 1968c2ecf20Sopenharmony_ci info->eccerrlog[1] = 1978c2ecf20Sopenharmony_ci lo_hi_readq(window + X38_C1ECCERRLOG); 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci x38_clear_error_info(mci); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic void x38_process_error_info(struct mem_ctl_info *mci, 2048c2ecf20Sopenharmony_ci struct x38_error_info *info) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci int channel; 2078c2ecf20Sopenharmony_ci u64 log; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (!(info->errsts & X38_ERRSTS_BITS)) 2108c2ecf20Sopenharmony_ci return; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if ((info->errsts ^ info->errsts2) & X38_ERRSTS_BITS) { 2138c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, 2148c2ecf20Sopenharmony_ci -1, -1, -1, 2158c2ecf20Sopenharmony_ci "UE overwrote CE", ""); 2168c2ecf20Sopenharmony_ci info->errsts = info->errsts2; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci for (channel = 0; channel < x38_channel_num; channel++) { 2208c2ecf20Sopenharmony_ci log = info->eccerrlog[channel]; 2218c2ecf20Sopenharmony_ci if (log & X38_ECCERRLOG_UE) { 2228c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 2238c2ecf20Sopenharmony_ci 0, 0, 0, 2248c2ecf20Sopenharmony_ci eccerrlog_row(channel, log), 2258c2ecf20Sopenharmony_ci -1, -1, 2268c2ecf20Sopenharmony_ci "x38 UE", ""); 2278c2ecf20Sopenharmony_ci } else if (log & X38_ECCERRLOG_CE) { 2288c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 2298c2ecf20Sopenharmony_ci 0, 0, eccerrlog_syndrome(log), 2308c2ecf20Sopenharmony_ci eccerrlog_row(channel, log), 2318c2ecf20Sopenharmony_ci -1, -1, 2328c2ecf20Sopenharmony_ci "x38 CE", ""); 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic void x38_check(struct mem_ctl_info *mci) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci struct x38_error_info info; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci edac_dbg(1, "MC%d\n", mci->mc_idx); 2428c2ecf20Sopenharmony_ci x38_get_and_clear_error_info(mci, &info); 2438c2ecf20Sopenharmony_ci x38_process_error_info(mci, &info); 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic void __iomem *x38_map_mchbar(struct pci_dev *pdev) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci union { 2498c2ecf20Sopenharmony_ci u64 mchbar; 2508c2ecf20Sopenharmony_ci struct { 2518c2ecf20Sopenharmony_ci u32 mchbar_low; 2528c2ecf20Sopenharmony_ci u32 mchbar_high; 2538c2ecf20Sopenharmony_ci }; 2548c2ecf20Sopenharmony_ci } u; 2558c2ecf20Sopenharmony_ci void __iomem *window; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, X38_MCHBAR_LOW, &u.mchbar_low); 2588c2ecf20Sopenharmony_ci pci_write_config_dword(pdev, X38_MCHBAR_LOW, u.mchbar_low | 0x1); 2598c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, X38_MCHBAR_HIGH, &u.mchbar_high); 2608c2ecf20Sopenharmony_ci u.mchbar &= X38_MCHBAR_MASK; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (u.mchbar != (resource_size_t)u.mchbar) { 2638c2ecf20Sopenharmony_ci printk(KERN_ERR 2648c2ecf20Sopenharmony_ci "x38: mmio space beyond accessible range (0x%llx)\n", 2658c2ecf20Sopenharmony_ci (unsigned long long)u.mchbar); 2668c2ecf20Sopenharmony_ci return NULL; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci window = ioremap(u.mchbar, X38_MMR_WINDOW_SIZE); 2708c2ecf20Sopenharmony_ci if (!window) 2718c2ecf20Sopenharmony_ci printk(KERN_ERR "x38: cannot map mmio space at 0x%llx\n", 2728c2ecf20Sopenharmony_ci (unsigned long long)u.mchbar); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return window; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic void x38_get_drbs(void __iomem *window, 2798c2ecf20Sopenharmony_ci u16 drbs[X38_CHANNELS][X38_RANKS_PER_CHANNEL]) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci int i; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci for (i = 0; i < X38_RANKS_PER_CHANNEL; i++) { 2848c2ecf20Sopenharmony_ci drbs[0][i] = readw(window + X38_C0DRB + 2*i) & X38_DRB_MASK; 2858c2ecf20Sopenharmony_ci drbs[1][i] = readw(window + X38_C1DRB + 2*i) & X38_DRB_MASK; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic bool x38_is_stacked(struct pci_dev *pdev, 2908c2ecf20Sopenharmony_ci u16 drbs[X38_CHANNELS][X38_RANKS_PER_CHANNEL]) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci u16 tom; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci pci_read_config_word(pdev, X38_TOM, &tom); 2958c2ecf20Sopenharmony_ci tom &= X38_TOM_MASK; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return drbs[X38_CHANNELS - 1][X38_RANKS_PER_CHANNEL - 1] == tom; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic unsigned long drb_to_nr_pages( 3018c2ecf20Sopenharmony_ci u16 drbs[X38_CHANNELS][X38_RANKS_PER_CHANNEL], 3028c2ecf20Sopenharmony_ci bool stacked, int channel, int rank) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci int n; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci n = drbs[channel][rank]; 3078c2ecf20Sopenharmony_ci if (rank > 0) 3088c2ecf20Sopenharmony_ci n -= drbs[channel][rank - 1]; 3098c2ecf20Sopenharmony_ci if (stacked && (channel == 1) && drbs[channel][rank] == 3108c2ecf20Sopenharmony_ci drbs[channel][X38_RANKS_PER_CHANNEL - 1]) { 3118c2ecf20Sopenharmony_ci n -= drbs[0][X38_RANKS_PER_CHANNEL - 1]; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci n <<= (X38_DRB_SHIFT - PAGE_SHIFT); 3158c2ecf20Sopenharmony_ci return n; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic int x38_probe1(struct pci_dev *pdev, int dev_idx) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci int rc; 3218c2ecf20Sopenharmony_ci int i, j; 3228c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = NULL; 3238c2ecf20Sopenharmony_ci struct edac_mc_layer layers[2]; 3248c2ecf20Sopenharmony_ci u16 drbs[X38_CHANNELS][X38_RANKS_PER_CHANNEL]; 3258c2ecf20Sopenharmony_ci bool stacked; 3268c2ecf20Sopenharmony_ci void __iomem *window; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci edac_dbg(0, "MC:\n"); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci window = x38_map_mchbar(pdev); 3318c2ecf20Sopenharmony_ci if (!window) 3328c2ecf20Sopenharmony_ci return -ENODEV; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci x38_get_drbs(window, drbs); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci how_many_channel(pdev); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci /* FIXME: unconventional pvt_info usage */ 3398c2ecf20Sopenharmony_ci layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; 3408c2ecf20Sopenharmony_ci layers[0].size = X38_RANKS; 3418c2ecf20Sopenharmony_ci layers[0].is_virt_csrow = true; 3428c2ecf20Sopenharmony_ci layers[1].type = EDAC_MC_LAYER_CHANNEL; 3438c2ecf20Sopenharmony_ci layers[1].size = x38_channel_num; 3448c2ecf20Sopenharmony_ci layers[1].is_virt_csrow = false; 3458c2ecf20Sopenharmony_ci mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 0); 3468c2ecf20Sopenharmony_ci if (!mci) 3478c2ecf20Sopenharmony_ci return -ENOMEM; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci edac_dbg(3, "MC: init mci\n"); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci mci->pdev = &pdev->dev; 3528c2ecf20Sopenharmony_ci mci->mtype_cap = MEM_FLAG_DDR2; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci mci->edac_ctl_cap = EDAC_FLAG_SECDED; 3558c2ecf20Sopenharmony_ci mci->edac_cap = EDAC_FLAG_SECDED; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci mci->mod_name = EDAC_MOD_STR; 3588c2ecf20Sopenharmony_ci mci->ctl_name = x38_devs[dev_idx].ctl_name; 3598c2ecf20Sopenharmony_ci mci->dev_name = pci_name(pdev); 3608c2ecf20Sopenharmony_ci mci->edac_check = x38_check; 3618c2ecf20Sopenharmony_ci mci->ctl_page_to_phys = NULL; 3628c2ecf20Sopenharmony_ci mci->pvt_info = window; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci stacked = x38_is_stacked(pdev, drbs); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci /* 3678c2ecf20Sopenharmony_ci * The dram rank boundary (DRB) reg values are boundary addresses 3688c2ecf20Sopenharmony_ci * for each DRAM rank with a granularity of 64MB. DRB regs are 3698c2ecf20Sopenharmony_ci * cumulative; the last one will contain the total memory 3708c2ecf20Sopenharmony_ci * contained in all ranks. 3718c2ecf20Sopenharmony_ci */ 3728c2ecf20Sopenharmony_ci for (i = 0; i < mci->nr_csrows; i++) { 3738c2ecf20Sopenharmony_ci unsigned long nr_pages; 3748c2ecf20Sopenharmony_ci struct csrow_info *csrow = mci->csrows[i]; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci nr_pages = drb_to_nr_pages(drbs, stacked, 3778c2ecf20Sopenharmony_ci i / X38_RANKS_PER_CHANNEL, 3788c2ecf20Sopenharmony_ci i % X38_RANKS_PER_CHANNEL); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (nr_pages == 0) 3818c2ecf20Sopenharmony_ci continue; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci for (j = 0; j < x38_channel_num; j++) { 3848c2ecf20Sopenharmony_ci struct dimm_info *dimm = csrow->channels[j]->dimm; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci dimm->nr_pages = nr_pages / x38_channel_num; 3878c2ecf20Sopenharmony_ci dimm->grain = nr_pages << PAGE_SHIFT; 3888c2ecf20Sopenharmony_ci dimm->mtype = MEM_DDR2; 3898c2ecf20Sopenharmony_ci dimm->dtype = DEV_UNKNOWN; 3908c2ecf20Sopenharmony_ci dimm->edac_mode = EDAC_UNKNOWN; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci x38_clear_error_info(mci); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci rc = -ENODEV; 3978c2ecf20Sopenharmony_ci if (edac_mc_add_mc(mci)) { 3988c2ecf20Sopenharmony_ci edac_dbg(3, "MC: failed edac_mc_add_mc()\n"); 3998c2ecf20Sopenharmony_ci goto fail; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* get this far and it's successful */ 4038c2ecf20Sopenharmony_ci edac_dbg(3, "MC: success\n"); 4048c2ecf20Sopenharmony_ci return 0; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cifail: 4078c2ecf20Sopenharmony_ci iounmap(window); 4088c2ecf20Sopenharmony_ci if (mci) 4098c2ecf20Sopenharmony_ci edac_mc_free(mci); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci return rc; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic int x38_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci int rc; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci edac_dbg(0, "MC:\n"); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (pci_enable_device(pdev) < 0) 4218c2ecf20Sopenharmony_ci return -EIO; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci rc = x38_probe1(pdev, ent->driver_data); 4248c2ecf20Sopenharmony_ci if (!mci_pdev) 4258c2ecf20Sopenharmony_ci mci_pdev = pci_dev_get(pdev); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci return rc; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic void x38_remove_one(struct pci_dev *pdev) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci edac_dbg(0, "\n"); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci mci = edac_mc_del_mc(&pdev->dev); 4378c2ecf20Sopenharmony_ci if (!mci) 4388c2ecf20Sopenharmony_ci return; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci iounmap(mci->pvt_info); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci edac_mc_free(mci); 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic const struct pci_device_id x38_pci_tbl[] = { 4468c2ecf20Sopenharmony_ci { 4478c2ecf20Sopenharmony_ci PCI_VEND_DEV(INTEL, X38_HB), PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4488c2ecf20Sopenharmony_ci X38}, 4498c2ecf20Sopenharmony_ci { 4508c2ecf20Sopenharmony_ci 0, 4518c2ecf20Sopenharmony_ci } /* 0 terminated list. */ 4528c2ecf20Sopenharmony_ci}; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, x38_pci_tbl); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic struct pci_driver x38_driver = { 4578c2ecf20Sopenharmony_ci .name = EDAC_MOD_STR, 4588c2ecf20Sopenharmony_ci .probe = x38_init_one, 4598c2ecf20Sopenharmony_ci .remove = x38_remove_one, 4608c2ecf20Sopenharmony_ci .id_table = x38_pci_tbl, 4618c2ecf20Sopenharmony_ci}; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic int __init x38_init(void) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci int pci_rc; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci edac_dbg(3, "MC:\n"); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci /* Ensure that the OPSTATE is set correctly for POLL or NMI */ 4708c2ecf20Sopenharmony_ci opstate_init(); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci pci_rc = pci_register_driver(&x38_driver); 4738c2ecf20Sopenharmony_ci if (pci_rc < 0) 4748c2ecf20Sopenharmony_ci goto fail0; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (!mci_pdev) { 4778c2ecf20Sopenharmony_ci x38_registered = 0; 4788c2ecf20Sopenharmony_ci mci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 4798c2ecf20Sopenharmony_ci PCI_DEVICE_ID_INTEL_X38_HB, NULL); 4808c2ecf20Sopenharmony_ci if (!mci_pdev) { 4818c2ecf20Sopenharmony_ci edac_dbg(0, "x38 pci_get_device fail\n"); 4828c2ecf20Sopenharmony_ci pci_rc = -ENODEV; 4838c2ecf20Sopenharmony_ci goto fail1; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci pci_rc = x38_init_one(mci_pdev, x38_pci_tbl); 4878c2ecf20Sopenharmony_ci if (pci_rc < 0) { 4888c2ecf20Sopenharmony_ci edac_dbg(0, "x38 init fail\n"); 4898c2ecf20Sopenharmony_ci pci_rc = -ENODEV; 4908c2ecf20Sopenharmony_ci goto fail1; 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci return 0; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cifail1: 4978c2ecf20Sopenharmony_ci pci_unregister_driver(&x38_driver); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_cifail0: 5008c2ecf20Sopenharmony_ci pci_dev_put(mci_pdev); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci return pci_rc; 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic void __exit x38_exit(void) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci edac_dbg(3, "MC:\n"); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci pci_unregister_driver(&x38_driver); 5108c2ecf20Sopenharmony_ci if (!x38_registered) { 5118c2ecf20Sopenharmony_ci x38_remove_one(mci_pdev); 5128c2ecf20Sopenharmony_ci pci_dev_put(mci_pdev); 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cimodule_init(x38_init); 5178c2ecf20Sopenharmony_cimodule_exit(x38_exit); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5208c2ecf20Sopenharmony_ciMODULE_AUTHOR("Cluster Computing, Inc. Hitoshi Mitake"); 5218c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MC support for Intel X38 memory hub controllers"); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_cimodule_param(edac_op_state, int, 0444); 5248c2ecf20Sopenharmony_ciMODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI"); 525