18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Intel 3200/3210 Memory Controller kernel module 38c2ecf20Sopenharmony_ci * Copyright (C) 2008-2009 Akamai Technologies, Inc. 48c2ecf20Sopenharmony_ci * Portions by Hitoshi Mitake <h.mitake@gmail.com>. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This file may be distributed under the terms of the 78c2ecf20Sopenharmony_ci * GNU General Public License. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/pci.h> 138c2ecf20Sopenharmony_ci#include <linux/pci_ids.h> 148c2ecf20Sopenharmony_ci#include <linux/edac.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include "edac_module.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/io-64-nonatomic-lo-hi.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define EDAC_MOD_STR "i3200_edac" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_3200_HB 0x29f0 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define I3200_DIMMS 4 258c2ecf20Sopenharmony_ci#define I3200_RANKS 8 268c2ecf20Sopenharmony_ci#define I3200_RANKS_PER_CHANNEL 4 278c2ecf20Sopenharmony_ci#define I3200_CHANNELS 2 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* Intel 3200 register addresses - device 0 function 0 - DRAM Controller */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define I3200_MCHBAR_LOW 0x48 /* MCH Memory Mapped Register BAR */ 328c2ecf20Sopenharmony_ci#define I3200_MCHBAR_HIGH 0x4c 338c2ecf20Sopenharmony_ci#define I3200_MCHBAR_MASK 0xfffffc000ULL /* bits 35:14 */ 348c2ecf20Sopenharmony_ci#define I3200_MMR_WINDOW_SIZE 16384 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define I3200_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 I3200_TOM_MASK 0x3ff /* bits 9:0 */ 428c2ecf20Sopenharmony_ci#define I3200_TOM_SHIFT 26 /* 64MiB grain */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define I3200_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 I3200_ERRSTS_UE 0x0002 638c2ecf20Sopenharmony_ci#define I3200_ERRSTS_CE 0x0001 648c2ecf20Sopenharmony_ci#define I3200_ERRSTS_BITS (I3200_ERRSTS_UE | I3200_ERRSTS_CE) 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* Intel MMIO register space - device 0 function 0 - MMR space */ 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define I3200_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 I3200_C1DRB 0x600 /* Channel 1 DRAM Rank Boundary (16b x 4) */ 758c2ecf20Sopenharmony_ci#define I3200_DRB_MASK 0x3ff /* bits 9:0 */ 768c2ecf20Sopenharmony_ci#define I3200_DRB_SHIFT 26 /* 64MiB grain */ 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#define I3200_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 I3200_C1ECCERRLOG 0x680 /* Chan 1 ECC Error Log (64b) */ 918c2ecf20Sopenharmony_ci#define I3200_ECCERRLOG_CE 0x1 928c2ecf20Sopenharmony_ci#define I3200_ECCERRLOG_UE 0x2 938c2ecf20Sopenharmony_ci#define I3200_ECCERRLOG_RANK_BITS 0x18000000 948c2ecf20Sopenharmony_ci#define I3200_ECCERRLOG_RANK_SHIFT 27 958c2ecf20Sopenharmony_ci#define I3200_ECCERRLOG_SYNDROME_BITS 0xff0000 968c2ecf20Sopenharmony_ci#define I3200_ECCERRLOG_SYNDROME_SHIFT 16 978c2ecf20Sopenharmony_ci#define I3200_CAPID0 0xe0 /* P.95 of spec for details */ 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistruct i3200_priv { 1008c2ecf20Sopenharmony_ci void __iomem *window; 1018c2ecf20Sopenharmony_ci}; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int nr_channels; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int how_many_channels(struct pci_dev *pdev) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci int n_channels; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci unsigned char capid0_8b; /* 8th byte of CAPID0 */ 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, I3200_CAPID0 + 8, &capid0_8b); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (capid0_8b & 0x20) { /* check DCD: Dual Channel Disable */ 1148c2ecf20Sopenharmony_ci edac_dbg(0, "In single channel mode\n"); 1158c2ecf20Sopenharmony_ci n_channels = 1; 1168c2ecf20Sopenharmony_ci } else { 1178c2ecf20Sopenharmony_ci edac_dbg(0, "In dual channel mode\n"); 1188c2ecf20Sopenharmony_ci n_channels = 2; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (capid0_8b & 0x10) /* check if both channels are filled */ 1228c2ecf20Sopenharmony_ci edac_dbg(0, "2 DIMMS per channel disabled\n"); 1238c2ecf20Sopenharmony_ci else 1248c2ecf20Sopenharmony_ci edac_dbg(0, "2 DIMMS per channel enabled\n"); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return n_channels; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic unsigned long eccerrlog_syndrome(u64 log) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci return (log & I3200_ECCERRLOG_SYNDROME_BITS) >> 1328c2ecf20Sopenharmony_ci I3200_ECCERRLOG_SYNDROME_SHIFT; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic int eccerrlog_row(int channel, u64 log) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci u64 rank = ((log & I3200_ECCERRLOG_RANK_BITS) >> 1388c2ecf20Sopenharmony_ci I3200_ECCERRLOG_RANK_SHIFT); 1398c2ecf20Sopenharmony_ci return rank | (channel * I3200_RANKS_PER_CHANNEL); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cienum i3200_chips { 1438c2ecf20Sopenharmony_ci I3200 = 0, 1448c2ecf20Sopenharmony_ci}; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistruct i3200_dev_info { 1478c2ecf20Sopenharmony_ci const char *ctl_name; 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistruct i3200_error_info { 1518c2ecf20Sopenharmony_ci u16 errsts; 1528c2ecf20Sopenharmony_ci u16 errsts2; 1538c2ecf20Sopenharmony_ci u64 eccerrlog[I3200_CHANNELS]; 1548c2ecf20Sopenharmony_ci}; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic const struct i3200_dev_info i3200_devs[] = { 1578c2ecf20Sopenharmony_ci [I3200] = { 1588c2ecf20Sopenharmony_ci .ctl_name = "i3200" 1598c2ecf20Sopenharmony_ci }, 1608c2ecf20Sopenharmony_ci}; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic struct pci_dev *mci_pdev; 1638c2ecf20Sopenharmony_cistatic int i3200_registered = 1; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic void i3200_clear_error_info(struct mem_ctl_info *mci) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct pci_dev *pdev; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci pdev = to_pci_dev(mci->pdev); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* 1738c2ecf20Sopenharmony_ci * Clear any error bits. 1748c2ecf20Sopenharmony_ci * (Yes, we really clear bits by writing 1 to them.) 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_ci pci_write_bits16(pdev, I3200_ERRSTS, I3200_ERRSTS_BITS, 1778c2ecf20Sopenharmony_ci I3200_ERRSTS_BITS); 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic void i3200_get_and_clear_error_info(struct mem_ctl_info *mci, 1818c2ecf20Sopenharmony_ci struct i3200_error_info *info) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct pci_dev *pdev; 1848c2ecf20Sopenharmony_ci struct i3200_priv *priv = mci->pvt_info; 1858c2ecf20Sopenharmony_ci void __iomem *window = priv->window; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci pdev = to_pci_dev(mci->pdev); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* 1908c2ecf20Sopenharmony_ci * This is a mess because there is no atomic way to read all the 1918c2ecf20Sopenharmony_ci * registers at once and the registers can transition from CE being 1928c2ecf20Sopenharmony_ci * overwritten by UE. 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci pci_read_config_word(pdev, I3200_ERRSTS, &info->errsts); 1958c2ecf20Sopenharmony_ci if (!(info->errsts & I3200_ERRSTS_BITS)) 1968c2ecf20Sopenharmony_ci return; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci info->eccerrlog[0] = readq(window + I3200_C0ECCERRLOG); 1998c2ecf20Sopenharmony_ci if (nr_channels == 2) 2008c2ecf20Sopenharmony_ci info->eccerrlog[1] = readq(window + I3200_C1ECCERRLOG); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci pci_read_config_word(pdev, I3200_ERRSTS, &info->errsts2); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* 2058c2ecf20Sopenharmony_ci * If the error is the same for both reads then the first set 2068c2ecf20Sopenharmony_ci * of reads is valid. If there is a change then there is a CE 2078c2ecf20Sopenharmony_ci * with no info and the second set of reads is valid and 2088c2ecf20Sopenharmony_ci * should be UE info. 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_ci if ((info->errsts ^ info->errsts2) & I3200_ERRSTS_BITS) { 2118c2ecf20Sopenharmony_ci info->eccerrlog[0] = readq(window + I3200_C0ECCERRLOG); 2128c2ecf20Sopenharmony_ci if (nr_channels == 2) 2138c2ecf20Sopenharmony_ci info->eccerrlog[1] = readq(window + I3200_C1ECCERRLOG); 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci i3200_clear_error_info(mci); 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic void i3200_process_error_info(struct mem_ctl_info *mci, 2208c2ecf20Sopenharmony_ci struct i3200_error_info *info) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci int channel; 2238c2ecf20Sopenharmony_ci u64 log; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (!(info->errsts & I3200_ERRSTS_BITS)) 2268c2ecf20Sopenharmony_ci return; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if ((info->errsts ^ info->errsts2) & I3200_ERRSTS_BITS) { 2298c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, 2308c2ecf20Sopenharmony_ci -1, -1, -1, "UE overwrote CE", ""); 2318c2ecf20Sopenharmony_ci info->errsts = info->errsts2; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci for (channel = 0; channel < nr_channels; channel++) { 2358c2ecf20Sopenharmony_ci log = info->eccerrlog[channel]; 2368c2ecf20Sopenharmony_ci if (log & I3200_ECCERRLOG_UE) { 2378c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 2388c2ecf20Sopenharmony_ci 0, 0, 0, 2398c2ecf20Sopenharmony_ci eccerrlog_row(channel, log), 2408c2ecf20Sopenharmony_ci -1, -1, 2418c2ecf20Sopenharmony_ci "i3000 UE", ""); 2428c2ecf20Sopenharmony_ci } else if (log & I3200_ECCERRLOG_CE) { 2438c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 2448c2ecf20Sopenharmony_ci 0, 0, eccerrlog_syndrome(log), 2458c2ecf20Sopenharmony_ci eccerrlog_row(channel, log), 2468c2ecf20Sopenharmony_ci -1, -1, 2478c2ecf20Sopenharmony_ci "i3000 CE", ""); 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic void i3200_check(struct mem_ctl_info *mci) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci struct i3200_error_info info; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci edac_dbg(1, "MC%d\n", mci->mc_idx); 2578c2ecf20Sopenharmony_ci i3200_get_and_clear_error_info(mci, &info); 2588c2ecf20Sopenharmony_ci i3200_process_error_info(mci, &info); 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic void __iomem *i3200_map_mchbar(struct pci_dev *pdev) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci union { 2648c2ecf20Sopenharmony_ci u64 mchbar; 2658c2ecf20Sopenharmony_ci struct { 2668c2ecf20Sopenharmony_ci u32 mchbar_low; 2678c2ecf20Sopenharmony_ci u32 mchbar_high; 2688c2ecf20Sopenharmony_ci }; 2698c2ecf20Sopenharmony_ci } u; 2708c2ecf20Sopenharmony_ci void __iomem *window; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, I3200_MCHBAR_LOW, &u.mchbar_low); 2738c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, I3200_MCHBAR_HIGH, &u.mchbar_high); 2748c2ecf20Sopenharmony_ci u.mchbar &= I3200_MCHBAR_MASK; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (u.mchbar != (resource_size_t)u.mchbar) { 2778c2ecf20Sopenharmony_ci printk(KERN_ERR 2788c2ecf20Sopenharmony_ci "i3200: mmio space beyond accessible range (0x%llx)\n", 2798c2ecf20Sopenharmony_ci (unsigned long long)u.mchbar); 2808c2ecf20Sopenharmony_ci return NULL; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci window = ioremap(u.mchbar, I3200_MMR_WINDOW_SIZE); 2848c2ecf20Sopenharmony_ci if (!window) 2858c2ecf20Sopenharmony_ci printk(KERN_ERR "i3200: cannot map mmio space at 0x%llx\n", 2868c2ecf20Sopenharmony_ci (unsigned long long)u.mchbar); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return window; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic void i3200_get_drbs(void __iomem *window, 2938c2ecf20Sopenharmony_ci u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL]) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci int i; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci for (i = 0; i < I3200_RANKS_PER_CHANNEL; i++) { 2988c2ecf20Sopenharmony_ci drbs[0][i] = readw(window + I3200_C0DRB + 2*i) & I3200_DRB_MASK; 2998c2ecf20Sopenharmony_ci drbs[1][i] = readw(window + I3200_C1DRB + 2*i) & I3200_DRB_MASK; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci edac_dbg(0, "drb[0][%d] = %d, drb[1][%d] = %d\n", i, drbs[0][i], i, drbs[1][i]); 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic bool i3200_is_stacked(struct pci_dev *pdev, 3068c2ecf20Sopenharmony_ci u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL]) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci u16 tom; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci pci_read_config_word(pdev, I3200_TOM, &tom); 3118c2ecf20Sopenharmony_ci tom &= I3200_TOM_MASK; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci return drbs[I3200_CHANNELS - 1][I3200_RANKS_PER_CHANNEL - 1] == tom; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic unsigned long drb_to_nr_pages( 3178c2ecf20Sopenharmony_ci u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL], bool stacked, 3188c2ecf20Sopenharmony_ci int channel, int rank) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci int n; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci n = drbs[channel][rank]; 3238c2ecf20Sopenharmony_ci if (!n) 3248c2ecf20Sopenharmony_ci return 0; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (rank > 0) 3278c2ecf20Sopenharmony_ci n -= drbs[channel][rank - 1]; 3288c2ecf20Sopenharmony_ci if (stacked && (channel == 1) && 3298c2ecf20Sopenharmony_ci drbs[channel][rank] == drbs[channel][I3200_RANKS_PER_CHANNEL - 1]) 3308c2ecf20Sopenharmony_ci n -= drbs[0][I3200_RANKS_PER_CHANNEL - 1]; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci n <<= (I3200_DRB_SHIFT - PAGE_SHIFT); 3338c2ecf20Sopenharmony_ci return n; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic int i3200_probe1(struct pci_dev *pdev, int dev_idx) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci int rc; 3398c2ecf20Sopenharmony_ci int i, j; 3408c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = NULL; 3418c2ecf20Sopenharmony_ci struct edac_mc_layer layers[2]; 3428c2ecf20Sopenharmony_ci u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL]; 3438c2ecf20Sopenharmony_ci bool stacked; 3448c2ecf20Sopenharmony_ci void __iomem *window; 3458c2ecf20Sopenharmony_ci struct i3200_priv *priv; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci edac_dbg(0, "MC:\n"); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci window = i3200_map_mchbar(pdev); 3508c2ecf20Sopenharmony_ci if (!window) 3518c2ecf20Sopenharmony_ci return -ENODEV; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci i3200_get_drbs(window, drbs); 3548c2ecf20Sopenharmony_ci nr_channels = how_many_channels(pdev); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; 3578c2ecf20Sopenharmony_ci layers[0].size = I3200_DIMMS; 3588c2ecf20Sopenharmony_ci layers[0].is_virt_csrow = true; 3598c2ecf20Sopenharmony_ci layers[1].type = EDAC_MC_LAYER_CHANNEL; 3608c2ecf20Sopenharmony_ci layers[1].size = nr_channels; 3618c2ecf20Sopenharmony_ci layers[1].is_virt_csrow = false; 3628c2ecf20Sopenharmony_ci mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 3638c2ecf20Sopenharmony_ci sizeof(struct i3200_priv)); 3648c2ecf20Sopenharmony_ci if (!mci) 3658c2ecf20Sopenharmony_ci return -ENOMEM; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci edac_dbg(3, "MC: init mci\n"); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci mci->pdev = &pdev->dev; 3708c2ecf20Sopenharmony_ci mci->mtype_cap = MEM_FLAG_DDR2; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci mci->edac_ctl_cap = EDAC_FLAG_SECDED; 3738c2ecf20Sopenharmony_ci mci->edac_cap = EDAC_FLAG_SECDED; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci mci->mod_name = EDAC_MOD_STR; 3768c2ecf20Sopenharmony_ci mci->ctl_name = i3200_devs[dev_idx].ctl_name; 3778c2ecf20Sopenharmony_ci mci->dev_name = pci_name(pdev); 3788c2ecf20Sopenharmony_ci mci->edac_check = i3200_check; 3798c2ecf20Sopenharmony_ci mci->ctl_page_to_phys = NULL; 3808c2ecf20Sopenharmony_ci priv = mci->pvt_info; 3818c2ecf20Sopenharmony_ci priv->window = window; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci stacked = i3200_is_stacked(pdev, drbs); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci /* 3868c2ecf20Sopenharmony_ci * The dram rank boundary (DRB) reg values are boundary addresses 3878c2ecf20Sopenharmony_ci * for each DRAM rank with a granularity of 64MB. DRB regs are 3888c2ecf20Sopenharmony_ci * cumulative; the last one will contain the total memory 3898c2ecf20Sopenharmony_ci * contained in all ranks. 3908c2ecf20Sopenharmony_ci */ 3918c2ecf20Sopenharmony_ci for (i = 0; i < I3200_DIMMS; i++) { 3928c2ecf20Sopenharmony_ci unsigned long nr_pages; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci for (j = 0; j < nr_channels; j++) { 3958c2ecf20Sopenharmony_ci struct dimm_info *dimm = edac_get_dimm(mci, i, j, 0); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci nr_pages = drb_to_nr_pages(drbs, stacked, j, i); 3988c2ecf20Sopenharmony_ci if (nr_pages == 0) 3998c2ecf20Sopenharmony_ci continue; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci edac_dbg(0, "csrow %d, channel %d%s, size = %ld MiB\n", i, j, 4028c2ecf20Sopenharmony_ci stacked ? " (stacked)" : "", PAGES_TO_MiB(nr_pages)); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci dimm->nr_pages = nr_pages; 4058c2ecf20Sopenharmony_ci dimm->grain = nr_pages << PAGE_SHIFT; 4068c2ecf20Sopenharmony_ci dimm->mtype = MEM_DDR2; 4078c2ecf20Sopenharmony_ci dimm->dtype = DEV_UNKNOWN; 4088c2ecf20Sopenharmony_ci dimm->edac_mode = EDAC_UNKNOWN; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci i3200_clear_error_info(mci); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci rc = -ENODEV; 4158c2ecf20Sopenharmony_ci if (edac_mc_add_mc(mci)) { 4168c2ecf20Sopenharmony_ci edac_dbg(3, "MC: failed edac_mc_add_mc()\n"); 4178c2ecf20Sopenharmony_ci goto fail; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* get this far and it's successful */ 4218c2ecf20Sopenharmony_ci edac_dbg(3, "MC: success\n"); 4228c2ecf20Sopenharmony_ci return 0; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cifail: 4258c2ecf20Sopenharmony_ci iounmap(window); 4268c2ecf20Sopenharmony_ci if (mci) 4278c2ecf20Sopenharmony_ci edac_mc_free(mci); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci return rc; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic int i3200_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci int rc; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci edac_dbg(0, "MC:\n"); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (pci_enable_device(pdev) < 0) 4398c2ecf20Sopenharmony_ci return -EIO; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci rc = i3200_probe1(pdev, ent->driver_data); 4428c2ecf20Sopenharmony_ci if (!mci_pdev) 4438c2ecf20Sopenharmony_ci mci_pdev = pci_dev_get(pdev); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci return rc; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic void i3200_remove_one(struct pci_dev *pdev) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 4518c2ecf20Sopenharmony_ci struct i3200_priv *priv; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci edac_dbg(0, "\n"); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci mci = edac_mc_del_mc(&pdev->dev); 4568c2ecf20Sopenharmony_ci if (!mci) 4578c2ecf20Sopenharmony_ci return; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci priv = mci->pvt_info; 4608c2ecf20Sopenharmony_ci iounmap(priv->window); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci edac_mc_free(mci); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci pci_disable_device(pdev); 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic const struct pci_device_id i3200_pci_tbl[] = { 4688c2ecf20Sopenharmony_ci { 4698c2ecf20Sopenharmony_ci PCI_VEND_DEV(INTEL, 3200_HB), PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4708c2ecf20Sopenharmony_ci I3200}, 4718c2ecf20Sopenharmony_ci { 4728c2ecf20Sopenharmony_ci 0, 4738c2ecf20Sopenharmony_ci } /* 0 terminated list. */ 4748c2ecf20Sopenharmony_ci}; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, i3200_pci_tbl); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_cistatic struct pci_driver i3200_driver = { 4798c2ecf20Sopenharmony_ci .name = EDAC_MOD_STR, 4808c2ecf20Sopenharmony_ci .probe = i3200_init_one, 4818c2ecf20Sopenharmony_ci .remove = i3200_remove_one, 4828c2ecf20Sopenharmony_ci .id_table = i3200_pci_tbl, 4838c2ecf20Sopenharmony_ci}; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic int __init i3200_init(void) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci int pci_rc; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci edac_dbg(3, "MC:\n"); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* Ensure that the OPSTATE is set correctly for POLL or NMI */ 4928c2ecf20Sopenharmony_ci opstate_init(); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci pci_rc = pci_register_driver(&i3200_driver); 4958c2ecf20Sopenharmony_ci if (pci_rc < 0) 4968c2ecf20Sopenharmony_ci goto fail0; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (!mci_pdev) { 4998c2ecf20Sopenharmony_ci i3200_registered = 0; 5008c2ecf20Sopenharmony_ci mci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 5018c2ecf20Sopenharmony_ci PCI_DEVICE_ID_INTEL_3200_HB, NULL); 5028c2ecf20Sopenharmony_ci if (!mci_pdev) { 5038c2ecf20Sopenharmony_ci edac_dbg(0, "i3200 pci_get_device fail\n"); 5048c2ecf20Sopenharmony_ci pci_rc = -ENODEV; 5058c2ecf20Sopenharmony_ci goto fail1; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci pci_rc = i3200_init_one(mci_pdev, i3200_pci_tbl); 5098c2ecf20Sopenharmony_ci if (pci_rc < 0) { 5108c2ecf20Sopenharmony_ci edac_dbg(0, "i3200 init fail\n"); 5118c2ecf20Sopenharmony_ci pci_rc = -ENODEV; 5128c2ecf20Sopenharmony_ci goto fail1; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci return 0; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_cifail1: 5198c2ecf20Sopenharmony_ci pci_unregister_driver(&i3200_driver); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cifail0: 5228c2ecf20Sopenharmony_ci pci_dev_put(mci_pdev); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci return pci_rc; 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic void __exit i3200_exit(void) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci edac_dbg(3, "MC:\n"); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci pci_unregister_driver(&i3200_driver); 5328c2ecf20Sopenharmony_ci if (!i3200_registered) { 5338c2ecf20Sopenharmony_ci i3200_remove_one(mci_pdev); 5348c2ecf20Sopenharmony_ci pci_dev_put(mci_pdev); 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cimodule_init(i3200_init); 5398c2ecf20Sopenharmony_cimodule_exit(i3200_exit); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5428c2ecf20Sopenharmony_ciMODULE_AUTHOR("Akamai Technologies, Inc."); 5438c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MC support for Intel 3200 memory hub controllers"); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cimodule_param(edac_op_state, int, 0444); 5468c2ecf20Sopenharmony_ciMODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI"); 547