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 { 1095f466cb0SMauro Carvalho Chehab int n_channels; 1105f466cb0SMauro Carvalho Chehab 111dd8ef1dbSJason Uhlenkott unsigned char capid0_8b; /* 8th byte of CAPID0 */ 112dd8ef1dbSJason Uhlenkott 113dd8ef1dbSJason Uhlenkott pci_read_config_byte(pdev, I3200_CAPID0 + 8, &capid0_8b); 1145f466cb0SMauro Carvalho Chehab 115dd8ef1dbSJason Uhlenkott if (capid0_8b & 0x20) { /* check DCD: Dual Channel Disable */ 116956b9ba1SJoe Perches edac_dbg(0, "In single channel mode\n"); 1175f466cb0SMauro Carvalho Chehab n_channels = 1; 118dd8ef1dbSJason Uhlenkott } else { 119956b9ba1SJoe Perches edac_dbg(0, "In dual channel mode\n"); 1205f466cb0SMauro Carvalho Chehab n_channels = 2; 121dd8ef1dbSJason Uhlenkott } 1225f466cb0SMauro Carvalho Chehab 1235f466cb0SMauro Carvalho Chehab if (capid0_8b & 0x10) /* check if both channels are filled */ 1245f466cb0SMauro Carvalho Chehab edac_dbg(0, "2 DIMMS per channel disabled\n"); 1255f466cb0SMauro Carvalho Chehab else 1265f466cb0SMauro Carvalho Chehab edac_dbg(0, "2 DIMMS per channel enabled\n"); 1275f466cb0SMauro Carvalho Chehab 1285f466cb0SMauro Carvalho Chehab return n_channels; 129dd8ef1dbSJason Uhlenkott } 130dd8ef1dbSJason Uhlenkott 131dd8ef1dbSJason Uhlenkott static unsigned long eccerrlog_syndrome(u64 log) 132dd8ef1dbSJason Uhlenkott { 133dd8ef1dbSJason Uhlenkott return (log & I3200_ECCERRLOG_SYNDROME_BITS) >> 134dd8ef1dbSJason Uhlenkott I3200_ECCERRLOG_SYNDROME_SHIFT; 135dd8ef1dbSJason Uhlenkott } 136dd8ef1dbSJason Uhlenkott 137dd8ef1dbSJason Uhlenkott static int eccerrlog_row(int channel, u64 log) 138dd8ef1dbSJason Uhlenkott { 139dd8ef1dbSJason Uhlenkott u64 rank = ((log & I3200_ECCERRLOG_RANK_BITS) >> 140dd8ef1dbSJason Uhlenkott I3200_ECCERRLOG_RANK_SHIFT); 141dd8ef1dbSJason Uhlenkott return rank | (channel * I3200_RANKS_PER_CHANNEL); 142dd8ef1dbSJason Uhlenkott } 143dd8ef1dbSJason Uhlenkott 144dd8ef1dbSJason Uhlenkott enum i3200_chips { 145dd8ef1dbSJason Uhlenkott I3200 = 0, 146dd8ef1dbSJason Uhlenkott }; 147dd8ef1dbSJason Uhlenkott 148dd8ef1dbSJason Uhlenkott struct i3200_dev_info { 149dd8ef1dbSJason Uhlenkott const char *ctl_name; 150dd8ef1dbSJason Uhlenkott }; 151dd8ef1dbSJason Uhlenkott 152dd8ef1dbSJason Uhlenkott struct i3200_error_info { 153dd8ef1dbSJason Uhlenkott u16 errsts; 154dd8ef1dbSJason Uhlenkott u16 errsts2; 155dd8ef1dbSJason Uhlenkott u64 eccerrlog[I3200_CHANNELS]; 156dd8ef1dbSJason Uhlenkott }; 157dd8ef1dbSJason Uhlenkott 158dd8ef1dbSJason Uhlenkott static const struct i3200_dev_info i3200_devs[] = { 159dd8ef1dbSJason Uhlenkott [I3200] = { 160dd8ef1dbSJason Uhlenkott .ctl_name = "i3200" 161dd8ef1dbSJason Uhlenkott }, 162dd8ef1dbSJason Uhlenkott }; 163dd8ef1dbSJason Uhlenkott 164dd8ef1dbSJason Uhlenkott static struct pci_dev *mci_pdev; 165dd8ef1dbSJason Uhlenkott static int i3200_registered = 1; 166dd8ef1dbSJason Uhlenkott 167dd8ef1dbSJason Uhlenkott 168dd8ef1dbSJason Uhlenkott static void i3200_clear_error_info(struct mem_ctl_info *mci) 169dd8ef1dbSJason Uhlenkott { 170dd8ef1dbSJason Uhlenkott struct pci_dev *pdev; 171dd8ef1dbSJason Uhlenkott 172fd687502SMauro Carvalho Chehab pdev = to_pci_dev(mci->pdev); 173dd8ef1dbSJason Uhlenkott 174dd8ef1dbSJason Uhlenkott /* 175dd8ef1dbSJason Uhlenkott * Clear any error bits. 176dd8ef1dbSJason Uhlenkott * (Yes, we really clear bits by writing 1 to them.) 177dd8ef1dbSJason Uhlenkott */ 178dd8ef1dbSJason Uhlenkott pci_write_bits16(pdev, I3200_ERRSTS, I3200_ERRSTS_BITS, 179dd8ef1dbSJason Uhlenkott I3200_ERRSTS_BITS); 180dd8ef1dbSJason Uhlenkott } 181dd8ef1dbSJason Uhlenkott 182dd8ef1dbSJason Uhlenkott static void i3200_get_and_clear_error_info(struct mem_ctl_info *mci, 183dd8ef1dbSJason Uhlenkott struct i3200_error_info *info) 184dd8ef1dbSJason Uhlenkott { 185dd8ef1dbSJason Uhlenkott struct pci_dev *pdev; 186dd8ef1dbSJason Uhlenkott struct i3200_priv *priv = mci->pvt_info; 187dd8ef1dbSJason Uhlenkott void __iomem *window = priv->window; 188dd8ef1dbSJason Uhlenkott 189fd687502SMauro Carvalho Chehab pdev = to_pci_dev(mci->pdev); 190dd8ef1dbSJason Uhlenkott 191dd8ef1dbSJason Uhlenkott /* 192dd8ef1dbSJason Uhlenkott * This is a mess because there is no atomic way to read all the 193dd8ef1dbSJason Uhlenkott * registers at once and the registers can transition from CE being 194dd8ef1dbSJason Uhlenkott * overwritten by UE. 195dd8ef1dbSJason Uhlenkott */ 196dd8ef1dbSJason Uhlenkott pci_read_config_word(pdev, I3200_ERRSTS, &info->errsts); 197dd8ef1dbSJason Uhlenkott if (!(info->errsts & I3200_ERRSTS_BITS)) 198dd8ef1dbSJason Uhlenkott return; 199dd8ef1dbSJason Uhlenkott 200dd8ef1dbSJason Uhlenkott info->eccerrlog[0] = readq(window + I3200_C0ECCERRLOG); 201dd8ef1dbSJason Uhlenkott if (nr_channels == 2) 202dd8ef1dbSJason Uhlenkott info->eccerrlog[1] = readq(window + I3200_C1ECCERRLOG); 203dd8ef1dbSJason Uhlenkott 204dd8ef1dbSJason Uhlenkott pci_read_config_word(pdev, I3200_ERRSTS, &info->errsts2); 205dd8ef1dbSJason Uhlenkott 206dd8ef1dbSJason Uhlenkott /* 207dd8ef1dbSJason Uhlenkott * If the error is the same for both reads then the first set 208dd8ef1dbSJason Uhlenkott * of reads is valid. If there is a change then there is a CE 209dd8ef1dbSJason Uhlenkott * with no info and the second set of reads is valid and 210dd8ef1dbSJason Uhlenkott * should be UE info. 211dd8ef1dbSJason Uhlenkott */ 212dd8ef1dbSJason Uhlenkott if ((info->errsts ^ info->errsts2) & I3200_ERRSTS_BITS) { 213dd8ef1dbSJason Uhlenkott info->eccerrlog[0] = readq(window + I3200_C0ECCERRLOG); 214dd8ef1dbSJason Uhlenkott if (nr_channels == 2) 215dd8ef1dbSJason Uhlenkott info->eccerrlog[1] = readq(window + I3200_C1ECCERRLOG); 216dd8ef1dbSJason Uhlenkott } 217dd8ef1dbSJason Uhlenkott 218dd8ef1dbSJason Uhlenkott i3200_clear_error_info(mci); 219dd8ef1dbSJason Uhlenkott } 220dd8ef1dbSJason Uhlenkott 221dd8ef1dbSJason Uhlenkott static void i3200_process_error_info(struct mem_ctl_info *mci, 222dd8ef1dbSJason Uhlenkott struct i3200_error_info *info) 223dd8ef1dbSJason Uhlenkott { 224dd8ef1dbSJason Uhlenkott int channel; 225dd8ef1dbSJason Uhlenkott u64 log; 226dd8ef1dbSJason Uhlenkott 227dd8ef1dbSJason Uhlenkott if (!(info->errsts & I3200_ERRSTS_BITS)) 228dd8ef1dbSJason Uhlenkott return; 229dd8ef1dbSJason Uhlenkott 230dd8ef1dbSJason Uhlenkott if ((info->errsts ^ info->errsts2) & I3200_ERRSTS_BITS) { 2319eb07a7fSMauro Carvalho Chehab edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, 23203f7eae8SMauro Carvalho Chehab -1, -1, -1, "UE overwrote CE", ""); 233dd8ef1dbSJason Uhlenkott info->errsts = info->errsts2; 234dd8ef1dbSJason Uhlenkott } 235dd8ef1dbSJason Uhlenkott 236dd8ef1dbSJason Uhlenkott for (channel = 0; channel < nr_channels; channel++) { 237dd8ef1dbSJason Uhlenkott log = info->eccerrlog[channel]; 238dd8ef1dbSJason Uhlenkott if (log & I3200_ECCERRLOG_UE) { 2399eb07a7fSMauro Carvalho Chehab edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 24095b93287SMauro Carvalho Chehab 0, 0, 0, 241dd8ef1dbSJason Uhlenkott eccerrlog_row(channel, log), 24295b93287SMauro Carvalho Chehab -1, -1, 24303f7eae8SMauro Carvalho Chehab "i3000 UE", ""); 244dd8ef1dbSJason Uhlenkott } else if (log & I3200_ECCERRLOG_CE) { 245*8a3f075dSJason Baron edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 24695b93287SMauro Carvalho Chehab 0, 0, eccerrlog_syndrome(log), 24795b93287SMauro Carvalho Chehab eccerrlog_row(channel, log), 24895b93287SMauro Carvalho Chehab -1, -1, 249*8a3f075dSJason Baron "i3000 CE", ""); 250dd8ef1dbSJason Uhlenkott } 251dd8ef1dbSJason Uhlenkott } 252dd8ef1dbSJason Uhlenkott } 253dd8ef1dbSJason Uhlenkott 254dd8ef1dbSJason Uhlenkott static void i3200_check(struct mem_ctl_info *mci) 255dd8ef1dbSJason Uhlenkott { 256dd8ef1dbSJason Uhlenkott struct i3200_error_info info; 257dd8ef1dbSJason Uhlenkott 258956b9ba1SJoe Perches edac_dbg(1, "MC%d\n", mci->mc_idx); 259dd8ef1dbSJason Uhlenkott i3200_get_and_clear_error_info(mci, &info); 260dd8ef1dbSJason Uhlenkott i3200_process_error_info(mci, &info); 261dd8ef1dbSJason Uhlenkott } 262dd8ef1dbSJason Uhlenkott 263166e9334SJingoo Han static void __iomem *i3200_map_mchbar(struct pci_dev *pdev) 264dd8ef1dbSJason Uhlenkott { 265dd8ef1dbSJason Uhlenkott union { 266dd8ef1dbSJason Uhlenkott u64 mchbar; 267dd8ef1dbSJason Uhlenkott struct { 268dd8ef1dbSJason Uhlenkott u32 mchbar_low; 269dd8ef1dbSJason Uhlenkott u32 mchbar_high; 270dd8ef1dbSJason Uhlenkott }; 271dd8ef1dbSJason Uhlenkott } u; 272dd8ef1dbSJason Uhlenkott void __iomem *window; 273dd8ef1dbSJason Uhlenkott 274dd8ef1dbSJason Uhlenkott pci_read_config_dword(pdev, I3200_MCHBAR_LOW, &u.mchbar_low); 275dd8ef1dbSJason Uhlenkott pci_read_config_dword(pdev, I3200_MCHBAR_HIGH, &u.mchbar_high); 276dd8ef1dbSJason Uhlenkott u.mchbar &= I3200_MCHBAR_MASK; 277dd8ef1dbSJason Uhlenkott 278dd8ef1dbSJason Uhlenkott if (u.mchbar != (resource_size_t)u.mchbar) { 279dd8ef1dbSJason Uhlenkott printk(KERN_ERR 280dd8ef1dbSJason Uhlenkott "i3200: mmio space beyond accessible range (0x%llx)\n", 281dd8ef1dbSJason Uhlenkott (unsigned long long)u.mchbar); 282dd8ef1dbSJason Uhlenkott return NULL; 283dd8ef1dbSJason Uhlenkott } 284dd8ef1dbSJason Uhlenkott 285dd8ef1dbSJason Uhlenkott window = ioremap_nocache(u.mchbar, I3200_MMR_WINDOW_SIZE); 286dd8ef1dbSJason Uhlenkott if (!window) 287dd8ef1dbSJason Uhlenkott printk(KERN_ERR "i3200: cannot map mmio space at 0x%llx\n", 288dd8ef1dbSJason Uhlenkott (unsigned long long)u.mchbar); 289dd8ef1dbSJason Uhlenkott 290dd8ef1dbSJason Uhlenkott return window; 291dd8ef1dbSJason Uhlenkott } 292dd8ef1dbSJason Uhlenkott 293dd8ef1dbSJason Uhlenkott 294dd8ef1dbSJason Uhlenkott static void i3200_get_drbs(void __iomem *window, 295dd8ef1dbSJason Uhlenkott u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL]) 296dd8ef1dbSJason Uhlenkott { 297dd8ef1dbSJason Uhlenkott int i; 298dd8ef1dbSJason Uhlenkott 299dd8ef1dbSJason Uhlenkott for (i = 0; i < I3200_RANKS_PER_CHANNEL; i++) { 300dd8ef1dbSJason Uhlenkott drbs[0][i] = readw(window + I3200_C0DRB + 2*i) & I3200_DRB_MASK; 301dd8ef1dbSJason Uhlenkott drbs[1][i] = readw(window + I3200_C1DRB + 2*i) & I3200_DRB_MASK; 3025f466cb0SMauro Carvalho Chehab 3035f466cb0SMauro Carvalho Chehab edac_dbg(0, "drb[0][%d] = %d, drb[1][%d] = %d\n", i, drbs[0][i], i, drbs[1][i]); 304dd8ef1dbSJason Uhlenkott } 305dd8ef1dbSJason Uhlenkott } 306dd8ef1dbSJason Uhlenkott 307dd8ef1dbSJason Uhlenkott static bool i3200_is_stacked(struct pci_dev *pdev, 308dd8ef1dbSJason Uhlenkott u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL]) 309dd8ef1dbSJason Uhlenkott { 310dd8ef1dbSJason Uhlenkott u16 tom; 311dd8ef1dbSJason Uhlenkott 312dd8ef1dbSJason Uhlenkott pci_read_config_word(pdev, I3200_TOM, &tom); 313dd8ef1dbSJason Uhlenkott tom &= I3200_TOM_MASK; 314dd8ef1dbSJason Uhlenkott 315dd8ef1dbSJason Uhlenkott return drbs[I3200_CHANNELS - 1][I3200_RANKS_PER_CHANNEL - 1] == tom; 316dd8ef1dbSJason Uhlenkott } 317dd8ef1dbSJason Uhlenkott 318dd8ef1dbSJason Uhlenkott static unsigned long drb_to_nr_pages( 319dd8ef1dbSJason Uhlenkott u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL], bool stacked, 320dd8ef1dbSJason Uhlenkott int channel, int rank) 321dd8ef1dbSJason Uhlenkott { 322dd8ef1dbSJason Uhlenkott int n; 323dd8ef1dbSJason Uhlenkott 324dd8ef1dbSJason Uhlenkott n = drbs[channel][rank]; 32561734e18SMauro Carvalho Chehab if (!n) 32661734e18SMauro Carvalho Chehab return 0; 32761734e18SMauro Carvalho Chehab 328dd8ef1dbSJason Uhlenkott if (rank > 0) 329dd8ef1dbSJason Uhlenkott n -= drbs[channel][rank - 1]; 330dd8ef1dbSJason Uhlenkott if (stacked && (channel == 1) && 331dd8ef1dbSJason Uhlenkott drbs[channel][rank] == drbs[channel][I3200_RANKS_PER_CHANNEL - 1]) 332dd8ef1dbSJason Uhlenkott n -= drbs[0][I3200_RANKS_PER_CHANNEL - 1]; 333dd8ef1dbSJason Uhlenkott 334dd8ef1dbSJason Uhlenkott n <<= (I3200_DRB_SHIFT - PAGE_SHIFT); 335dd8ef1dbSJason Uhlenkott return n; 336dd8ef1dbSJason Uhlenkott } 337dd8ef1dbSJason Uhlenkott 338dd8ef1dbSJason Uhlenkott static int i3200_probe1(struct pci_dev *pdev, int dev_idx) 339dd8ef1dbSJason Uhlenkott { 340dd8ef1dbSJason Uhlenkott int rc; 341084a4fccSMauro Carvalho Chehab int i, j; 342dd8ef1dbSJason Uhlenkott struct mem_ctl_info *mci = NULL; 34395b93287SMauro Carvalho Chehab struct edac_mc_layer layers[2]; 344dd8ef1dbSJason Uhlenkott u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL]; 345dd8ef1dbSJason Uhlenkott bool stacked; 346dd8ef1dbSJason Uhlenkott void __iomem *window; 347dd8ef1dbSJason Uhlenkott struct i3200_priv *priv; 348dd8ef1dbSJason Uhlenkott 349956b9ba1SJoe Perches edac_dbg(0, "MC:\n"); 350dd8ef1dbSJason Uhlenkott 351dd8ef1dbSJason Uhlenkott window = i3200_map_mchbar(pdev); 352dd8ef1dbSJason Uhlenkott if (!window) 353dd8ef1dbSJason Uhlenkott return -ENODEV; 354dd8ef1dbSJason Uhlenkott 355dd8ef1dbSJason Uhlenkott i3200_get_drbs(window, drbs); 356dd8ef1dbSJason Uhlenkott nr_channels = how_many_channels(pdev); 357dd8ef1dbSJason Uhlenkott 35895b93287SMauro Carvalho Chehab layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; 35995b93287SMauro Carvalho Chehab layers[0].size = I3200_DIMMS; 36095b93287SMauro Carvalho Chehab layers[0].is_virt_csrow = true; 36195b93287SMauro Carvalho Chehab layers[1].type = EDAC_MC_LAYER_CHANNEL; 36295b93287SMauro Carvalho Chehab layers[1].size = nr_channels; 36395b93287SMauro Carvalho Chehab layers[1].is_virt_csrow = false; 364ca0907b9SMauro Carvalho Chehab mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 36595b93287SMauro Carvalho Chehab sizeof(struct i3200_priv)); 366dd8ef1dbSJason Uhlenkott if (!mci) 367dd8ef1dbSJason Uhlenkott return -ENOMEM; 368dd8ef1dbSJason Uhlenkott 369956b9ba1SJoe Perches edac_dbg(3, "MC: init mci\n"); 370dd8ef1dbSJason Uhlenkott 371fd687502SMauro Carvalho Chehab mci->pdev = &pdev->dev; 372dd8ef1dbSJason Uhlenkott mci->mtype_cap = MEM_FLAG_DDR2; 373dd8ef1dbSJason Uhlenkott 374dd8ef1dbSJason Uhlenkott mci->edac_ctl_cap = EDAC_FLAG_SECDED; 375dd8ef1dbSJason Uhlenkott mci->edac_cap = EDAC_FLAG_SECDED; 376dd8ef1dbSJason Uhlenkott 377dd8ef1dbSJason Uhlenkott mci->mod_name = EDAC_MOD_STR; 378dd8ef1dbSJason Uhlenkott mci->mod_ver = I3200_REVISION; 379dd8ef1dbSJason Uhlenkott mci->ctl_name = i3200_devs[dev_idx].ctl_name; 380dd8ef1dbSJason Uhlenkott mci->dev_name = pci_name(pdev); 381dd8ef1dbSJason Uhlenkott mci->edac_check = i3200_check; 382dd8ef1dbSJason Uhlenkott mci->ctl_page_to_phys = NULL; 383dd8ef1dbSJason Uhlenkott priv = mci->pvt_info; 384dd8ef1dbSJason Uhlenkott priv->window = window; 385dd8ef1dbSJason Uhlenkott 386dd8ef1dbSJason Uhlenkott stacked = i3200_is_stacked(pdev, drbs); 387dd8ef1dbSJason Uhlenkott 388dd8ef1dbSJason Uhlenkott /* 389dd8ef1dbSJason Uhlenkott * The dram rank boundary (DRB) reg values are boundary addresses 390dd8ef1dbSJason Uhlenkott * for each DRAM rank with a granularity of 64MB. DRB regs are 391dd8ef1dbSJason Uhlenkott * cumulative; the last one will contain the total memory 392dd8ef1dbSJason Uhlenkott * contained in all ranks. 393dd8ef1dbSJason Uhlenkott */ 39461734e18SMauro Carvalho Chehab for (i = 0; i < I3200_DIMMS; i++) { 395dd8ef1dbSJason Uhlenkott unsigned long nr_pages; 396dd8ef1dbSJason Uhlenkott 39761734e18SMauro Carvalho Chehab for (j = 0; j < nr_channels; j++) { 39861734e18SMauro Carvalho Chehab struct dimm_info *dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, 39961734e18SMauro Carvalho Chehab mci->n_layers, i, j, 0); 400dd8ef1dbSJason Uhlenkott 40161734e18SMauro Carvalho Chehab nr_pages = drb_to_nr_pages(drbs, stacked, j, i); 402084a4fccSMauro Carvalho Chehab if (nr_pages == 0) 403dd8ef1dbSJason Uhlenkott continue; 404dd8ef1dbSJason Uhlenkott 40561734e18SMauro Carvalho Chehab edac_dbg(0, "csrow %d, channel %d%s, size = %ld Mb\n", i, j, 40661734e18SMauro Carvalho Chehab stacked ? " (stacked)" : "", PAGES_TO_MiB(nr_pages)); 407084a4fccSMauro Carvalho Chehab 408582a8996SMauro Carvalho Chehab dimm->nr_pages = nr_pages; 409084a4fccSMauro Carvalho Chehab dimm->grain = nr_pages << PAGE_SHIFT; 410084a4fccSMauro Carvalho Chehab dimm->mtype = MEM_DDR2; 411084a4fccSMauro Carvalho Chehab dimm->dtype = DEV_UNKNOWN; 412084a4fccSMauro Carvalho Chehab dimm->edac_mode = EDAC_UNKNOWN; 413084a4fccSMauro Carvalho Chehab } 414dd8ef1dbSJason Uhlenkott } 415dd8ef1dbSJason Uhlenkott 416dd8ef1dbSJason Uhlenkott i3200_clear_error_info(mci); 417dd8ef1dbSJason Uhlenkott 418dd8ef1dbSJason Uhlenkott rc = -ENODEV; 419dd8ef1dbSJason Uhlenkott if (edac_mc_add_mc(mci)) { 420956b9ba1SJoe Perches edac_dbg(3, "MC: failed edac_mc_add_mc()\n"); 421dd8ef1dbSJason Uhlenkott goto fail; 422dd8ef1dbSJason Uhlenkott } 423dd8ef1dbSJason Uhlenkott 424dd8ef1dbSJason Uhlenkott /* get this far and it's successful */ 425956b9ba1SJoe Perches edac_dbg(3, "MC: success\n"); 426dd8ef1dbSJason Uhlenkott return 0; 427dd8ef1dbSJason Uhlenkott 428dd8ef1dbSJason Uhlenkott fail: 429dd8ef1dbSJason Uhlenkott iounmap(window); 430dd8ef1dbSJason Uhlenkott if (mci) 431dd8ef1dbSJason Uhlenkott edac_mc_free(mci); 432dd8ef1dbSJason Uhlenkott 433dd8ef1dbSJason Uhlenkott return rc; 434dd8ef1dbSJason Uhlenkott } 435dd8ef1dbSJason Uhlenkott 4369b3c6e85SGreg Kroah-Hartman static int i3200_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) 437dd8ef1dbSJason Uhlenkott { 438dd8ef1dbSJason Uhlenkott int rc; 439dd8ef1dbSJason Uhlenkott 440956b9ba1SJoe Perches edac_dbg(0, "MC:\n"); 441dd8ef1dbSJason Uhlenkott 442dd8ef1dbSJason Uhlenkott if (pci_enable_device(pdev) < 0) 443dd8ef1dbSJason Uhlenkott return -EIO; 444dd8ef1dbSJason Uhlenkott 445dd8ef1dbSJason Uhlenkott rc = i3200_probe1(pdev, ent->driver_data); 446dd8ef1dbSJason Uhlenkott if (!mci_pdev) 447dd8ef1dbSJason Uhlenkott mci_pdev = pci_dev_get(pdev); 448dd8ef1dbSJason Uhlenkott 449dd8ef1dbSJason Uhlenkott return rc; 450dd8ef1dbSJason Uhlenkott } 451dd8ef1dbSJason Uhlenkott 4529b3c6e85SGreg Kroah-Hartman static void i3200_remove_one(struct pci_dev *pdev) 453dd8ef1dbSJason Uhlenkott { 454dd8ef1dbSJason Uhlenkott struct mem_ctl_info *mci; 455dd8ef1dbSJason Uhlenkott struct i3200_priv *priv; 456dd8ef1dbSJason Uhlenkott 457956b9ba1SJoe Perches edac_dbg(0, "\n"); 458dd8ef1dbSJason Uhlenkott 459dd8ef1dbSJason Uhlenkott mci = edac_mc_del_mc(&pdev->dev); 460dd8ef1dbSJason Uhlenkott if (!mci) 461dd8ef1dbSJason Uhlenkott return; 462dd8ef1dbSJason Uhlenkott 463dd8ef1dbSJason Uhlenkott priv = mci->pvt_info; 464dd8ef1dbSJason Uhlenkott iounmap(priv->window); 465dd8ef1dbSJason Uhlenkott 466dd8ef1dbSJason Uhlenkott edac_mc_free(mci); 467b90fe156SHitoshi Mitake 468b90fe156SHitoshi Mitake pci_disable_device(pdev); 469dd8ef1dbSJason Uhlenkott } 470dd8ef1dbSJason Uhlenkott 471ba935f40SJingoo Han static const struct pci_device_id i3200_pci_tbl[] = { 472dd8ef1dbSJason Uhlenkott { 473dd8ef1dbSJason Uhlenkott PCI_VEND_DEV(INTEL, 3200_HB), PCI_ANY_ID, PCI_ANY_ID, 0, 0, 474dd8ef1dbSJason Uhlenkott I3200}, 475dd8ef1dbSJason Uhlenkott { 476dd8ef1dbSJason Uhlenkott 0, 477dd8ef1dbSJason Uhlenkott } /* 0 terminated list. */ 478dd8ef1dbSJason Uhlenkott }; 479dd8ef1dbSJason Uhlenkott 480dd8ef1dbSJason Uhlenkott MODULE_DEVICE_TABLE(pci, i3200_pci_tbl); 481dd8ef1dbSJason Uhlenkott 482dd8ef1dbSJason Uhlenkott static struct pci_driver i3200_driver = { 483dd8ef1dbSJason Uhlenkott .name = EDAC_MOD_STR, 484dd8ef1dbSJason Uhlenkott .probe = i3200_init_one, 4859b3c6e85SGreg Kroah-Hartman .remove = i3200_remove_one, 486dd8ef1dbSJason Uhlenkott .id_table = i3200_pci_tbl, 487dd8ef1dbSJason Uhlenkott }; 488dd8ef1dbSJason Uhlenkott 489dd8ef1dbSJason Uhlenkott static int __init i3200_init(void) 490dd8ef1dbSJason Uhlenkott { 491dd8ef1dbSJason Uhlenkott int pci_rc; 492dd8ef1dbSJason Uhlenkott 493956b9ba1SJoe Perches edac_dbg(3, "MC:\n"); 494dd8ef1dbSJason Uhlenkott 495dd8ef1dbSJason Uhlenkott /* Ensure that the OPSTATE is set correctly for POLL or NMI */ 496dd8ef1dbSJason Uhlenkott opstate_init(); 497dd8ef1dbSJason Uhlenkott 498dd8ef1dbSJason Uhlenkott pci_rc = pci_register_driver(&i3200_driver); 499dd8ef1dbSJason Uhlenkott if (pci_rc < 0) 500dd8ef1dbSJason Uhlenkott goto fail0; 501dd8ef1dbSJason Uhlenkott 502dd8ef1dbSJason Uhlenkott if (!mci_pdev) { 503dd8ef1dbSJason Uhlenkott i3200_registered = 0; 504dd8ef1dbSJason Uhlenkott mci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 505dd8ef1dbSJason Uhlenkott PCI_DEVICE_ID_INTEL_3200_HB, NULL); 506dd8ef1dbSJason Uhlenkott if (!mci_pdev) { 507956b9ba1SJoe Perches edac_dbg(0, "i3200 pci_get_device fail\n"); 508dd8ef1dbSJason Uhlenkott pci_rc = -ENODEV; 509dd8ef1dbSJason Uhlenkott goto fail1; 510dd8ef1dbSJason Uhlenkott } 511dd8ef1dbSJason Uhlenkott 512dd8ef1dbSJason Uhlenkott pci_rc = i3200_init_one(mci_pdev, i3200_pci_tbl); 513dd8ef1dbSJason Uhlenkott if (pci_rc < 0) { 514956b9ba1SJoe Perches edac_dbg(0, "i3200 init fail\n"); 515dd8ef1dbSJason Uhlenkott pci_rc = -ENODEV; 516dd8ef1dbSJason Uhlenkott goto fail1; 517dd8ef1dbSJason Uhlenkott } 518dd8ef1dbSJason Uhlenkott } 519dd8ef1dbSJason Uhlenkott 520dd8ef1dbSJason Uhlenkott return 0; 521dd8ef1dbSJason Uhlenkott 522dd8ef1dbSJason Uhlenkott fail1: 523dd8ef1dbSJason Uhlenkott pci_unregister_driver(&i3200_driver); 524dd8ef1dbSJason Uhlenkott 525dd8ef1dbSJason Uhlenkott fail0: 526dd8ef1dbSJason Uhlenkott if (mci_pdev) 527dd8ef1dbSJason Uhlenkott pci_dev_put(mci_pdev); 528dd8ef1dbSJason Uhlenkott 529dd8ef1dbSJason Uhlenkott return pci_rc; 530dd8ef1dbSJason Uhlenkott } 531dd8ef1dbSJason Uhlenkott 532dd8ef1dbSJason Uhlenkott static void __exit i3200_exit(void) 533dd8ef1dbSJason Uhlenkott { 534956b9ba1SJoe Perches edac_dbg(3, "MC:\n"); 535dd8ef1dbSJason Uhlenkott 536dd8ef1dbSJason Uhlenkott pci_unregister_driver(&i3200_driver); 537dd8ef1dbSJason Uhlenkott if (!i3200_registered) { 538dd8ef1dbSJason Uhlenkott i3200_remove_one(mci_pdev); 539dd8ef1dbSJason Uhlenkott pci_dev_put(mci_pdev); 540dd8ef1dbSJason Uhlenkott } 541dd8ef1dbSJason Uhlenkott } 542dd8ef1dbSJason Uhlenkott 543dd8ef1dbSJason Uhlenkott module_init(i3200_init); 544dd8ef1dbSJason Uhlenkott module_exit(i3200_exit); 545dd8ef1dbSJason Uhlenkott 546dd8ef1dbSJason Uhlenkott MODULE_LICENSE("GPL"); 547dd8ef1dbSJason Uhlenkott MODULE_AUTHOR("Akamai Technologies, Inc."); 548dd8ef1dbSJason Uhlenkott MODULE_DESCRIPTION("MC support for Intel 3200 memory hub controllers"); 549dd8ef1dbSJason Uhlenkott 550dd8ef1dbSJason Uhlenkott module_param(edac_op_state, int, 0444); 551dd8ef1dbSJason Uhlenkott MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI"); 552