1dd8ef1dbSJason Uhlenkott /* 2dd8ef1dbSJason Uhlenkott * Intel 3200/3210 Memory Controller kernel module 3dd8ef1dbSJason Uhlenkott * Copyright (C) 2008-2009 Akamai Technologies, Inc. 4dd8ef1dbSJason Uhlenkott * Portions by Hitoshi Mitake <h.mitake@gmail.com>. 5dd8ef1dbSJason Uhlenkott * 6dd8ef1dbSJason Uhlenkott * This file may be distributed under the terms of the 7dd8ef1dbSJason Uhlenkott * GNU General Public License. 8dd8ef1dbSJason Uhlenkott */ 9dd8ef1dbSJason Uhlenkott 10dd8ef1dbSJason Uhlenkott #include <linux/module.h> 11dd8ef1dbSJason Uhlenkott #include <linux/init.h> 12dd8ef1dbSJason Uhlenkott #include <linux/pci.h> 13dd8ef1dbSJason Uhlenkott #include <linux/pci_ids.h> 14dd8ef1dbSJason Uhlenkott #include <linux/edac.h> 15dd8ef1dbSJason Uhlenkott #include <linux/io.h> 16dd8ef1dbSJason Uhlenkott #include "edac_core.h" 17dd8ef1dbSJason Uhlenkott 18797a796aSHitoshi Mitake #include <asm-generic/io-64-nonatomic-lo-hi.h> 19797a796aSHitoshi Mitake 20dd8ef1dbSJason Uhlenkott #define I3200_REVISION "1.1" 21dd8ef1dbSJason Uhlenkott 22dd8ef1dbSJason Uhlenkott #define EDAC_MOD_STR "i3200_edac" 23dd8ef1dbSJason Uhlenkott 24dd8ef1dbSJason Uhlenkott #define PCI_DEVICE_ID_INTEL_3200_HB 0x29f0 25dd8ef1dbSJason Uhlenkott 2695b93287SMauro Carvalho Chehab #define I3200_DIMMS 4 27dd8ef1dbSJason Uhlenkott #define I3200_RANKS 8 28dd8ef1dbSJason Uhlenkott #define I3200_RANKS_PER_CHANNEL 4 29dd8ef1dbSJason Uhlenkott #define I3200_CHANNELS 2 30dd8ef1dbSJason Uhlenkott 31dd8ef1dbSJason Uhlenkott /* Intel 3200 register addresses - device 0 function 0 - DRAM Controller */ 32dd8ef1dbSJason Uhlenkott 33dd8ef1dbSJason Uhlenkott #define I3200_MCHBAR_LOW 0x48 /* MCH Memory Mapped Register BAR */ 34dd8ef1dbSJason Uhlenkott #define I3200_MCHBAR_HIGH 0x4c 35dd8ef1dbSJason Uhlenkott #define I3200_MCHBAR_MASK 0xfffffc000ULL /* bits 35:14 */ 36dd8ef1dbSJason Uhlenkott #define I3200_MMR_WINDOW_SIZE 16384 37dd8ef1dbSJason Uhlenkott 38dd8ef1dbSJason Uhlenkott #define I3200_TOM 0xa0 /* Top of Memory (16b) 39dd8ef1dbSJason Uhlenkott * 40dd8ef1dbSJason Uhlenkott * 15:10 reserved 41dd8ef1dbSJason Uhlenkott * 9:0 total populated physical memory 42dd8ef1dbSJason Uhlenkott */ 43dd8ef1dbSJason Uhlenkott #define I3200_TOM_MASK 0x3ff /* bits 9:0 */ 44dd8ef1dbSJason Uhlenkott #define I3200_TOM_SHIFT 26 /* 64MiB grain */ 45dd8ef1dbSJason Uhlenkott 46dd8ef1dbSJason Uhlenkott #define I3200_ERRSTS 0xc8 /* Error Status Register (16b) 47dd8ef1dbSJason Uhlenkott * 48dd8ef1dbSJason Uhlenkott * 15 reserved 49dd8ef1dbSJason Uhlenkott * 14 Isochronous TBWRR Run Behind FIFO Full 50dd8ef1dbSJason Uhlenkott * (ITCV) 51dd8ef1dbSJason Uhlenkott * 13 Isochronous TBWRR Run Behind FIFO Put 52dd8ef1dbSJason Uhlenkott * (ITSTV) 53dd8ef1dbSJason Uhlenkott * 12 reserved 54dd8ef1dbSJason Uhlenkott * 11 MCH Thermal Sensor Event 55dd8ef1dbSJason Uhlenkott * for SMI/SCI/SERR (GTSE) 56dd8ef1dbSJason Uhlenkott * 10 reserved 57dd8ef1dbSJason Uhlenkott * 9 LOCK to non-DRAM Memory Flag (LCKF) 58dd8ef1dbSJason Uhlenkott * 8 reserved 59dd8ef1dbSJason Uhlenkott * 7 DRAM Throttle Flag (DTF) 60dd8ef1dbSJason Uhlenkott * 6:2 reserved 61dd8ef1dbSJason Uhlenkott * 1 Multi-bit DRAM ECC Error Flag (DMERR) 62dd8ef1dbSJason Uhlenkott * 0 Single-bit DRAM ECC Error Flag (DSERR) 63dd8ef1dbSJason Uhlenkott */ 64dd8ef1dbSJason Uhlenkott #define I3200_ERRSTS_UE 0x0002 65dd8ef1dbSJason Uhlenkott #define I3200_ERRSTS_CE 0x0001 66dd8ef1dbSJason Uhlenkott #define I3200_ERRSTS_BITS (I3200_ERRSTS_UE | I3200_ERRSTS_CE) 67dd8ef1dbSJason Uhlenkott 68dd8ef1dbSJason Uhlenkott 69dd8ef1dbSJason Uhlenkott /* Intel MMIO register space - device 0 function 0 - MMR space */ 70dd8ef1dbSJason Uhlenkott 71dd8ef1dbSJason Uhlenkott #define I3200_C0DRB 0x200 /* Channel 0 DRAM Rank Boundary (16b x 4) 72dd8ef1dbSJason Uhlenkott * 73dd8ef1dbSJason Uhlenkott * 15:10 reserved 74dd8ef1dbSJason Uhlenkott * 9:0 Channel 0 DRAM Rank Boundary Address 75dd8ef1dbSJason Uhlenkott */ 76dd8ef1dbSJason Uhlenkott #define I3200_C1DRB 0x600 /* Channel 1 DRAM Rank Boundary (16b x 4) */ 77dd8ef1dbSJason Uhlenkott #define I3200_DRB_MASK 0x3ff /* bits 9:0 */ 78dd8ef1dbSJason Uhlenkott #define I3200_DRB_SHIFT 26 /* 64MiB grain */ 79dd8ef1dbSJason Uhlenkott 80dd8ef1dbSJason Uhlenkott #define I3200_C0ECCERRLOG 0x280 /* Channel 0 ECC Error Log (64b) 81dd8ef1dbSJason Uhlenkott * 82dd8ef1dbSJason Uhlenkott * 63:48 Error Column Address (ERRCOL) 83dd8ef1dbSJason Uhlenkott * 47:32 Error Row Address (ERRROW) 84dd8ef1dbSJason Uhlenkott * 31:29 Error Bank Address (ERRBANK) 85dd8ef1dbSJason Uhlenkott * 28:27 Error Rank Address (ERRRANK) 86dd8ef1dbSJason Uhlenkott * 26:24 reserved 87dd8ef1dbSJason Uhlenkott * 23:16 Error Syndrome (ERRSYND) 88dd8ef1dbSJason Uhlenkott * 15: 2 reserved 89dd8ef1dbSJason Uhlenkott * 1 Multiple Bit Error Status (MERRSTS) 90dd8ef1dbSJason Uhlenkott * 0 Correctable Error Status (CERRSTS) 91dd8ef1dbSJason Uhlenkott */ 92dd8ef1dbSJason Uhlenkott #define I3200_C1ECCERRLOG 0x680 /* Chan 1 ECC Error Log (64b) */ 93dd8ef1dbSJason Uhlenkott #define I3200_ECCERRLOG_CE 0x1 94dd8ef1dbSJason Uhlenkott #define I3200_ECCERRLOG_UE 0x2 95dd8ef1dbSJason Uhlenkott #define I3200_ECCERRLOG_RANK_BITS 0x18000000 96dd8ef1dbSJason Uhlenkott #define I3200_ECCERRLOG_RANK_SHIFT 27 97dd8ef1dbSJason Uhlenkott #define I3200_ECCERRLOG_SYNDROME_BITS 0xff0000 98dd8ef1dbSJason Uhlenkott #define I3200_ECCERRLOG_SYNDROME_SHIFT 16 99dd8ef1dbSJason Uhlenkott #define I3200_CAPID0 0xe0 /* P.95 of spec for details */ 100dd8ef1dbSJason Uhlenkott 101dd8ef1dbSJason Uhlenkott struct i3200_priv { 102dd8ef1dbSJason Uhlenkott void __iomem *window; 103dd8ef1dbSJason Uhlenkott }; 104dd8ef1dbSJason Uhlenkott 105dd8ef1dbSJason Uhlenkott static int nr_channels; 106dd8ef1dbSJason Uhlenkott 107dd8ef1dbSJason Uhlenkott static int how_many_channels(struct pci_dev *pdev) 108dd8ef1dbSJason Uhlenkott { 109dd8ef1dbSJason Uhlenkott unsigned char capid0_8b; /* 8th byte of CAPID0 */ 110dd8ef1dbSJason Uhlenkott 111dd8ef1dbSJason Uhlenkott pci_read_config_byte(pdev, I3200_CAPID0 + 8, &capid0_8b); 112dd8ef1dbSJason Uhlenkott if (capid0_8b & 0x20) { /* check DCD: Dual Channel Disable */ 113956b9ba1SJoe Perches edac_dbg(0, "In single channel mode\n"); 114dd8ef1dbSJason Uhlenkott return 1; 115dd8ef1dbSJason Uhlenkott } else { 116956b9ba1SJoe Perches edac_dbg(0, "In dual channel mode\n"); 117dd8ef1dbSJason Uhlenkott return 2; 118dd8ef1dbSJason Uhlenkott } 119dd8ef1dbSJason Uhlenkott } 120dd8ef1dbSJason Uhlenkott 121dd8ef1dbSJason Uhlenkott static unsigned long eccerrlog_syndrome(u64 log) 122dd8ef1dbSJason Uhlenkott { 123dd8ef1dbSJason Uhlenkott return (log & I3200_ECCERRLOG_SYNDROME_BITS) >> 124dd8ef1dbSJason Uhlenkott I3200_ECCERRLOG_SYNDROME_SHIFT; 125dd8ef1dbSJason Uhlenkott } 126dd8ef1dbSJason Uhlenkott 127dd8ef1dbSJason Uhlenkott static int eccerrlog_row(int channel, u64 log) 128dd8ef1dbSJason Uhlenkott { 129dd8ef1dbSJason Uhlenkott u64 rank = ((log & I3200_ECCERRLOG_RANK_BITS) >> 130dd8ef1dbSJason Uhlenkott I3200_ECCERRLOG_RANK_SHIFT); 131dd8ef1dbSJason Uhlenkott return rank | (channel * I3200_RANKS_PER_CHANNEL); 132dd8ef1dbSJason Uhlenkott } 133dd8ef1dbSJason Uhlenkott 134dd8ef1dbSJason Uhlenkott enum i3200_chips { 135dd8ef1dbSJason Uhlenkott I3200 = 0, 136dd8ef1dbSJason Uhlenkott }; 137dd8ef1dbSJason Uhlenkott 138dd8ef1dbSJason Uhlenkott struct i3200_dev_info { 139dd8ef1dbSJason Uhlenkott const char *ctl_name; 140dd8ef1dbSJason Uhlenkott }; 141dd8ef1dbSJason Uhlenkott 142dd8ef1dbSJason Uhlenkott struct i3200_error_info { 143dd8ef1dbSJason Uhlenkott u16 errsts; 144dd8ef1dbSJason Uhlenkott u16 errsts2; 145dd8ef1dbSJason Uhlenkott u64 eccerrlog[I3200_CHANNELS]; 146dd8ef1dbSJason Uhlenkott }; 147dd8ef1dbSJason Uhlenkott 148dd8ef1dbSJason Uhlenkott static const struct i3200_dev_info i3200_devs[] = { 149dd8ef1dbSJason Uhlenkott [I3200] = { 150dd8ef1dbSJason Uhlenkott .ctl_name = "i3200" 151dd8ef1dbSJason Uhlenkott }, 152dd8ef1dbSJason Uhlenkott }; 153dd8ef1dbSJason Uhlenkott 154dd8ef1dbSJason Uhlenkott static struct pci_dev *mci_pdev; 155dd8ef1dbSJason Uhlenkott static int i3200_registered = 1; 156dd8ef1dbSJason Uhlenkott 157dd8ef1dbSJason Uhlenkott 158dd8ef1dbSJason Uhlenkott static void i3200_clear_error_info(struct mem_ctl_info *mci) 159dd8ef1dbSJason Uhlenkott { 160dd8ef1dbSJason Uhlenkott struct pci_dev *pdev; 161dd8ef1dbSJason Uhlenkott 162fd687502SMauro Carvalho Chehab pdev = to_pci_dev(mci->pdev); 163dd8ef1dbSJason Uhlenkott 164dd8ef1dbSJason Uhlenkott /* 165dd8ef1dbSJason Uhlenkott * Clear any error bits. 166dd8ef1dbSJason Uhlenkott * (Yes, we really clear bits by writing 1 to them.) 167dd8ef1dbSJason Uhlenkott */ 168dd8ef1dbSJason Uhlenkott pci_write_bits16(pdev, I3200_ERRSTS, I3200_ERRSTS_BITS, 169dd8ef1dbSJason Uhlenkott I3200_ERRSTS_BITS); 170dd8ef1dbSJason Uhlenkott } 171dd8ef1dbSJason Uhlenkott 172dd8ef1dbSJason Uhlenkott static void i3200_get_and_clear_error_info(struct mem_ctl_info *mci, 173dd8ef1dbSJason Uhlenkott struct i3200_error_info *info) 174dd8ef1dbSJason Uhlenkott { 175dd8ef1dbSJason Uhlenkott struct pci_dev *pdev; 176dd8ef1dbSJason Uhlenkott struct i3200_priv *priv = mci->pvt_info; 177dd8ef1dbSJason Uhlenkott void __iomem *window = priv->window; 178dd8ef1dbSJason Uhlenkott 179fd687502SMauro Carvalho Chehab pdev = to_pci_dev(mci->pdev); 180dd8ef1dbSJason Uhlenkott 181dd8ef1dbSJason Uhlenkott /* 182dd8ef1dbSJason Uhlenkott * This is a mess because there is no atomic way to read all the 183dd8ef1dbSJason Uhlenkott * registers at once and the registers can transition from CE being 184dd8ef1dbSJason Uhlenkott * overwritten by UE. 185dd8ef1dbSJason Uhlenkott */ 186dd8ef1dbSJason Uhlenkott pci_read_config_word(pdev, I3200_ERRSTS, &info->errsts); 187dd8ef1dbSJason Uhlenkott if (!(info->errsts & I3200_ERRSTS_BITS)) 188dd8ef1dbSJason Uhlenkott return; 189dd8ef1dbSJason Uhlenkott 190dd8ef1dbSJason Uhlenkott info->eccerrlog[0] = readq(window + I3200_C0ECCERRLOG); 191dd8ef1dbSJason Uhlenkott if (nr_channels == 2) 192dd8ef1dbSJason Uhlenkott info->eccerrlog[1] = readq(window + I3200_C1ECCERRLOG); 193dd8ef1dbSJason Uhlenkott 194dd8ef1dbSJason Uhlenkott pci_read_config_word(pdev, I3200_ERRSTS, &info->errsts2); 195dd8ef1dbSJason Uhlenkott 196dd8ef1dbSJason Uhlenkott /* 197dd8ef1dbSJason Uhlenkott * If the error is the same for both reads then the first set 198dd8ef1dbSJason Uhlenkott * of reads is valid. If there is a change then there is a CE 199dd8ef1dbSJason Uhlenkott * with no info and the second set of reads is valid and 200dd8ef1dbSJason Uhlenkott * should be UE info. 201dd8ef1dbSJason Uhlenkott */ 202dd8ef1dbSJason Uhlenkott if ((info->errsts ^ info->errsts2) & I3200_ERRSTS_BITS) { 203dd8ef1dbSJason Uhlenkott info->eccerrlog[0] = readq(window + I3200_C0ECCERRLOG); 204dd8ef1dbSJason Uhlenkott if (nr_channels == 2) 205dd8ef1dbSJason Uhlenkott info->eccerrlog[1] = readq(window + I3200_C1ECCERRLOG); 206dd8ef1dbSJason Uhlenkott } 207dd8ef1dbSJason Uhlenkott 208dd8ef1dbSJason Uhlenkott i3200_clear_error_info(mci); 209dd8ef1dbSJason Uhlenkott } 210dd8ef1dbSJason Uhlenkott 211dd8ef1dbSJason Uhlenkott static void i3200_process_error_info(struct mem_ctl_info *mci, 212dd8ef1dbSJason Uhlenkott struct i3200_error_info *info) 213dd8ef1dbSJason Uhlenkott { 214dd8ef1dbSJason Uhlenkott int channel; 215dd8ef1dbSJason Uhlenkott u64 log; 216dd8ef1dbSJason Uhlenkott 217dd8ef1dbSJason Uhlenkott if (!(info->errsts & I3200_ERRSTS_BITS)) 218dd8ef1dbSJason Uhlenkott return; 219dd8ef1dbSJason Uhlenkott 220dd8ef1dbSJason Uhlenkott if ((info->errsts ^ info->errsts2) & I3200_ERRSTS_BITS) { 2219eb07a7fSMauro Carvalho Chehab edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, 22203f7eae8SMauro Carvalho Chehab -1, -1, -1, "UE overwrote CE", ""); 223dd8ef1dbSJason Uhlenkott info->errsts = info->errsts2; 224dd8ef1dbSJason Uhlenkott } 225dd8ef1dbSJason Uhlenkott 226dd8ef1dbSJason Uhlenkott for (channel = 0; channel < nr_channels; channel++) { 227dd8ef1dbSJason Uhlenkott log = info->eccerrlog[channel]; 228dd8ef1dbSJason Uhlenkott if (log & I3200_ECCERRLOG_UE) { 2299eb07a7fSMauro Carvalho Chehab edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 23095b93287SMauro Carvalho Chehab 0, 0, 0, 231dd8ef1dbSJason Uhlenkott eccerrlog_row(channel, log), 23295b93287SMauro Carvalho Chehab -1, -1, 23303f7eae8SMauro Carvalho Chehab "i3000 UE", ""); 234dd8ef1dbSJason Uhlenkott } else if (log & I3200_ECCERRLOG_CE) { 2359eb07a7fSMauro Carvalho Chehab edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 23695b93287SMauro Carvalho Chehab 0, 0, eccerrlog_syndrome(log), 23795b93287SMauro Carvalho Chehab eccerrlog_row(channel, log), 23895b93287SMauro Carvalho Chehab -1, -1, 23903f7eae8SMauro Carvalho Chehab "i3000 UE", ""); 240dd8ef1dbSJason Uhlenkott } 241dd8ef1dbSJason Uhlenkott } 242dd8ef1dbSJason Uhlenkott } 243dd8ef1dbSJason Uhlenkott 244dd8ef1dbSJason Uhlenkott static void i3200_check(struct mem_ctl_info *mci) 245dd8ef1dbSJason Uhlenkott { 246dd8ef1dbSJason Uhlenkott struct i3200_error_info info; 247dd8ef1dbSJason Uhlenkott 248956b9ba1SJoe Perches edac_dbg(1, "MC%d\n", mci->mc_idx); 249dd8ef1dbSJason Uhlenkott i3200_get_and_clear_error_info(mci, &info); 250dd8ef1dbSJason Uhlenkott i3200_process_error_info(mci, &info); 251dd8ef1dbSJason Uhlenkott } 252dd8ef1dbSJason Uhlenkott 253dd8ef1dbSJason Uhlenkott 254dd8ef1dbSJason Uhlenkott void __iomem *i3200_map_mchbar(struct pci_dev *pdev) 255dd8ef1dbSJason Uhlenkott { 256dd8ef1dbSJason Uhlenkott union { 257dd8ef1dbSJason Uhlenkott u64 mchbar; 258dd8ef1dbSJason Uhlenkott struct { 259dd8ef1dbSJason Uhlenkott u32 mchbar_low; 260dd8ef1dbSJason Uhlenkott u32 mchbar_high; 261dd8ef1dbSJason Uhlenkott }; 262dd8ef1dbSJason Uhlenkott } u; 263dd8ef1dbSJason Uhlenkott void __iomem *window; 264dd8ef1dbSJason Uhlenkott 265dd8ef1dbSJason Uhlenkott pci_read_config_dword(pdev, I3200_MCHBAR_LOW, &u.mchbar_low); 266dd8ef1dbSJason Uhlenkott pci_read_config_dword(pdev, I3200_MCHBAR_HIGH, &u.mchbar_high); 267dd8ef1dbSJason Uhlenkott u.mchbar &= I3200_MCHBAR_MASK; 268dd8ef1dbSJason Uhlenkott 269dd8ef1dbSJason Uhlenkott if (u.mchbar != (resource_size_t)u.mchbar) { 270dd8ef1dbSJason Uhlenkott printk(KERN_ERR 271dd8ef1dbSJason Uhlenkott "i3200: mmio space beyond accessible range (0x%llx)\n", 272dd8ef1dbSJason Uhlenkott (unsigned long long)u.mchbar); 273dd8ef1dbSJason Uhlenkott return NULL; 274dd8ef1dbSJason Uhlenkott } 275dd8ef1dbSJason Uhlenkott 276dd8ef1dbSJason Uhlenkott window = ioremap_nocache(u.mchbar, I3200_MMR_WINDOW_SIZE); 277dd8ef1dbSJason Uhlenkott if (!window) 278dd8ef1dbSJason Uhlenkott printk(KERN_ERR "i3200: cannot map mmio space at 0x%llx\n", 279dd8ef1dbSJason Uhlenkott (unsigned long long)u.mchbar); 280dd8ef1dbSJason Uhlenkott 281dd8ef1dbSJason Uhlenkott return window; 282dd8ef1dbSJason Uhlenkott } 283dd8ef1dbSJason Uhlenkott 284dd8ef1dbSJason Uhlenkott 285dd8ef1dbSJason Uhlenkott static void i3200_get_drbs(void __iomem *window, 286dd8ef1dbSJason Uhlenkott u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL]) 287dd8ef1dbSJason Uhlenkott { 288dd8ef1dbSJason Uhlenkott int i; 289dd8ef1dbSJason Uhlenkott 290dd8ef1dbSJason Uhlenkott for (i = 0; i < I3200_RANKS_PER_CHANNEL; i++) { 291dd8ef1dbSJason Uhlenkott drbs[0][i] = readw(window + I3200_C0DRB + 2*i) & I3200_DRB_MASK; 292dd8ef1dbSJason Uhlenkott drbs[1][i] = readw(window + I3200_C1DRB + 2*i) & I3200_DRB_MASK; 293dd8ef1dbSJason Uhlenkott } 294dd8ef1dbSJason Uhlenkott } 295dd8ef1dbSJason Uhlenkott 296dd8ef1dbSJason Uhlenkott static bool i3200_is_stacked(struct pci_dev *pdev, 297dd8ef1dbSJason Uhlenkott u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL]) 298dd8ef1dbSJason Uhlenkott { 299dd8ef1dbSJason Uhlenkott u16 tom; 300dd8ef1dbSJason Uhlenkott 301dd8ef1dbSJason Uhlenkott pci_read_config_word(pdev, I3200_TOM, &tom); 302dd8ef1dbSJason Uhlenkott tom &= I3200_TOM_MASK; 303dd8ef1dbSJason Uhlenkott 304dd8ef1dbSJason Uhlenkott return drbs[I3200_CHANNELS - 1][I3200_RANKS_PER_CHANNEL - 1] == tom; 305dd8ef1dbSJason Uhlenkott } 306dd8ef1dbSJason Uhlenkott 307dd8ef1dbSJason Uhlenkott static unsigned long drb_to_nr_pages( 308dd8ef1dbSJason Uhlenkott u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL], bool stacked, 309dd8ef1dbSJason Uhlenkott int channel, int rank) 310dd8ef1dbSJason Uhlenkott { 311dd8ef1dbSJason Uhlenkott int n; 312dd8ef1dbSJason Uhlenkott 313dd8ef1dbSJason Uhlenkott n = drbs[channel][rank]; 314dd8ef1dbSJason Uhlenkott if (rank > 0) 315dd8ef1dbSJason Uhlenkott n -= drbs[channel][rank - 1]; 316dd8ef1dbSJason Uhlenkott if (stacked && (channel == 1) && 317dd8ef1dbSJason Uhlenkott drbs[channel][rank] == drbs[channel][I3200_RANKS_PER_CHANNEL - 1]) 318dd8ef1dbSJason Uhlenkott n -= drbs[0][I3200_RANKS_PER_CHANNEL - 1]; 319dd8ef1dbSJason Uhlenkott 320dd8ef1dbSJason Uhlenkott n <<= (I3200_DRB_SHIFT - PAGE_SHIFT); 321dd8ef1dbSJason Uhlenkott return n; 322dd8ef1dbSJason Uhlenkott } 323dd8ef1dbSJason Uhlenkott 324dd8ef1dbSJason Uhlenkott static int i3200_probe1(struct pci_dev *pdev, int dev_idx) 325dd8ef1dbSJason Uhlenkott { 326dd8ef1dbSJason Uhlenkott int rc; 327084a4fccSMauro Carvalho Chehab int i, j; 328dd8ef1dbSJason Uhlenkott struct mem_ctl_info *mci = NULL; 32995b93287SMauro Carvalho Chehab struct edac_mc_layer layers[2]; 330dd8ef1dbSJason Uhlenkott u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL]; 331dd8ef1dbSJason Uhlenkott bool stacked; 332dd8ef1dbSJason Uhlenkott void __iomem *window; 333dd8ef1dbSJason Uhlenkott struct i3200_priv *priv; 334dd8ef1dbSJason Uhlenkott 335956b9ba1SJoe Perches edac_dbg(0, "MC:\n"); 336dd8ef1dbSJason Uhlenkott 337dd8ef1dbSJason Uhlenkott window = i3200_map_mchbar(pdev); 338dd8ef1dbSJason Uhlenkott if (!window) 339dd8ef1dbSJason Uhlenkott return -ENODEV; 340dd8ef1dbSJason Uhlenkott 341dd8ef1dbSJason Uhlenkott i3200_get_drbs(window, drbs); 342dd8ef1dbSJason Uhlenkott nr_channels = how_many_channels(pdev); 343dd8ef1dbSJason Uhlenkott 34495b93287SMauro Carvalho Chehab layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; 34595b93287SMauro Carvalho Chehab layers[0].size = I3200_DIMMS; 34695b93287SMauro Carvalho Chehab layers[0].is_virt_csrow = true; 34795b93287SMauro Carvalho Chehab layers[1].type = EDAC_MC_LAYER_CHANNEL; 34895b93287SMauro Carvalho Chehab layers[1].size = nr_channels; 34995b93287SMauro Carvalho Chehab layers[1].is_virt_csrow = false; 350ca0907b9SMauro Carvalho Chehab mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 35195b93287SMauro Carvalho Chehab sizeof(struct i3200_priv)); 352dd8ef1dbSJason Uhlenkott if (!mci) 353dd8ef1dbSJason Uhlenkott return -ENOMEM; 354dd8ef1dbSJason Uhlenkott 355956b9ba1SJoe Perches edac_dbg(3, "MC: init mci\n"); 356dd8ef1dbSJason Uhlenkott 357fd687502SMauro Carvalho Chehab mci->pdev = &pdev->dev; 358dd8ef1dbSJason Uhlenkott mci->mtype_cap = MEM_FLAG_DDR2; 359dd8ef1dbSJason Uhlenkott 360dd8ef1dbSJason Uhlenkott mci->edac_ctl_cap = EDAC_FLAG_SECDED; 361dd8ef1dbSJason Uhlenkott mci->edac_cap = EDAC_FLAG_SECDED; 362dd8ef1dbSJason Uhlenkott 363dd8ef1dbSJason Uhlenkott mci->mod_name = EDAC_MOD_STR; 364dd8ef1dbSJason Uhlenkott mci->mod_ver = I3200_REVISION; 365dd8ef1dbSJason Uhlenkott mci->ctl_name = i3200_devs[dev_idx].ctl_name; 366dd8ef1dbSJason Uhlenkott mci->dev_name = pci_name(pdev); 367dd8ef1dbSJason Uhlenkott mci->edac_check = i3200_check; 368dd8ef1dbSJason Uhlenkott mci->ctl_page_to_phys = NULL; 369dd8ef1dbSJason Uhlenkott priv = mci->pvt_info; 370dd8ef1dbSJason Uhlenkott priv->window = window; 371dd8ef1dbSJason Uhlenkott 372dd8ef1dbSJason Uhlenkott stacked = i3200_is_stacked(pdev, drbs); 373dd8ef1dbSJason Uhlenkott 374dd8ef1dbSJason Uhlenkott /* 375dd8ef1dbSJason Uhlenkott * The dram rank boundary (DRB) reg values are boundary addresses 376dd8ef1dbSJason Uhlenkott * for each DRAM rank with a granularity of 64MB. DRB regs are 377dd8ef1dbSJason Uhlenkott * cumulative; the last one will contain the total memory 378dd8ef1dbSJason Uhlenkott * contained in all ranks. 379dd8ef1dbSJason Uhlenkott */ 380dd8ef1dbSJason Uhlenkott for (i = 0; i < mci->nr_csrows; i++) { 381dd8ef1dbSJason Uhlenkott unsigned long nr_pages; 382de3910ebSMauro Carvalho Chehab struct csrow_info *csrow = mci->csrows[i]; 383dd8ef1dbSJason Uhlenkott 384dd8ef1dbSJason Uhlenkott nr_pages = drb_to_nr_pages(drbs, stacked, 385dd8ef1dbSJason Uhlenkott i / I3200_RANKS_PER_CHANNEL, 386dd8ef1dbSJason Uhlenkott i % I3200_RANKS_PER_CHANNEL); 387dd8ef1dbSJason Uhlenkott 388084a4fccSMauro Carvalho Chehab if (nr_pages == 0) 389dd8ef1dbSJason Uhlenkott continue; 390dd8ef1dbSJason Uhlenkott 391084a4fccSMauro Carvalho Chehab for (j = 0; j < nr_channels; j++) { 392de3910ebSMauro Carvalho Chehab struct dimm_info *dimm = csrow->channels[j]->dimm; 393084a4fccSMauro Carvalho Chehab 394582a8996SMauro Carvalho Chehab dimm->nr_pages = nr_pages; 395084a4fccSMauro Carvalho Chehab dimm->grain = nr_pages << PAGE_SHIFT; 396084a4fccSMauro Carvalho Chehab dimm->mtype = MEM_DDR2; 397084a4fccSMauro Carvalho Chehab dimm->dtype = DEV_UNKNOWN; 398084a4fccSMauro Carvalho Chehab dimm->edac_mode = EDAC_UNKNOWN; 399084a4fccSMauro Carvalho Chehab } 400dd8ef1dbSJason Uhlenkott } 401dd8ef1dbSJason Uhlenkott 402dd8ef1dbSJason Uhlenkott i3200_clear_error_info(mci); 403dd8ef1dbSJason Uhlenkott 404dd8ef1dbSJason Uhlenkott rc = -ENODEV; 405dd8ef1dbSJason Uhlenkott if (edac_mc_add_mc(mci)) { 406956b9ba1SJoe Perches edac_dbg(3, "MC: failed edac_mc_add_mc()\n"); 407dd8ef1dbSJason Uhlenkott goto fail; 408dd8ef1dbSJason Uhlenkott } 409dd8ef1dbSJason Uhlenkott 410dd8ef1dbSJason Uhlenkott /* get this far and it's successful */ 411956b9ba1SJoe Perches edac_dbg(3, "MC: success\n"); 412dd8ef1dbSJason Uhlenkott return 0; 413dd8ef1dbSJason Uhlenkott 414dd8ef1dbSJason Uhlenkott fail: 415dd8ef1dbSJason Uhlenkott iounmap(window); 416dd8ef1dbSJason Uhlenkott if (mci) 417dd8ef1dbSJason Uhlenkott edac_mc_free(mci); 418dd8ef1dbSJason Uhlenkott 419dd8ef1dbSJason Uhlenkott return rc; 420dd8ef1dbSJason Uhlenkott } 421dd8ef1dbSJason Uhlenkott 422*9b3c6e85SGreg Kroah-Hartman static int i3200_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) 423dd8ef1dbSJason Uhlenkott { 424dd8ef1dbSJason Uhlenkott int rc; 425dd8ef1dbSJason Uhlenkott 426956b9ba1SJoe Perches edac_dbg(0, "MC:\n"); 427dd8ef1dbSJason Uhlenkott 428dd8ef1dbSJason Uhlenkott if (pci_enable_device(pdev) < 0) 429dd8ef1dbSJason Uhlenkott return -EIO; 430dd8ef1dbSJason Uhlenkott 431dd8ef1dbSJason Uhlenkott rc = i3200_probe1(pdev, ent->driver_data); 432dd8ef1dbSJason Uhlenkott if (!mci_pdev) 433dd8ef1dbSJason Uhlenkott mci_pdev = pci_dev_get(pdev); 434dd8ef1dbSJason Uhlenkott 435dd8ef1dbSJason Uhlenkott return rc; 436dd8ef1dbSJason Uhlenkott } 437dd8ef1dbSJason Uhlenkott 438*9b3c6e85SGreg Kroah-Hartman static void i3200_remove_one(struct pci_dev *pdev) 439dd8ef1dbSJason Uhlenkott { 440dd8ef1dbSJason Uhlenkott struct mem_ctl_info *mci; 441dd8ef1dbSJason Uhlenkott struct i3200_priv *priv; 442dd8ef1dbSJason Uhlenkott 443956b9ba1SJoe Perches edac_dbg(0, "\n"); 444dd8ef1dbSJason Uhlenkott 445dd8ef1dbSJason Uhlenkott mci = edac_mc_del_mc(&pdev->dev); 446dd8ef1dbSJason Uhlenkott if (!mci) 447dd8ef1dbSJason Uhlenkott return; 448dd8ef1dbSJason Uhlenkott 449dd8ef1dbSJason Uhlenkott priv = mci->pvt_info; 450dd8ef1dbSJason Uhlenkott iounmap(priv->window); 451dd8ef1dbSJason Uhlenkott 452dd8ef1dbSJason Uhlenkott edac_mc_free(mci); 453dd8ef1dbSJason Uhlenkott } 454dd8ef1dbSJason Uhlenkott 45536c46f31SLionel Debroux static DEFINE_PCI_DEVICE_TABLE(i3200_pci_tbl) = { 456dd8ef1dbSJason Uhlenkott { 457dd8ef1dbSJason Uhlenkott PCI_VEND_DEV(INTEL, 3200_HB), PCI_ANY_ID, PCI_ANY_ID, 0, 0, 458dd8ef1dbSJason Uhlenkott I3200}, 459dd8ef1dbSJason Uhlenkott { 460dd8ef1dbSJason Uhlenkott 0, 461dd8ef1dbSJason Uhlenkott } /* 0 terminated list. */ 462dd8ef1dbSJason Uhlenkott }; 463dd8ef1dbSJason Uhlenkott 464dd8ef1dbSJason Uhlenkott MODULE_DEVICE_TABLE(pci, i3200_pci_tbl); 465dd8ef1dbSJason Uhlenkott 466dd8ef1dbSJason Uhlenkott static struct pci_driver i3200_driver = { 467dd8ef1dbSJason Uhlenkott .name = EDAC_MOD_STR, 468dd8ef1dbSJason Uhlenkott .probe = i3200_init_one, 469*9b3c6e85SGreg Kroah-Hartman .remove = i3200_remove_one, 470dd8ef1dbSJason Uhlenkott .id_table = i3200_pci_tbl, 471dd8ef1dbSJason Uhlenkott }; 472dd8ef1dbSJason Uhlenkott 473dd8ef1dbSJason Uhlenkott static int __init i3200_init(void) 474dd8ef1dbSJason Uhlenkott { 475dd8ef1dbSJason Uhlenkott int pci_rc; 476dd8ef1dbSJason Uhlenkott 477956b9ba1SJoe Perches edac_dbg(3, "MC:\n"); 478dd8ef1dbSJason Uhlenkott 479dd8ef1dbSJason Uhlenkott /* Ensure that the OPSTATE is set correctly for POLL or NMI */ 480dd8ef1dbSJason Uhlenkott opstate_init(); 481dd8ef1dbSJason Uhlenkott 482dd8ef1dbSJason Uhlenkott pci_rc = pci_register_driver(&i3200_driver); 483dd8ef1dbSJason Uhlenkott if (pci_rc < 0) 484dd8ef1dbSJason Uhlenkott goto fail0; 485dd8ef1dbSJason Uhlenkott 486dd8ef1dbSJason Uhlenkott if (!mci_pdev) { 487dd8ef1dbSJason Uhlenkott i3200_registered = 0; 488dd8ef1dbSJason Uhlenkott mci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 489dd8ef1dbSJason Uhlenkott PCI_DEVICE_ID_INTEL_3200_HB, NULL); 490dd8ef1dbSJason Uhlenkott if (!mci_pdev) { 491956b9ba1SJoe Perches edac_dbg(0, "i3200 pci_get_device fail\n"); 492dd8ef1dbSJason Uhlenkott pci_rc = -ENODEV; 493dd8ef1dbSJason Uhlenkott goto fail1; 494dd8ef1dbSJason Uhlenkott } 495dd8ef1dbSJason Uhlenkott 496dd8ef1dbSJason Uhlenkott pci_rc = i3200_init_one(mci_pdev, i3200_pci_tbl); 497dd8ef1dbSJason Uhlenkott if (pci_rc < 0) { 498956b9ba1SJoe Perches edac_dbg(0, "i3200 init fail\n"); 499dd8ef1dbSJason Uhlenkott pci_rc = -ENODEV; 500dd8ef1dbSJason Uhlenkott goto fail1; 501dd8ef1dbSJason Uhlenkott } 502dd8ef1dbSJason Uhlenkott } 503dd8ef1dbSJason Uhlenkott 504dd8ef1dbSJason Uhlenkott return 0; 505dd8ef1dbSJason Uhlenkott 506dd8ef1dbSJason Uhlenkott fail1: 507dd8ef1dbSJason Uhlenkott pci_unregister_driver(&i3200_driver); 508dd8ef1dbSJason Uhlenkott 509dd8ef1dbSJason Uhlenkott fail0: 510dd8ef1dbSJason Uhlenkott if (mci_pdev) 511dd8ef1dbSJason Uhlenkott pci_dev_put(mci_pdev); 512dd8ef1dbSJason Uhlenkott 513dd8ef1dbSJason Uhlenkott return pci_rc; 514dd8ef1dbSJason Uhlenkott } 515dd8ef1dbSJason Uhlenkott 516dd8ef1dbSJason Uhlenkott static void __exit i3200_exit(void) 517dd8ef1dbSJason Uhlenkott { 518956b9ba1SJoe Perches edac_dbg(3, "MC:\n"); 519dd8ef1dbSJason Uhlenkott 520dd8ef1dbSJason Uhlenkott pci_unregister_driver(&i3200_driver); 521dd8ef1dbSJason Uhlenkott if (!i3200_registered) { 522dd8ef1dbSJason Uhlenkott i3200_remove_one(mci_pdev); 523dd8ef1dbSJason Uhlenkott pci_dev_put(mci_pdev); 524dd8ef1dbSJason Uhlenkott } 525dd8ef1dbSJason Uhlenkott } 526dd8ef1dbSJason Uhlenkott 527dd8ef1dbSJason Uhlenkott module_init(i3200_init); 528dd8ef1dbSJason Uhlenkott module_exit(i3200_exit); 529dd8ef1dbSJason Uhlenkott 530dd8ef1dbSJason Uhlenkott MODULE_LICENSE("GPL"); 531dd8ef1dbSJason Uhlenkott MODULE_AUTHOR("Akamai Technologies, Inc."); 532dd8ef1dbSJason Uhlenkott MODULE_DESCRIPTION("MC support for Intel 3200 memory hub controllers"); 533dd8ef1dbSJason Uhlenkott 534dd8ef1dbSJason Uhlenkott module_param(edac_op_state, int, 0444); 535dd8ef1dbSJason Uhlenkott MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI"); 536