112237550SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
252707f91SMauro Carvalho Chehab /* Intel i7 core/Nehalem Memory Controller kernel module
352707f91SMauro Carvalho Chehab *
4e7bf068aSDavid Sterba * This driver supports the memory controllers found on the Intel
552707f91SMauro Carvalho Chehab * processor families i7core, i7core 7xx/8xx, i5core, Xeon 35xx,
652707f91SMauro Carvalho Chehab * Xeon 55xx and Xeon 56xx also known as Nehalem, Nehalem-EP, Lynnfield
752707f91SMauro Carvalho Chehab * and Westmere-EP.
8a0c36a1fSMauro Carvalho Chehab *
952707f91SMauro Carvalho Chehab * Copyright (c) 2009-2010 by:
1037e59f87SMauro Carvalho Chehab * Mauro Carvalho Chehab
11a0c36a1fSMauro Carvalho Chehab *
12a0c36a1fSMauro Carvalho Chehab * Red Hat Inc. https://www.redhat.com
13a0c36a1fSMauro Carvalho Chehab *
14a0c36a1fSMauro Carvalho Chehab * Forked and adapted from the i5400_edac driver
15a0c36a1fSMauro Carvalho Chehab *
16a0c36a1fSMauro Carvalho Chehab * Based on the following public Intel datasheets:
17a0c36a1fSMauro Carvalho Chehab * Intel Core i7 Processor Extreme Edition and Intel Core i7 Processor
18a0c36a1fSMauro Carvalho Chehab * Datasheet, Volume 2:
19a0c36a1fSMauro Carvalho Chehab * http://download.intel.com/design/processor/datashts/320835.pdf
20a0c36a1fSMauro Carvalho Chehab * Intel Xeon Processor 5500 Series Datasheet Volume 2
21a0c36a1fSMauro Carvalho Chehab * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
22a0c36a1fSMauro Carvalho Chehab * also available at:
23a0c36a1fSMauro Carvalho Chehab * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
24a0c36a1fSMauro Carvalho Chehab */
25a0c36a1fSMauro Carvalho Chehab
26a0c36a1fSMauro Carvalho Chehab #include <linux/module.h>
27a0c36a1fSMauro Carvalho Chehab #include <linux/init.h>
28a0c36a1fSMauro Carvalho Chehab #include <linux/pci.h>
29a0c36a1fSMauro Carvalho Chehab #include <linux/pci_ids.h>
30a0c36a1fSMauro Carvalho Chehab #include <linux/slab.h>
313b918c12SRandy Dunlap #include <linux/delay.h>
32535e9c78SNils Carlson #include <linux/dmi.h>
33a0c36a1fSMauro Carvalho Chehab #include <linux/edac.h>
34a0c36a1fSMauro Carvalho Chehab #include <linux/mmzone.h>
35f4742949SMauro Carvalho Chehab #include <linux/smp.h>
364140c542SBorislav Petkov #include <asm/mce.h>
3714d2c083SMauro Carvalho Chehab #include <asm/processor.h>
384fad8098SSedat Dilek #include <asm/div64.h>
39a0c36a1fSMauro Carvalho Chehab
4078d88e8aSMauro Carvalho Chehab #include "edac_module.h"
41a0c36a1fSMauro Carvalho Chehab
4218c29002SMauro Carvalho Chehab /* Static vars */
4318c29002SMauro Carvalho Chehab static LIST_HEAD(i7core_edac_list);
4418c29002SMauro Carvalho Chehab static DEFINE_MUTEX(i7core_edac_lock);
4518c29002SMauro Carvalho Chehab static int probed;
4618c29002SMauro Carvalho Chehab
4754a08ab1SMauro Carvalho Chehab static int use_pci_fixup;
4854a08ab1SMauro Carvalho Chehab module_param(use_pci_fixup, int, 0444);
4954a08ab1SMauro Carvalho Chehab MODULE_PARM_DESC(use_pci_fixup, "Enable PCI fixup to seek for hidden devices");
50a0c36a1fSMauro Carvalho Chehab /*
51f4742949SMauro Carvalho Chehab * This is used for Nehalem-EP and Nehalem-EX devices, where the non-core
52f4742949SMauro Carvalho Chehab * registers start at bus 255, and are not reported by BIOS.
53f4742949SMauro Carvalho Chehab * We currently find devices with only 2 sockets. In order to support more QPI
54f4742949SMauro Carvalho Chehab * Quick Path Interconnect, just increment this number.
55f4742949SMauro Carvalho Chehab */
56f4742949SMauro Carvalho Chehab #define MAX_SOCKET_BUSES 2
57f4742949SMauro Carvalho Chehab
58f4742949SMauro Carvalho Chehab
59f4742949SMauro Carvalho Chehab /*
60a0c36a1fSMauro Carvalho Chehab * Alter this version for the module when modifications are made
61a0c36a1fSMauro Carvalho Chehab */
62152ba394SMichal Marek #define I7CORE_REVISION " Ver: 1.0.0"
63a0c36a1fSMauro Carvalho Chehab #define EDAC_MOD_STR "i7core_edac"
64a0c36a1fSMauro Carvalho Chehab
65a0c36a1fSMauro Carvalho Chehab /*
66a0c36a1fSMauro Carvalho Chehab * Debug macros
67a0c36a1fSMauro Carvalho Chehab */
68a0c36a1fSMauro Carvalho Chehab #define i7core_printk(level, fmt, arg...) \
69a0c36a1fSMauro Carvalho Chehab edac_printk(level, "i7core", fmt, ##arg)
70a0c36a1fSMauro Carvalho Chehab
71a0c36a1fSMauro Carvalho Chehab #define i7core_mc_printk(mci, level, fmt, arg...) \
72a0c36a1fSMauro Carvalho Chehab edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg)
73a0c36a1fSMauro Carvalho Chehab
74a0c36a1fSMauro Carvalho Chehab /*
75a0c36a1fSMauro Carvalho Chehab * i7core Memory Controller Registers
76a0c36a1fSMauro Carvalho Chehab */
77a0c36a1fSMauro Carvalho Chehab
78e9bd2e73SMauro Carvalho Chehab /* OFFSETS for Device 0 Function 0 */
79e9bd2e73SMauro Carvalho Chehab
80e9bd2e73SMauro Carvalho Chehab #define MC_CFG_CONTROL 0x90
81e8b6a127SSamuel Gabrielsson #define MC_CFG_UNLOCK 0x02
82e8b6a127SSamuel Gabrielsson #define MC_CFG_LOCK 0x00
83e9bd2e73SMauro Carvalho Chehab
84a0c36a1fSMauro Carvalho Chehab /* OFFSETS for Device 3 Function 0 */
85a0c36a1fSMauro Carvalho Chehab
86a0c36a1fSMauro Carvalho Chehab #define MC_CONTROL 0x48
87a0c36a1fSMauro Carvalho Chehab #define MC_STATUS 0x4c
88a0c36a1fSMauro Carvalho Chehab #define MC_MAX_DOD 0x64
89a0c36a1fSMauro Carvalho Chehab
90442305b1SMauro Carvalho Chehab /*
9115ed103aSDavid Mackey * OFFSETS for Device 3 Function 4, as indicated on Xeon 5500 datasheet:
92442305b1SMauro Carvalho Chehab * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
93442305b1SMauro Carvalho Chehab */
94442305b1SMauro Carvalho Chehab
95442305b1SMauro Carvalho Chehab #define MC_TEST_ERR_RCV1 0x60
96442305b1SMauro Carvalho Chehab #define DIMM2_COR_ERR(r) ((r) & 0x7fff)
97442305b1SMauro Carvalho Chehab
98442305b1SMauro Carvalho Chehab #define MC_TEST_ERR_RCV0 0x64
99442305b1SMauro Carvalho Chehab #define DIMM1_COR_ERR(r) (((r) >> 16) & 0x7fff)
100442305b1SMauro Carvalho Chehab #define DIMM0_COR_ERR(r) ((r) & 0x7fff)
101442305b1SMauro Carvalho Chehab
10215ed103aSDavid Mackey /* OFFSETS for Device 3 Function 2, as indicated on Xeon 5500 datasheet */
103e8b6a127SSamuel Gabrielsson #define MC_SSRCONTROL 0x48
104e8b6a127SSamuel Gabrielsson #define SSR_MODE_DISABLE 0x00
105e8b6a127SSamuel Gabrielsson #define SSR_MODE_ENABLE 0x01
106e8b6a127SSamuel Gabrielsson #define SSR_MODE_MASK 0x03
107e8b6a127SSamuel Gabrielsson
108e8b6a127SSamuel Gabrielsson #define MC_SCRUB_CONTROL 0x4c
109e8b6a127SSamuel Gabrielsson #define STARTSCRUB (1 << 24)
110535e9c78SNils Carlson #define SCRUBINTERVAL_MASK 0xffffff
111e8b6a127SSamuel Gabrielsson
112b4e8f0b6SMauro Carvalho Chehab #define MC_COR_ECC_CNT_0 0x80
113b4e8f0b6SMauro Carvalho Chehab #define MC_COR_ECC_CNT_1 0x84
114b4e8f0b6SMauro Carvalho Chehab #define MC_COR_ECC_CNT_2 0x88
115b4e8f0b6SMauro Carvalho Chehab #define MC_COR_ECC_CNT_3 0x8c
116b4e8f0b6SMauro Carvalho Chehab #define MC_COR_ECC_CNT_4 0x90
117b4e8f0b6SMauro Carvalho Chehab #define MC_COR_ECC_CNT_5 0x94
118b4e8f0b6SMauro Carvalho Chehab
119b4e8f0b6SMauro Carvalho Chehab #define DIMM_TOP_COR_ERR(r) (((r) >> 16) & 0x7fff)
120b4e8f0b6SMauro Carvalho Chehab #define DIMM_BOT_COR_ERR(r) ((r) & 0x7fff)
121b4e8f0b6SMauro Carvalho Chehab
122b4e8f0b6SMauro Carvalho Chehab
123a0c36a1fSMauro Carvalho Chehab /* OFFSETS for Devices 4,5 and 6 Function 0 */
124a0c36a1fSMauro Carvalho Chehab
1250b2b7b7eSMauro Carvalho Chehab #define MC_CHANNEL_DIMM_INIT_PARAMS 0x58
1260b2b7b7eSMauro Carvalho Chehab #define THREE_DIMMS_PRESENT (1 << 24)
1270b2b7b7eSMauro Carvalho Chehab #define SINGLE_QUAD_RANK_PRESENT (1 << 23)
1280b2b7b7eSMauro Carvalho Chehab #define QUAD_RANK_PRESENT (1 << 22)
1290b2b7b7eSMauro Carvalho Chehab #define REGISTERED_DIMM (1 << 15)
1300b2b7b7eSMauro Carvalho Chehab
131f122a892SMauro Carvalho Chehab #define MC_CHANNEL_MAPPER 0x60
132f122a892SMauro Carvalho Chehab #define RDLCH(r, ch) ((((r) >> (3 + (ch * 6))) & 0x07) - 1)
133f122a892SMauro Carvalho Chehab #define WRLCH(r, ch) ((((r) >> (ch * 6)) & 0x07) - 1)
134f122a892SMauro Carvalho Chehab
1350b2b7b7eSMauro Carvalho Chehab #define MC_CHANNEL_RANK_PRESENT 0x7c
1360b2b7b7eSMauro Carvalho Chehab #define RANK_PRESENT_MASK 0xffff
1370b2b7b7eSMauro Carvalho Chehab
138a0c36a1fSMauro Carvalho Chehab #define MC_CHANNEL_ADDR_MATCH 0xf0
139194a40feSMauro Carvalho Chehab #define MC_CHANNEL_ERROR_MASK 0xf8
140194a40feSMauro Carvalho Chehab #define MC_CHANNEL_ERROR_INJECT 0xfc
141194a40feSMauro Carvalho Chehab #define INJECT_ADDR_PARITY 0x10
142194a40feSMauro Carvalho Chehab #define INJECT_ECC 0x08
143194a40feSMauro Carvalho Chehab #define MASK_CACHELINE 0x06
144194a40feSMauro Carvalho Chehab #define MASK_FULL_CACHELINE 0x06
145194a40feSMauro Carvalho Chehab #define MASK_MSB32_CACHELINE 0x04
146194a40feSMauro Carvalho Chehab #define MASK_LSB32_CACHELINE 0x02
147194a40feSMauro Carvalho Chehab #define NO_MASK_CACHELINE 0x00
148194a40feSMauro Carvalho Chehab #define REPEAT_EN 0x01
149a0c36a1fSMauro Carvalho Chehab
1500b2b7b7eSMauro Carvalho Chehab /* OFFSETS for Devices 4,5 and 6 Function 1 */
151b990538aSMauro Carvalho Chehab
1520b2b7b7eSMauro Carvalho Chehab #define MC_DOD_CH_DIMM0 0x48
1530b2b7b7eSMauro Carvalho Chehab #define MC_DOD_CH_DIMM1 0x4c
1540b2b7b7eSMauro Carvalho Chehab #define MC_DOD_CH_DIMM2 0x50
1550b2b7b7eSMauro Carvalho Chehab #define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10))
1560b2b7b7eSMauro Carvalho Chehab #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10)
1570b2b7b7eSMauro Carvalho Chehab #define DIMM_PRESENT_MASK (1 << 9)
1580b2b7b7eSMauro Carvalho Chehab #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9)
159854d3349SMauro Carvalho Chehab #define MC_DOD_NUMBANK_MASK ((1 << 8) | (1 << 7))
160854d3349SMauro Carvalho Chehab #define MC_DOD_NUMBANK(x) (((x) & MC_DOD_NUMBANK_MASK) >> 7)
161854d3349SMauro Carvalho Chehab #define MC_DOD_NUMRANK_MASK ((1 << 6) | (1 << 5))
162854d3349SMauro Carvalho Chehab #define MC_DOD_NUMRANK(x) (((x) & MC_DOD_NUMRANK_MASK) >> 5)
1635566cb7cSMauro Carvalho Chehab #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3) | (1 << 2))
1645566cb7cSMauro Carvalho Chehab #define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 2)
165854d3349SMauro Carvalho Chehab #define MC_DOD_NUMCOL_MASK 3
166854d3349SMauro Carvalho Chehab #define MC_DOD_NUMCOL(x) ((x) & MC_DOD_NUMCOL_MASK)
1670b2b7b7eSMauro Carvalho Chehab
168f122a892SMauro Carvalho Chehab #define MC_RANK_PRESENT 0x7c
169f122a892SMauro Carvalho Chehab
1700b2b7b7eSMauro Carvalho Chehab #define MC_SAG_CH_0 0x80
1710b2b7b7eSMauro Carvalho Chehab #define MC_SAG_CH_1 0x84
1720b2b7b7eSMauro Carvalho Chehab #define MC_SAG_CH_2 0x88
1730b2b7b7eSMauro Carvalho Chehab #define MC_SAG_CH_3 0x8c
1740b2b7b7eSMauro Carvalho Chehab #define MC_SAG_CH_4 0x90
1750b2b7b7eSMauro Carvalho Chehab #define MC_SAG_CH_5 0x94
1760b2b7b7eSMauro Carvalho Chehab #define MC_SAG_CH_6 0x98
1770b2b7b7eSMauro Carvalho Chehab #define MC_SAG_CH_7 0x9c
1780b2b7b7eSMauro Carvalho Chehab
1790b2b7b7eSMauro Carvalho Chehab #define MC_RIR_LIMIT_CH_0 0x40
1800b2b7b7eSMauro Carvalho Chehab #define MC_RIR_LIMIT_CH_1 0x44
1810b2b7b7eSMauro Carvalho Chehab #define MC_RIR_LIMIT_CH_2 0x48
1820b2b7b7eSMauro Carvalho Chehab #define MC_RIR_LIMIT_CH_3 0x4C
1830b2b7b7eSMauro Carvalho Chehab #define MC_RIR_LIMIT_CH_4 0x50
1840b2b7b7eSMauro Carvalho Chehab #define MC_RIR_LIMIT_CH_5 0x54
1850b2b7b7eSMauro Carvalho Chehab #define MC_RIR_LIMIT_CH_6 0x58
1860b2b7b7eSMauro Carvalho Chehab #define MC_RIR_LIMIT_CH_7 0x5C
1870b2b7b7eSMauro Carvalho Chehab #define MC_RIR_LIMIT_MASK ((1 << 10) - 1)
1880b2b7b7eSMauro Carvalho Chehab
1890b2b7b7eSMauro Carvalho Chehab #define MC_RIR_WAY_CH 0x80
1900b2b7b7eSMauro Carvalho Chehab #define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7)
1910b2b7b7eSMauro Carvalho Chehab #define MC_RIR_WAY_RANK_MASK 0x7
1920b2b7b7eSMauro Carvalho Chehab
193a0c36a1fSMauro Carvalho Chehab /*
194a0c36a1fSMauro Carvalho Chehab * i7core structs
195a0c36a1fSMauro Carvalho Chehab */
196a0c36a1fSMauro Carvalho Chehab
197a0c36a1fSMauro Carvalho Chehab #define NUM_CHANS 3
198442305b1SMauro Carvalho Chehab #define MAX_DIMMS 3 /* Max DIMMS per channel */
199442305b1SMauro Carvalho Chehab #define MAX_MCR_FUNC 4
200442305b1SMauro Carvalho Chehab #define MAX_CHAN_FUNC 3
201a0c36a1fSMauro Carvalho Chehab
202a0c36a1fSMauro Carvalho Chehab struct i7core_info {
203a0c36a1fSMauro Carvalho Chehab u32 mc_control;
204a0c36a1fSMauro Carvalho Chehab u32 mc_status;
205a0c36a1fSMauro Carvalho Chehab u32 max_dod;
206f122a892SMauro Carvalho Chehab u32 ch_map;
207a0c36a1fSMauro Carvalho Chehab };
208a0c36a1fSMauro Carvalho Chehab
209194a40feSMauro Carvalho Chehab
210194a40feSMauro Carvalho Chehab struct i7core_inject {
211194a40feSMauro Carvalho Chehab int enable;
212194a40feSMauro Carvalho Chehab
213194a40feSMauro Carvalho Chehab u32 section;
214194a40feSMauro Carvalho Chehab u32 type;
215194a40feSMauro Carvalho Chehab u32 eccmask;
216194a40feSMauro Carvalho Chehab
217194a40feSMauro Carvalho Chehab /* Error address mask */
218194a40feSMauro Carvalho Chehab int channel, dimm, rank, bank, page, col;
219194a40feSMauro Carvalho Chehab };
220194a40feSMauro Carvalho Chehab
2210b2b7b7eSMauro Carvalho Chehab struct i7core_channel {
2220bf09e82SMauro Carvalho Chehab bool is_3dimms_present;
2230bf09e82SMauro Carvalho Chehab bool is_single_4rank;
2240bf09e82SMauro Carvalho Chehab bool has_4rank;
2250b2b7b7eSMauro Carvalho Chehab u32 dimms;
2260b2b7b7eSMauro Carvalho Chehab };
2270b2b7b7eSMauro Carvalho Chehab
2288f331907SMauro Carvalho Chehab struct pci_id_descr {
2298f331907SMauro Carvalho Chehab int dev;
2308f331907SMauro Carvalho Chehab int func;
2318f331907SMauro Carvalho Chehab int dev_id;
232de06eeefSMauro Carvalho Chehab int optional;
2338f331907SMauro Carvalho Chehab };
2348f331907SMauro Carvalho Chehab
235bd9e19caSVernon Mauery struct pci_id_table {
2361288c18fSMauro Carvalho Chehab const struct pci_id_descr *descr;
237bd9e19caSVernon Mauery int n_devs;
238bd9e19caSVernon Mauery };
239bd9e19caSVernon Mauery
240f4742949SMauro Carvalho Chehab struct i7core_dev {
241f4742949SMauro Carvalho Chehab struct list_head list;
242f4742949SMauro Carvalho Chehab u8 socket;
243f4742949SMauro Carvalho Chehab struct pci_dev **pdev;
244de06eeefSMauro Carvalho Chehab int n_devs;
245f4742949SMauro Carvalho Chehab struct mem_ctl_info *mci;
246f4742949SMauro Carvalho Chehab };
247f4742949SMauro Carvalho Chehab
248a0c36a1fSMauro Carvalho Chehab struct i7core_pvt {
249356f0a30SMauro Carvalho Chehab struct device *addrmatch_dev, *chancounts_dev;
2505c4cdb5aSMauro Carvalho Chehab
251f4742949SMauro Carvalho Chehab struct pci_dev *pci_noncore;
252f4742949SMauro Carvalho Chehab struct pci_dev *pci_mcr[MAX_MCR_FUNC + 1];
253f4742949SMauro Carvalho Chehab struct pci_dev *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1];
254f4742949SMauro Carvalho Chehab
255f4742949SMauro Carvalho Chehab struct i7core_dev *i7core_dev;
25667166af4SMauro Carvalho Chehab
257a0c36a1fSMauro Carvalho Chehab struct i7core_info info;
258194a40feSMauro Carvalho Chehab struct i7core_inject inject;
259f4742949SMauro Carvalho Chehab struct i7core_channel channel[NUM_CHANS];
26067166af4SMauro Carvalho Chehab
261f4742949SMauro Carvalho Chehab int ce_count_available;
262b4e8f0b6SMauro Carvalho Chehab
263b4e8f0b6SMauro Carvalho Chehab /* ECC corrected errors counts per udimm */
264f4742949SMauro Carvalho Chehab unsigned long udimm_ce_count[MAX_DIMMS];
265f4742949SMauro Carvalho Chehab int udimm_last_ce_count[MAX_DIMMS];
266b4e8f0b6SMauro Carvalho Chehab /* ECC corrected errors counts per rdimm */
267f4742949SMauro Carvalho Chehab unsigned long rdimm_ce_count[NUM_CHANS][MAX_DIMMS];
268f4742949SMauro Carvalho Chehab int rdimm_last_ce_count[NUM_CHANS][MAX_DIMMS];
269442305b1SMauro Carvalho Chehab
27027100db0SMauro Carvalho Chehab bool is_registered, enable_scrub;
27114d2c083SMauro Carvalho Chehab
272535e9c78SNils Carlson /* DCLK Frequency used for computing scrub rate */
273535e9c78SNils Carlson int dclk_freq;
274535e9c78SNils Carlson
275939747bdSMauro Carvalho Chehab /* Struct to control EDAC polling */
276939747bdSMauro Carvalho Chehab struct edac_pci_ctl_info *i7core_pci;
277a0c36a1fSMauro Carvalho Chehab };
278a0c36a1fSMauro Carvalho Chehab
2798f331907SMauro Carvalho Chehab #define PCI_DESCR(device, function, device_id) \
2808f331907SMauro Carvalho Chehab .dev = (device), \
2818f331907SMauro Carvalho Chehab .func = (function), \
2828f331907SMauro Carvalho Chehab .dev_id = (device_id)
2838f331907SMauro Carvalho Chehab
2841288c18fSMauro Carvalho Chehab static const struct pci_id_descr pci_dev_descr_i7core_nehalem[] = {
2858f331907SMauro Carvalho Chehab /* Memory controller */
2868f331907SMauro Carvalho Chehab { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
2878f331907SMauro Carvalho Chehab { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
288de06eeefSMauro Carvalho Chehab /* Exists only for RDIMM */
289de06eeefSMauro Carvalho Chehab { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS), .optional = 1 },
2908f331907SMauro Carvalho Chehab { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
2918f331907SMauro Carvalho Chehab
2928f331907SMauro Carvalho Chehab /* Channel 0 */
2938f331907SMauro Carvalho Chehab { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
2948f331907SMauro Carvalho Chehab { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
2958f331907SMauro Carvalho Chehab { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
2968f331907SMauro Carvalho Chehab { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
2978f331907SMauro Carvalho Chehab
2988f331907SMauro Carvalho Chehab /* Channel 1 */
2998f331907SMauro Carvalho Chehab { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
3008f331907SMauro Carvalho Chehab { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
3018f331907SMauro Carvalho Chehab { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
3028f331907SMauro Carvalho Chehab { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
3038f331907SMauro Carvalho Chehab
3048f331907SMauro Carvalho Chehab /* Channel 2 */
3058f331907SMauro Carvalho Chehab { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
3068f331907SMauro Carvalho Chehab { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
3078f331907SMauro Carvalho Chehab { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
3088f331907SMauro Carvalho Chehab { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
309224e871fSMauro Carvalho Chehab
310224e871fSMauro Carvalho Chehab /* Generic Non-core registers */
311224e871fSMauro Carvalho Chehab /*
312224e871fSMauro Carvalho Chehab * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
313224e871fSMauro Carvalho Chehab * On Xeon 55xx, however, it has a different id (8086:2c40). So,
314224e871fSMauro Carvalho Chehab * the probing code needs to test for the other address in case of
315224e871fSMauro Carvalho Chehab * failure of this one
316224e871fSMauro Carvalho Chehab */
317224e871fSMauro Carvalho Chehab { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NONCORE) },
318224e871fSMauro Carvalho Chehab
319a0c36a1fSMauro Carvalho Chehab };
3208f331907SMauro Carvalho Chehab
3211288c18fSMauro Carvalho Chehab static const struct pci_id_descr pci_dev_descr_lynnfield[] = {
32252a2e4fcSMauro Carvalho Chehab { PCI_DESCR( 3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR) },
32352a2e4fcSMauro Carvalho Chehab { PCI_DESCR( 3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD) },
32452a2e4fcSMauro Carvalho Chehab { PCI_DESCR( 3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST) },
32552a2e4fcSMauro Carvalho Chehab
32652a2e4fcSMauro Carvalho Chehab { PCI_DESCR( 4, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL) },
32752a2e4fcSMauro Carvalho Chehab { PCI_DESCR( 4, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR) },
32852a2e4fcSMauro Carvalho Chehab { PCI_DESCR( 4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK) },
32952a2e4fcSMauro Carvalho Chehab { PCI_DESCR( 4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC) },
33052a2e4fcSMauro Carvalho Chehab
331508fa179SMauro Carvalho Chehab { PCI_DESCR( 5, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL) },
332508fa179SMauro Carvalho Chehab { PCI_DESCR( 5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR) },
333508fa179SMauro Carvalho Chehab { PCI_DESCR( 5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK) },
334508fa179SMauro Carvalho Chehab { PCI_DESCR( 5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC) },
335224e871fSMauro Carvalho Chehab
336224e871fSMauro Carvalho Chehab /*
337224e871fSMauro Carvalho Chehab * This is the PCI device has an alternate address on some
338224e871fSMauro Carvalho Chehab * processors like Core i7 860
339224e871fSMauro Carvalho Chehab */
340224e871fSMauro Carvalho Chehab { PCI_DESCR( 0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE) },
34152a2e4fcSMauro Carvalho Chehab };
34252a2e4fcSMauro Carvalho Chehab
3431288c18fSMauro Carvalho Chehab static const struct pci_id_descr pci_dev_descr_i7core_westmere[] = {
344bd9e19caSVernon Mauery /* Memory controller */
345bd9e19caSVernon Mauery { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR_REV2) },
346bd9e19caSVernon Mauery { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD_REV2) },
347bd9e19caSVernon Mauery /* Exists only for RDIMM */
348bd9e19caSVernon Mauery { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_RAS_REV2), .optional = 1 },
349bd9e19caSVernon Mauery { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST_REV2) },
350bd9e19caSVernon Mauery
351bd9e19caSVernon Mauery /* Channel 0 */
352bd9e19caSVernon Mauery { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL_REV2) },
353bd9e19caSVernon Mauery { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR_REV2) },
354bd9e19caSVernon Mauery { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK_REV2) },
355bd9e19caSVernon Mauery { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC_REV2) },
356bd9e19caSVernon Mauery
357bd9e19caSVernon Mauery /* Channel 1 */
358bd9e19caSVernon Mauery { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL_REV2) },
359bd9e19caSVernon Mauery { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR_REV2) },
360bd9e19caSVernon Mauery { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK_REV2) },
361bd9e19caSVernon Mauery { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC_REV2) },
362bd9e19caSVernon Mauery
363bd9e19caSVernon Mauery /* Channel 2 */
364bd9e19caSVernon Mauery { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_CTRL_REV2) },
365bd9e19caSVernon Mauery { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_ADDR_REV2) },
366bd9e19caSVernon Mauery { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_RANK_REV2) },
367bd9e19caSVernon Mauery { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_TC_REV2) },
368224e871fSMauro Carvalho Chehab
369224e871fSMauro Carvalho Chehab /* Generic Non-core registers */
370224e871fSMauro Carvalho Chehab { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2) },
371224e871fSMauro Carvalho Chehab
372bd9e19caSVernon Mauery };
373bd9e19caSVernon Mauery
3741288c18fSMauro Carvalho Chehab #define PCI_ID_TABLE_ENTRY(A) { .descr=A, .n_devs = ARRAY_SIZE(A) }
3751288c18fSMauro Carvalho Chehab static const struct pci_id_table pci_dev_table[] = {
376bd9e19caSVernon Mauery PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_nehalem),
377bd9e19caSVernon Mauery PCI_ID_TABLE_ENTRY(pci_dev_descr_lynnfield),
378bd9e19caSVernon Mauery PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_westmere),
3793c52cc57SMauro Carvalho Chehab {0,} /* 0 terminated list. */
380bd9e19caSVernon Mauery };
381bd9e19caSVernon Mauery
3828f331907SMauro Carvalho Chehab /*
3838f331907SMauro Carvalho Chehab * pci_device_id table for which devices we are looking for
3848f331907SMauro Carvalho Chehab */
385ba935f40SJingoo Han static const struct pci_device_id i7core_pci_tbl[] = {
386d1fd4fb6SMauro Carvalho Chehab {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
387f05da2f7SMauro Carvalho Chehab {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_LINK0)},
3888f331907SMauro Carvalho Chehab {0,} /* 0 terminated list. */
3898f331907SMauro Carvalho Chehab };
3908f331907SMauro Carvalho Chehab
391a0c36a1fSMauro Carvalho Chehab /****************************************************************************
39215ed103aSDavid Mackey Ancillary status routines
393a0c36a1fSMauro Carvalho Chehab ****************************************************************************/
394a0c36a1fSMauro Carvalho Chehab
395a0c36a1fSMauro Carvalho Chehab /* MC_CONTROL bits */
396ef708b53SMauro Carvalho Chehab #define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
397ef708b53SMauro Carvalho Chehab #define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
398a0c36a1fSMauro Carvalho Chehab
399a0c36a1fSMauro Carvalho Chehab /* MC_STATUS bits */
40061053fdeSKeith Mannthey #define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 4))
401ef708b53SMauro Carvalho Chehab #define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
402a0c36a1fSMauro Carvalho Chehab
403a0c36a1fSMauro Carvalho Chehab /* MC_MAX_DOD read functions */
numdimms(u32 dimms)404854d3349SMauro Carvalho Chehab static inline int numdimms(u32 dimms)
405a0c36a1fSMauro Carvalho Chehab {
406854d3349SMauro Carvalho Chehab return (dimms & 0x3) + 1;
407a0c36a1fSMauro Carvalho Chehab }
408a0c36a1fSMauro Carvalho Chehab
numrank(u32 rank)409854d3349SMauro Carvalho Chehab static inline int numrank(u32 rank)
410a0c36a1fSMauro Carvalho Chehab {
411c31d34feSNiklas Söderlund static const int ranks[] = { 1, 2, 4, -EINVAL };
412a0c36a1fSMauro Carvalho Chehab
413854d3349SMauro Carvalho Chehab return ranks[rank & 0x3];
414a0c36a1fSMauro Carvalho Chehab }
415a0c36a1fSMauro Carvalho Chehab
numbank(u32 bank)416854d3349SMauro Carvalho Chehab static inline int numbank(u32 bank)
417a0c36a1fSMauro Carvalho Chehab {
418c31d34feSNiklas Söderlund static const int banks[] = { 4, 8, 16, -EINVAL };
419a0c36a1fSMauro Carvalho Chehab
420854d3349SMauro Carvalho Chehab return banks[bank & 0x3];
421a0c36a1fSMauro Carvalho Chehab }
422a0c36a1fSMauro Carvalho Chehab
numrow(u32 row)423854d3349SMauro Carvalho Chehab static inline int numrow(u32 row)
424a0c36a1fSMauro Carvalho Chehab {
425c31d34feSNiklas Söderlund static const int rows[] = {
426a0c36a1fSMauro Carvalho Chehab 1 << 12, 1 << 13, 1 << 14, 1 << 15,
427a0c36a1fSMauro Carvalho Chehab 1 << 16, -EINVAL, -EINVAL, -EINVAL,
428a0c36a1fSMauro Carvalho Chehab };
429a0c36a1fSMauro Carvalho Chehab
430854d3349SMauro Carvalho Chehab return rows[row & 0x7];
431a0c36a1fSMauro Carvalho Chehab }
432a0c36a1fSMauro Carvalho Chehab
numcol(u32 col)433854d3349SMauro Carvalho Chehab static inline int numcol(u32 col)
434a0c36a1fSMauro Carvalho Chehab {
435c31d34feSNiklas Söderlund static const int cols[] = {
436a0c36a1fSMauro Carvalho Chehab 1 << 10, 1 << 11, 1 << 12, -EINVAL,
437a0c36a1fSMauro Carvalho Chehab };
438854d3349SMauro Carvalho Chehab return cols[col & 0x3];
439a0c36a1fSMauro Carvalho Chehab }
440a0c36a1fSMauro Carvalho Chehab
get_i7core_dev(u8 socket)441f4742949SMauro Carvalho Chehab static struct i7core_dev *get_i7core_dev(u8 socket)
44266607706SMauro Carvalho Chehab {
44366607706SMauro Carvalho Chehab struct i7core_dev *i7core_dev;
44466607706SMauro Carvalho Chehab
44566607706SMauro Carvalho Chehab list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
44666607706SMauro Carvalho Chehab if (i7core_dev->socket == socket)
44766607706SMauro Carvalho Chehab return i7core_dev;
44866607706SMauro Carvalho Chehab }
44966607706SMauro Carvalho Chehab
45066607706SMauro Carvalho Chehab return NULL;
45166607706SMauro Carvalho Chehab }
45266607706SMauro Carvalho Chehab
alloc_i7core_dev(u8 socket,const struct pci_id_table * table)453848b2f7eSHidetoshi Seto static struct i7core_dev *alloc_i7core_dev(u8 socket,
454848b2f7eSHidetoshi Seto const struct pci_id_table *table)
455848b2f7eSHidetoshi Seto {
456848b2f7eSHidetoshi Seto struct i7core_dev *i7core_dev;
457848b2f7eSHidetoshi Seto
458848b2f7eSHidetoshi Seto i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
459848b2f7eSHidetoshi Seto if (!i7core_dev)
460848b2f7eSHidetoshi Seto return NULL;
461848b2f7eSHidetoshi Seto
4626396bb22SKees Cook i7core_dev->pdev = kcalloc(table->n_devs, sizeof(*i7core_dev->pdev),
463848b2f7eSHidetoshi Seto GFP_KERNEL);
464848b2f7eSHidetoshi Seto if (!i7core_dev->pdev) {
465848b2f7eSHidetoshi Seto kfree(i7core_dev);
466848b2f7eSHidetoshi Seto return NULL;
467848b2f7eSHidetoshi Seto }
468848b2f7eSHidetoshi Seto
469848b2f7eSHidetoshi Seto i7core_dev->socket = socket;
470848b2f7eSHidetoshi Seto i7core_dev->n_devs = table->n_devs;
471848b2f7eSHidetoshi Seto list_add_tail(&i7core_dev->list, &i7core_edac_list);
472848b2f7eSHidetoshi Seto
473848b2f7eSHidetoshi Seto return i7core_dev;
474848b2f7eSHidetoshi Seto }
475848b2f7eSHidetoshi Seto
free_i7core_dev(struct i7core_dev * i7core_dev)4762aa9be44SHidetoshi Seto static void free_i7core_dev(struct i7core_dev *i7core_dev)
4772aa9be44SHidetoshi Seto {
4782aa9be44SHidetoshi Seto list_del(&i7core_dev->list);
4792aa9be44SHidetoshi Seto kfree(i7core_dev->pdev);
4802aa9be44SHidetoshi Seto kfree(i7core_dev);
4812aa9be44SHidetoshi Seto }
4822aa9be44SHidetoshi Seto
483a0c36a1fSMauro Carvalho Chehab /****************************************************************************
484a0c36a1fSMauro Carvalho Chehab Memory check routines
485a0c36a1fSMauro Carvalho Chehab ****************************************************************************/
486ef708b53SMauro Carvalho Chehab
get_dimm_config(struct mem_ctl_info * mci)487084a4fccSMauro Carvalho Chehab static int get_dimm_config(struct mem_ctl_info *mci)
488a0c36a1fSMauro Carvalho Chehab {
489a0c36a1fSMauro Carvalho Chehab struct i7core_pvt *pvt = mci->pvt_info;
490854d3349SMauro Carvalho Chehab struct pci_dev *pdev;
491ba6c5c62SMauro Carvalho Chehab int i, j;
4921c6fed80SMauro Carvalho Chehab enum edac_type mode;
493854d3349SMauro Carvalho Chehab enum mem_type mtype;
494084a4fccSMauro Carvalho Chehab struct dimm_info *dimm;
495a0c36a1fSMauro Carvalho Chehab
496854d3349SMauro Carvalho Chehab /* Get data from the MC register, function 0 */
497f4742949SMauro Carvalho Chehab pdev = pvt->pci_mcr[0];
4987dd6953cSMauro Carvalho Chehab if (!pdev)
4998f331907SMauro Carvalho Chehab return -ENODEV;
5008f331907SMauro Carvalho Chehab
501f122a892SMauro Carvalho Chehab /* Device 3 function 0 reads */
5027dd6953cSMauro Carvalho Chehab pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
5037dd6953cSMauro Carvalho Chehab pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
5047dd6953cSMauro Carvalho Chehab pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
5057dd6953cSMauro Carvalho Chehab pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
506f122a892SMauro Carvalho Chehab
507956b9ba1SJoe Perches edac_dbg(0, "QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
508956b9ba1SJoe Perches pvt->i7core_dev->socket, pvt->info.mc_control,
509956b9ba1SJoe Perches pvt->info.mc_status, pvt->info.max_dod, pvt->info.ch_map);
510a0c36a1fSMauro Carvalho Chehab
5111c6fed80SMauro Carvalho Chehab if (ECC_ENABLED(pvt)) {
512956b9ba1SJoe Perches edac_dbg(0, "ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
5131c6fed80SMauro Carvalho Chehab if (ECCx8(pvt))
5141c6fed80SMauro Carvalho Chehab mode = EDAC_S8ECD8ED;
515a0c36a1fSMauro Carvalho Chehab else
5161c6fed80SMauro Carvalho Chehab mode = EDAC_S4ECD4ED;
5171c6fed80SMauro Carvalho Chehab } else {
518956b9ba1SJoe Perches edac_dbg(0, "ECC disabled\n");
5191c6fed80SMauro Carvalho Chehab mode = EDAC_NONE;
5201c6fed80SMauro Carvalho Chehab }
521a0c36a1fSMauro Carvalho Chehab
522a0c36a1fSMauro Carvalho Chehab /* FIXME: need to handle the error codes */
523956b9ba1SJoe Perches edac_dbg(0, "DOD Max limits: DIMMS: %d, %d-ranked, %d-banked x%x x 0x%x\n",
524854d3349SMauro Carvalho Chehab numdimms(pvt->info.max_dod),
525854d3349SMauro Carvalho Chehab numrank(pvt->info.max_dod >> 2),
526276b824cSMauro Carvalho Chehab numbank(pvt->info.max_dod >> 4),
527854d3349SMauro Carvalho Chehab numrow(pvt->info.max_dod >> 6),
528854d3349SMauro Carvalho Chehab numcol(pvt->info.max_dod >> 9));
529a0c36a1fSMauro Carvalho Chehab
5300b2b7b7eSMauro Carvalho Chehab for (i = 0; i < NUM_CHANS; i++) {
531854d3349SMauro Carvalho Chehab u32 data, dimm_dod[3], value[8];
5320b2b7b7eSMauro Carvalho Chehab
53352a2e4fcSMauro Carvalho Chehab if (!pvt->pci_ch[i][0])
53452a2e4fcSMauro Carvalho Chehab continue;
53552a2e4fcSMauro Carvalho Chehab
5360b2b7b7eSMauro Carvalho Chehab if (!CH_ACTIVE(pvt, i)) {
537956b9ba1SJoe Perches edac_dbg(0, "Channel %i is not active\n", i);
5380b2b7b7eSMauro Carvalho Chehab continue;
5390b2b7b7eSMauro Carvalho Chehab }
5400b2b7b7eSMauro Carvalho Chehab if (CH_DISABLED(pvt, i)) {
541956b9ba1SJoe Perches edac_dbg(0, "Channel %i is disabled\n", i);
5420b2b7b7eSMauro Carvalho Chehab continue;
5430b2b7b7eSMauro Carvalho Chehab }
5440b2b7b7eSMauro Carvalho Chehab
545f122a892SMauro Carvalho Chehab /* Devices 4-6 function 0 */
546f4742949SMauro Carvalho Chehab pci_read_config_dword(pvt->pci_ch[i][0],
5470b2b7b7eSMauro Carvalho Chehab MC_CHANNEL_DIMM_INIT_PARAMS, &data);
5480b2b7b7eSMauro Carvalho Chehab
5490bf09e82SMauro Carvalho Chehab
5500bf09e82SMauro Carvalho Chehab if (data & THREE_DIMMS_PRESENT)
5510bf09e82SMauro Carvalho Chehab pvt->channel[i].is_3dimms_present = true;
5520bf09e82SMauro Carvalho Chehab
5530bf09e82SMauro Carvalho Chehab if (data & SINGLE_QUAD_RANK_PRESENT)
5540bf09e82SMauro Carvalho Chehab pvt->channel[i].is_single_4rank = true;
5550bf09e82SMauro Carvalho Chehab
5560bf09e82SMauro Carvalho Chehab if (data & QUAD_RANK_PRESENT)
5570bf09e82SMauro Carvalho Chehab pvt->channel[i].has_4rank = true;
5580b2b7b7eSMauro Carvalho Chehab
559854d3349SMauro Carvalho Chehab if (data & REGISTERED_DIMM)
560854d3349SMauro Carvalho Chehab mtype = MEM_RDDR3;
56114d2c083SMauro Carvalho Chehab else
562854d3349SMauro Carvalho Chehab mtype = MEM_DDR3;
563854d3349SMauro Carvalho Chehab
564854d3349SMauro Carvalho Chehab /* Devices 4-6 function 1 */
565f4742949SMauro Carvalho Chehab pci_read_config_dword(pvt->pci_ch[i][1],
566854d3349SMauro Carvalho Chehab MC_DOD_CH_DIMM0, &dimm_dod[0]);
567f4742949SMauro Carvalho Chehab pci_read_config_dword(pvt->pci_ch[i][1],
568854d3349SMauro Carvalho Chehab MC_DOD_CH_DIMM1, &dimm_dod[1]);
569f4742949SMauro Carvalho Chehab pci_read_config_dword(pvt->pci_ch[i][1],
570854d3349SMauro Carvalho Chehab MC_DOD_CH_DIMM2, &dimm_dod[2]);
5710b2b7b7eSMauro Carvalho Chehab
572956b9ba1SJoe Perches edac_dbg(0, "Ch%d phy rd%d, wr%d (0x%08x): %s%s%s%cDIMMs\n",
5731c6fed80SMauro Carvalho Chehab i,
5741c6fed80SMauro Carvalho Chehab RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
5751c6fed80SMauro Carvalho Chehab data,
5760bf09e82SMauro Carvalho Chehab pvt->channel[i].is_3dimms_present ? "3DIMMS " : "",
5770bf09e82SMauro Carvalho Chehab pvt->channel[i].is_3dimms_present ? "SINGLE_4R " : "",
5780bf09e82SMauro Carvalho Chehab pvt->channel[i].has_4rank ? "HAS_4R " : "",
579854d3349SMauro Carvalho Chehab (data & REGISTERED_DIMM) ? 'R' : 'U');
580854d3349SMauro Carvalho Chehab
581854d3349SMauro Carvalho Chehab for (j = 0; j < 3; j++) {
582854d3349SMauro Carvalho Chehab u32 banks, ranks, rows, cols;
5835566cb7cSMauro Carvalho Chehab u32 size, npages;
584854d3349SMauro Carvalho Chehab
585854d3349SMauro Carvalho Chehab if (!DIMM_PRESENT(dimm_dod[j]))
586854d3349SMauro Carvalho Chehab continue;
587854d3349SMauro Carvalho Chehab
588bc9ad9e4SRobert Richter dimm = edac_get_dimm(mci, i, j, 0);
589854d3349SMauro Carvalho Chehab banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
590854d3349SMauro Carvalho Chehab ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
591854d3349SMauro Carvalho Chehab rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
592854d3349SMauro Carvalho Chehab cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
593854d3349SMauro Carvalho Chehab
5945566cb7cSMauro Carvalho Chehab /* DDR3 has 8 I/O banks */
5955566cb7cSMauro Carvalho Chehab size = (rows * cols * banks * ranks) >> (20 - 3);
5965566cb7cSMauro Carvalho Chehab
5976f6da136SQiuxu Zhuo edac_dbg(0, "\tdimm %d %d MiB offset: %x, bank: %d, rank: %d, row: %#x, col: %#x\n",
59817cb7b0cSMauro Carvalho Chehab j, size,
599854d3349SMauro Carvalho Chehab RANKOFFSET(dimm_dod[j]),
600854d3349SMauro Carvalho Chehab banks, ranks, rows, cols);
601854d3349SMauro Carvalho Chehab
602e9144601SMauro Carvalho Chehab npages = MiB_TO_PAGES(size);
6035566cb7cSMauro Carvalho Chehab
604a895bf8bSMauro Carvalho Chehab dimm->nr_pages = npages;
605b4e8f0b6SMauro Carvalho Chehab
606854d3349SMauro Carvalho Chehab switch (banks) {
607854d3349SMauro Carvalho Chehab case 4:
608084a4fccSMauro Carvalho Chehab dimm->dtype = DEV_X4;
609854d3349SMauro Carvalho Chehab break;
610854d3349SMauro Carvalho Chehab case 8:
611084a4fccSMauro Carvalho Chehab dimm->dtype = DEV_X8;
612854d3349SMauro Carvalho Chehab break;
613854d3349SMauro Carvalho Chehab case 16:
614084a4fccSMauro Carvalho Chehab dimm->dtype = DEV_X16;
615854d3349SMauro Carvalho Chehab break;
616854d3349SMauro Carvalho Chehab default:
617084a4fccSMauro Carvalho Chehab dimm->dtype = DEV_UNKNOWN;
618854d3349SMauro Carvalho Chehab }
619854d3349SMauro Carvalho Chehab
620084a4fccSMauro Carvalho Chehab snprintf(dimm->label, sizeof(dimm->label),
621767ba4a5SMauro Carvalho Chehab "CPU#%uChannel#%u_DIMM#%u",
622767ba4a5SMauro Carvalho Chehab pvt->i7core_dev->socket, i, j);
623084a4fccSMauro Carvalho Chehab dimm->grain = 8;
624084a4fccSMauro Carvalho Chehab dimm->edac_mode = mode;
625084a4fccSMauro Carvalho Chehab dimm->mtype = mtype;
626854d3349SMauro Carvalho Chehab }
6277dd6953cSMauro Carvalho Chehab
6287dd6953cSMauro Carvalho Chehab pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
6297dd6953cSMauro Carvalho Chehab pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
6307dd6953cSMauro Carvalho Chehab pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
6317dd6953cSMauro Carvalho Chehab pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
6327dd6953cSMauro Carvalho Chehab pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
6337dd6953cSMauro Carvalho Chehab pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
6347dd6953cSMauro Carvalho Chehab pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
6357dd6953cSMauro Carvalho Chehab pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
636956b9ba1SJoe Perches edac_dbg(1, "\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
6377dd6953cSMauro Carvalho Chehab for (j = 0; j < 8; j++)
638956b9ba1SJoe Perches edac_dbg(1, "\t\t%#x\t%#x\t%#x\n",
6397dd6953cSMauro Carvalho Chehab (value[j] >> 27) & 0x1,
6407dd6953cSMauro Carvalho Chehab (value[j] >> 24) & 0x7,
64180b8ce89SDavid Sterba (value[j] & ((1 << 24) - 1)));
6420b2b7b7eSMauro Carvalho Chehab }
6430b2b7b7eSMauro Carvalho Chehab
644a0c36a1fSMauro Carvalho Chehab return 0;
645a0c36a1fSMauro Carvalho Chehab }
646a0c36a1fSMauro Carvalho Chehab
647a0c36a1fSMauro Carvalho Chehab /****************************************************************************
648194a40feSMauro Carvalho Chehab Error insertion routines
649194a40feSMauro Carvalho Chehab ****************************************************************************/
650194a40feSMauro Carvalho Chehab
6515c4cdb5aSMauro Carvalho Chehab #define to_mci(k) container_of(k, struct mem_ctl_info, dev)
6525c4cdb5aSMauro Carvalho Chehab
653194a40feSMauro Carvalho Chehab /* The i7core has independent error injection features per channel.
654194a40feSMauro Carvalho Chehab However, to have a simpler code, we don't allow enabling error injection
655194a40feSMauro Carvalho Chehab on more than one channel.
656194a40feSMauro Carvalho Chehab Also, since a change at an inject parameter will be applied only at enable,
657194a40feSMauro Carvalho Chehab we're disabling error injection on all write calls to the sysfs nodes that
658194a40feSMauro Carvalho Chehab controls the error code injection.
659194a40feSMauro Carvalho Chehab */
disable_inject(const struct mem_ctl_info * mci)6601288c18fSMauro Carvalho Chehab static int disable_inject(const struct mem_ctl_info *mci)
661194a40feSMauro Carvalho Chehab {
662194a40feSMauro Carvalho Chehab struct i7core_pvt *pvt = mci->pvt_info;
663194a40feSMauro Carvalho Chehab
664194a40feSMauro Carvalho Chehab pvt->inject.enable = 0;
665194a40feSMauro Carvalho Chehab
666f4742949SMauro Carvalho Chehab if (!pvt->pci_ch[pvt->inject.channel][0])
6678f331907SMauro Carvalho Chehab return -ENODEV;
6688f331907SMauro Carvalho Chehab
669f4742949SMauro Carvalho Chehab pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
6704157d9f5SMauro Carvalho Chehab MC_CHANNEL_ERROR_INJECT, 0);
6718f331907SMauro Carvalho Chehab
6728f331907SMauro Carvalho Chehab return 0;
673194a40feSMauro Carvalho Chehab }
674194a40feSMauro Carvalho Chehab
675194a40feSMauro Carvalho Chehab /*
676194a40feSMauro Carvalho Chehab * i7core inject inject.section
677194a40feSMauro Carvalho Chehab *
678194a40feSMauro Carvalho Chehab * accept and store error injection inject.section value
679194a40feSMauro Carvalho Chehab * bit 0 - refers to the lower 32-byte half cacheline
680194a40feSMauro Carvalho Chehab * bit 1 - refers to the upper 32-byte half cacheline
681194a40feSMauro Carvalho Chehab */
i7core_inject_section_store(struct device * dev,struct device_attribute * mattr,const char * data,size_t count)6825c4cdb5aSMauro Carvalho Chehab static ssize_t i7core_inject_section_store(struct device *dev,
6835c4cdb5aSMauro Carvalho Chehab struct device_attribute *mattr,
684194a40feSMauro Carvalho Chehab const char *data, size_t count)
685194a40feSMauro Carvalho Chehab {
6865c4cdb5aSMauro Carvalho Chehab struct mem_ctl_info *mci = to_mci(dev);
687194a40feSMauro Carvalho Chehab struct i7core_pvt *pvt = mci->pvt_info;
688194a40feSMauro Carvalho Chehab unsigned long value;
689194a40feSMauro Carvalho Chehab int rc;
690194a40feSMauro Carvalho Chehab
691194a40feSMauro Carvalho Chehab if (pvt->inject.enable)
692194a40feSMauro Carvalho Chehab disable_inject(mci);
693194a40feSMauro Carvalho Chehab
694c7f62fc8SJingoo Han rc = kstrtoul(data, 10, &value);
695194a40feSMauro Carvalho Chehab if ((rc < 0) || (value > 3))
6962068def5SMauro Carvalho Chehab return -EIO;
697194a40feSMauro Carvalho Chehab
698194a40feSMauro Carvalho Chehab pvt->inject.section = (u32) value;
699194a40feSMauro Carvalho Chehab return count;
700194a40feSMauro Carvalho Chehab }
701194a40feSMauro Carvalho Chehab
i7core_inject_section_show(struct device * dev,struct device_attribute * mattr,char * data)7025c4cdb5aSMauro Carvalho Chehab static ssize_t i7core_inject_section_show(struct device *dev,
7035c4cdb5aSMauro Carvalho Chehab struct device_attribute *mattr,
704194a40feSMauro Carvalho Chehab char *data)
705194a40feSMauro Carvalho Chehab {
7065c4cdb5aSMauro Carvalho Chehab struct mem_ctl_info *mci = to_mci(dev);
707194a40feSMauro Carvalho Chehab struct i7core_pvt *pvt = mci->pvt_info;
708194a40feSMauro Carvalho Chehab return sprintf(data, "0x%08x\n", pvt->inject.section);
709194a40feSMauro Carvalho Chehab }
710194a40feSMauro Carvalho Chehab
711194a40feSMauro Carvalho Chehab /*
712194a40feSMauro Carvalho Chehab * i7core inject.type
713194a40feSMauro Carvalho Chehab *
714194a40feSMauro Carvalho Chehab * accept and store error injection inject.section value
715194a40feSMauro Carvalho Chehab * bit 0 - repeat enable - Enable error repetition
716194a40feSMauro Carvalho Chehab * bit 1 - inject ECC error
717194a40feSMauro Carvalho Chehab * bit 2 - inject parity error
718194a40feSMauro Carvalho Chehab */
i7core_inject_type_store(struct device * dev,struct device_attribute * mattr,const char * data,size_t count)7195c4cdb5aSMauro Carvalho Chehab static ssize_t i7core_inject_type_store(struct device *dev,
7205c4cdb5aSMauro Carvalho Chehab struct device_attribute *mattr,
721194a40feSMauro Carvalho Chehab const char *data, size_t count)
722194a40feSMauro Carvalho Chehab {
7235c4cdb5aSMauro Carvalho Chehab struct mem_ctl_info *mci = to_mci(dev);
724194a40feSMauro Carvalho Chehab struct i7core_pvt *pvt = mci->pvt_info;
725194a40feSMauro Carvalho Chehab unsigned long value;
726194a40feSMauro Carvalho Chehab int rc;
727194a40feSMauro Carvalho Chehab
728194a40feSMauro Carvalho Chehab if (pvt->inject.enable)
729194a40feSMauro Carvalho Chehab disable_inject(mci);
730194a40feSMauro Carvalho Chehab
731c7f62fc8SJingoo Han rc = kstrtoul(data, 10, &value);
732194a40feSMauro Carvalho Chehab if ((rc < 0) || (value > 7))
7332068def5SMauro Carvalho Chehab return -EIO;
734194a40feSMauro Carvalho Chehab
735194a40feSMauro Carvalho Chehab pvt->inject.type = (u32) value;
736194a40feSMauro Carvalho Chehab return count;
737194a40feSMauro Carvalho Chehab }
738194a40feSMauro Carvalho Chehab
i7core_inject_type_show(struct device * dev,struct device_attribute * mattr,char * data)7395c4cdb5aSMauro Carvalho Chehab static ssize_t i7core_inject_type_show(struct device *dev,
7405c4cdb5aSMauro Carvalho Chehab struct device_attribute *mattr,
741194a40feSMauro Carvalho Chehab char *data)
742194a40feSMauro Carvalho Chehab {
7435c4cdb5aSMauro Carvalho Chehab struct mem_ctl_info *mci = to_mci(dev);
744194a40feSMauro Carvalho Chehab struct i7core_pvt *pvt = mci->pvt_info;
7455c4cdb5aSMauro Carvalho Chehab
746194a40feSMauro Carvalho Chehab return sprintf(data, "0x%08x\n", pvt->inject.type);
747194a40feSMauro Carvalho Chehab }
748194a40feSMauro Carvalho Chehab
749194a40feSMauro Carvalho Chehab /*
750194a40feSMauro Carvalho Chehab * i7core_inject_inject.eccmask_store
751194a40feSMauro Carvalho Chehab *
752194a40feSMauro Carvalho Chehab * The type of error (UE/CE) will depend on the inject.eccmask value:
753194a40feSMauro Carvalho Chehab * Any bits set to a 1 will flip the corresponding ECC bit
754194a40feSMauro Carvalho Chehab * Correctable errors can be injected by flipping 1 bit or the bits within
755194a40feSMauro Carvalho Chehab * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
756194a40feSMauro Carvalho Chehab * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
757194a40feSMauro Carvalho Chehab * uncorrectable error to be injected.
758194a40feSMauro Carvalho Chehab */
i7core_inject_eccmask_store(struct device * dev,struct device_attribute * mattr,const char * data,size_t count)7595c4cdb5aSMauro Carvalho Chehab static ssize_t i7core_inject_eccmask_store(struct device *dev,
7605c4cdb5aSMauro Carvalho Chehab struct device_attribute *mattr,
761194a40feSMauro Carvalho Chehab const char *data, size_t count)
762194a40feSMauro Carvalho Chehab {
7635c4cdb5aSMauro Carvalho Chehab struct mem_ctl_info *mci = to_mci(dev);
764194a40feSMauro Carvalho Chehab struct i7core_pvt *pvt = mci->pvt_info;
765194a40feSMauro Carvalho Chehab unsigned long value;
766194a40feSMauro Carvalho Chehab int rc;
767194a40feSMauro Carvalho Chehab
768194a40feSMauro Carvalho Chehab if (pvt->inject.enable)
769194a40feSMauro Carvalho Chehab disable_inject(mci);
770194a40feSMauro Carvalho Chehab
771c7f62fc8SJingoo Han rc = kstrtoul(data, 10, &value);
772194a40feSMauro Carvalho Chehab if (rc < 0)
7732068def5SMauro Carvalho Chehab return -EIO;
774194a40feSMauro Carvalho Chehab
775194a40feSMauro Carvalho Chehab pvt->inject.eccmask = (u32) value;
776194a40feSMauro Carvalho Chehab return count;
777194a40feSMauro Carvalho Chehab }
778194a40feSMauro Carvalho Chehab
i7core_inject_eccmask_show(struct device * dev,struct device_attribute * mattr,char * data)7795c4cdb5aSMauro Carvalho Chehab static ssize_t i7core_inject_eccmask_show(struct device *dev,
7805c4cdb5aSMauro Carvalho Chehab struct device_attribute *mattr,
781194a40feSMauro Carvalho Chehab char *data)
782194a40feSMauro Carvalho Chehab {
7835c4cdb5aSMauro Carvalho Chehab struct mem_ctl_info *mci = to_mci(dev);
784194a40feSMauro Carvalho Chehab struct i7core_pvt *pvt = mci->pvt_info;
7855c4cdb5aSMauro Carvalho Chehab
786194a40feSMauro Carvalho Chehab return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
787194a40feSMauro Carvalho Chehab }
788194a40feSMauro Carvalho Chehab
789194a40feSMauro Carvalho Chehab /*
790194a40feSMauro Carvalho Chehab * i7core_addrmatch
791194a40feSMauro Carvalho Chehab *
792194a40feSMauro Carvalho Chehab * The type of error (UE/CE) will depend on the inject.eccmask value:
793194a40feSMauro Carvalho Chehab * Any bits set to a 1 will flip the corresponding ECC bit
794194a40feSMauro Carvalho Chehab * Correctable errors can be injected by flipping 1 bit or the bits within
795194a40feSMauro Carvalho Chehab * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
796194a40feSMauro Carvalho Chehab * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
797194a40feSMauro Carvalho Chehab * uncorrectable error to be injected.
798194a40feSMauro Carvalho Chehab */
799194a40feSMauro Carvalho Chehab
800a5538e53SMauro Carvalho Chehab #define DECLARE_ADDR_MATCH(param, limit) \
801a5538e53SMauro Carvalho Chehab static ssize_t i7core_inject_store_##param( \
8025c4cdb5aSMauro Carvalho Chehab struct device *dev, \
8035c4cdb5aSMauro Carvalho Chehab struct device_attribute *mattr, \
804a5538e53SMauro Carvalho Chehab const char *data, size_t count) \
805a5538e53SMauro Carvalho Chehab { \
80642709efbSPrarit Bhargava struct mem_ctl_info *mci = dev_get_drvdata(dev); \
807cc301b3aSMauro Carvalho Chehab struct i7core_pvt *pvt; \
808a5538e53SMauro Carvalho Chehab long value; \
809a5538e53SMauro Carvalho Chehab int rc; \
810a5538e53SMauro Carvalho Chehab \
811956b9ba1SJoe Perches edac_dbg(1, "\n"); \
812cc301b3aSMauro Carvalho Chehab pvt = mci->pvt_info; \
813cc301b3aSMauro Carvalho Chehab \
814a5538e53SMauro Carvalho Chehab if (pvt->inject.enable) \
815a5538e53SMauro Carvalho Chehab disable_inject(mci); \
816a5538e53SMauro Carvalho Chehab \
8174f87fad1SMauro Carvalho Chehab if (!strcasecmp(data, "any") || !strcasecmp(data, "any\n"))\
818a5538e53SMauro Carvalho Chehab value = -1; \
819a5538e53SMauro Carvalho Chehab else { \
820c7f62fc8SJingoo Han rc = kstrtoul(data, 10, &value); \
821a5538e53SMauro Carvalho Chehab if ((rc < 0) || (value >= limit)) \
822a5538e53SMauro Carvalho Chehab return -EIO; \
823a5538e53SMauro Carvalho Chehab } \
824a5538e53SMauro Carvalho Chehab \
825a5538e53SMauro Carvalho Chehab pvt->inject.param = value; \
826a5538e53SMauro Carvalho Chehab \
827a5538e53SMauro Carvalho Chehab return count; \
828a5538e53SMauro Carvalho Chehab } \
829a5538e53SMauro Carvalho Chehab \
830a5538e53SMauro Carvalho Chehab static ssize_t i7core_inject_show_##param( \
8315c4cdb5aSMauro Carvalho Chehab struct device *dev, \
8325c4cdb5aSMauro Carvalho Chehab struct device_attribute *mattr, \
833a5538e53SMauro Carvalho Chehab char *data) \
834a5538e53SMauro Carvalho Chehab { \
83542709efbSPrarit Bhargava struct mem_ctl_info *mci = dev_get_drvdata(dev); \
836cc301b3aSMauro Carvalho Chehab struct i7core_pvt *pvt; \
837cc301b3aSMauro Carvalho Chehab \
838cc301b3aSMauro Carvalho Chehab pvt = mci->pvt_info; \
839956b9ba1SJoe Perches edac_dbg(1, "pvt=%p\n", pvt); \
840a5538e53SMauro Carvalho Chehab if (pvt->inject.param < 0) \
841a5538e53SMauro Carvalho Chehab return sprintf(data, "any\n"); \
842a5538e53SMauro Carvalho Chehab else \
843a5538e53SMauro Carvalho Chehab return sprintf(data, "%d\n", pvt->inject.param);\
844194a40feSMauro Carvalho Chehab }
845194a40feSMauro Carvalho Chehab
846a5538e53SMauro Carvalho Chehab #define ATTR_ADDR_MATCH(param) \
8475c4cdb5aSMauro Carvalho Chehab static DEVICE_ATTR(param, S_IRUGO | S_IWUSR, \
8485c4cdb5aSMauro Carvalho Chehab i7core_inject_show_##param, \
8495c4cdb5aSMauro Carvalho Chehab i7core_inject_store_##param)
850194a40feSMauro Carvalho Chehab
851a5538e53SMauro Carvalho Chehab DECLARE_ADDR_MATCH(channel, 3);
852a5538e53SMauro Carvalho Chehab DECLARE_ADDR_MATCH(dimm, 3);
853a5538e53SMauro Carvalho Chehab DECLARE_ADDR_MATCH(rank, 4);
854a5538e53SMauro Carvalho Chehab DECLARE_ADDR_MATCH(bank, 32);
855a5538e53SMauro Carvalho Chehab DECLARE_ADDR_MATCH(page, 0x10000);
856a5538e53SMauro Carvalho Chehab DECLARE_ADDR_MATCH(col, 0x4000);
857194a40feSMauro Carvalho Chehab
8585c4cdb5aSMauro Carvalho Chehab ATTR_ADDR_MATCH(channel);
8595c4cdb5aSMauro Carvalho Chehab ATTR_ADDR_MATCH(dimm);
8605c4cdb5aSMauro Carvalho Chehab ATTR_ADDR_MATCH(rank);
8615c4cdb5aSMauro Carvalho Chehab ATTR_ADDR_MATCH(bank);
8625c4cdb5aSMauro Carvalho Chehab ATTR_ADDR_MATCH(page);
8635c4cdb5aSMauro Carvalho Chehab ATTR_ADDR_MATCH(col);
8645c4cdb5aSMauro Carvalho Chehab
write_and_test(struct pci_dev * dev,const int where,const u32 val)8651288c18fSMauro Carvalho Chehab static int write_and_test(struct pci_dev *dev, const int where, const u32 val)
866276b824cSMauro Carvalho Chehab {
867276b824cSMauro Carvalho Chehab u32 read;
868276b824cSMauro Carvalho Chehab int count;
869276b824cSMauro Carvalho Chehab
870956b9ba1SJoe Perches edac_dbg(0, "setting pci %02x:%02x.%x reg=%02x value=%08x\n",
8714157d9f5SMauro Carvalho Chehab dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
8724157d9f5SMauro Carvalho Chehab where, val);
8734157d9f5SMauro Carvalho Chehab
874276b824cSMauro Carvalho Chehab for (count = 0; count < 10; count++) {
875276b824cSMauro Carvalho Chehab if (count)
876276b824cSMauro Carvalho Chehab msleep(100);
877276b824cSMauro Carvalho Chehab pci_write_config_dword(dev, where, val);
878276b824cSMauro Carvalho Chehab pci_read_config_dword(dev, where, &read);
879276b824cSMauro Carvalho Chehab
880276b824cSMauro Carvalho Chehab if (read == val)
881276b824cSMauro Carvalho Chehab return 0;
882276b824cSMauro Carvalho Chehab }
883276b824cSMauro Carvalho Chehab
8844157d9f5SMauro Carvalho Chehab i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x "
8854157d9f5SMauro Carvalho Chehab "write=%08x. Read=%08x\n",
8864157d9f5SMauro Carvalho Chehab dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
8874157d9f5SMauro Carvalho Chehab where, val, read);
888276b824cSMauro Carvalho Chehab
889276b824cSMauro Carvalho Chehab return -EINVAL;
890276b824cSMauro Carvalho Chehab }
891276b824cSMauro Carvalho Chehab
892194a40feSMauro Carvalho Chehab /*
893194a40feSMauro Carvalho Chehab * This routine prepares the Memory Controller for error injection.
894194a40feSMauro Carvalho Chehab * The error will be injected when some process tries to write to the
895194a40feSMauro Carvalho Chehab * memory that matches the given criteria.
896194a40feSMauro Carvalho Chehab * The criteria can be set in terms of a mask where dimm, rank, bank, page
897194a40feSMauro Carvalho Chehab * and col can be specified.
898194a40feSMauro Carvalho Chehab * A -1 value for any of the mask items will make the MCU to ignore
899194a40feSMauro Carvalho Chehab * that matching criteria for error injection.
900194a40feSMauro Carvalho Chehab *
901194a40feSMauro Carvalho Chehab * It should be noticed that the error will only happen after a write operation
902194a40feSMauro Carvalho Chehab * on a memory that matches the condition. if REPEAT_EN is not enabled at
903194a40feSMauro Carvalho Chehab * inject mask, then it will produce just one error. Otherwise, it will repeat
904194a40feSMauro Carvalho Chehab * until the injectmask would be cleaned.
905194a40feSMauro Carvalho Chehab *
906194a40feSMauro Carvalho Chehab * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
907194a40feSMauro Carvalho Chehab * is reliable enough to check if the MC is using the
908194a40feSMauro Carvalho Chehab * three channels. However, this is not clear at the datasheet.
909194a40feSMauro Carvalho Chehab */
i7core_inject_enable_store(struct device * dev,struct device_attribute * mattr,const char * data,size_t count)9105c4cdb5aSMauro Carvalho Chehab static ssize_t i7core_inject_enable_store(struct device *dev,
9115c4cdb5aSMauro Carvalho Chehab struct device_attribute *mattr,
912194a40feSMauro Carvalho Chehab const char *data, size_t count)
913194a40feSMauro Carvalho Chehab {
9145c4cdb5aSMauro Carvalho Chehab struct mem_ctl_info *mci = to_mci(dev);
915194a40feSMauro Carvalho Chehab struct i7core_pvt *pvt = mci->pvt_info;
916194a40feSMauro Carvalho Chehab u32 injectmask;
917194a40feSMauro Carvalho Chehab u64 mask = 0;
918194a40feSMauro Carvalho Chehab int rc;
919194a40feSMauro Carvalho Chehab long enable;
920194a40feSMauro Carvalho Chehab
921f4742949SMauro Carvalho Chehab if (!pvt->pci_ch[pvt->inject.channel][0])
9228f331907SMauro Carvalho Chehab return 0;
9238f331907SMauro Carvalho Chehab
924c7f62fc8SJingoo Han rc = kstrtoul(data, 10, &enable);
925194a40feSMauro Carvalho Chehab if ((rc < 0))
926194a40feSMauro Carvalho Chehab return 0;
927194a40feSMauro Carvalho Chehab
928194a40feSMauro Carvalho Chehab if (enable) {
929194a40feSMauro Carvalho Chehab pvt->inject.enable = 1;
930194a40feSMauro Carvalho Chehab } else {
931194a40feSMauro Carvalho Chehab disable_inject(mci);
932194a40feSMauro Carvalho Chehab return count;
933194a40feSMauro Carvalho Chehab }
934194a40feSMauro Carvalho Chehab
935194a40feSMauro Carvalho Chehab /* Sets pvt->inject.dimm mask */
936194a40feSMauro Carvalho Chehab if (pvt->inject.dimm < 0)
937486dd09fSAlan Cox mask |= 1LL << 41;
938194a40feSMauro Carvalho Chehab else {
939f4742949SMauro Carvalho Chehab if (pvt->channel[pvt->inject.channel].dimms > 2)
940486dd09fSAlan Cox mask |= (pvt->inject.dimm & 0x3LL) << 35;
941194a40feSMauro Carvalho Chehab else
942486dd09fSAlan Cox mask |= (pvt->inject.dimm & 0x1LL) << 36;
943194a40feSMauro Carvalho Chehab }
944194a40feSMauro Carvalho Chehab
945194a40feSMauro Carvalho Chehab /* Sets pvt->inject.rank mask */
946194a40feSMauro Carvalho Chehab if (pvt->inject.rank < 0)
947486dd09fSAlan Cox mask |= 1LL << 40;
948194a40feSMauro Carvalho Chehab else {
949f4742949SMauro Carvalho Chehab if (pvt->channel[pvt->inject.channel].dimms > 2)
950486dd09fSAlan Cox mask |= (pvt->inject.rank & 0x1LL) << 34;
951194a40feSMauro Carvalho Chehab else
952486dd09fSAlan Cox mask |= (pvt->inject.rank & 0x3LL) << 34;
953194a40feSMauro Carvalho Chehab }
954194a40feSMauro Carvalho Chehab
955194a40feSMauro Carvalho Chehab /* Sets pvt->inject.bank mask */
956194a40feSMauro Carvalho Chehab if (pvt->inject.bank < 0)
957486dd09fSAlan Cox mask |= 1LL << 39;
958194a40feSMauro Carvalho Chehab else
959486dd09fSAlan Cox mask |= (pvt->inject.bank & 0x15LL) << 30;
960194a40feSMauro Carvalho Chehab
961194a40feSMauro Carvalho Chehab /* Sets pvt->inject.page mask */
962194a40feSMauro Carvalho Chehab if (pvt->inject.page < 0)
963486dd09fSAlan Cox mask |= 1LL << 38;
964194a40feSMauro Carvalho Chehab else
965486dd09fSAlan Cox mask |= (pvt->inject.page & 0xffff) << 14;
966194a40feSMauro Carvalho Chehab
967194a40feSMauro Carvalho Chehab /* Sets pvt->inject.column mask */
968194a40feSMauro Carvalho Chehab if (pvt->inject.col < 0)
969486dd09fSAlan Cox mask |= 1LL << 37;
970194a40feSMauro Carvalho Chehab else
971486dd09fSAlan Cox mask |= (pvt->inject.col & 0x3fff);
972194a40feSMauro Carvalho Chehab
973194a40feSMauro Carvalho Chehab /*
974194a40feSMauro Carvalho Chehab * bit 0: REPEAT_EN
975194a40feSMauro Carvalho Chehab * bits 1-2: MASK_HALF_CACHELINE
976194a40feSMauro Carvalho Chehab * bit 3: INJECT_ECC
977194a40feSMauro Carvalho Chehab * bit 4: INJECT_ADDR_PARITY
978194a40feSMauro Carvalho Chehab */
979194a40feSMauro Carvalho Chehab
9807b029d03SMauro Carvalho Chehab injectmask = (pvt->inject.type & 1) |
9817b029d03SMauro Carvalho Chehab (pvt->inject.section & 0x3) << 1 |
982194a40feSMauro Carvalho Chehab (pvt->inject.type & 0x6) << (3 - 1);
983194a40feSMauro Carvalho Chehab
984276b824cSMauro Carvalho Chehab /* Unlock writes to registers - this register is write only */
985f4742949SMauro Carvalho Chehab pci_write_config_dword(pvt->pci_noncore,
986276b824cSMauro Carvalho Chehab MC_CFG_CONTROL, 0x2);
987194a40feSMauro Carvalho Chehab
988f4742949SMauro Carvalho Chehab write_and_test(pvt->pci_ch[pvt->inject.channel][0],
989276b824cSMauro Carvalho Chehab MC_CHANNEL_ADDR_MATCH, mask);
990f4742949SMauro Carvalho Chehab write_and_test(pvt->pci_ch[pvt->inject.channel][0],
991276b824cSMauro Carvalho Chehab MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
992276b824cSMauro Carvalho Chehab
993f4742949SMauro Carvalho Chehab write_and_test(pvt->pci_ch[pvt->inject.channel][0],
994276b824cSMauro Carvalho Chehab MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
995276b824cSMauro Carvalho Chehab
996f4742949SMauro Carvalho Chehab write_and_test(pvt->pci_ch[pvt->inject.channel][0],
9974157d9f5SMauro Carvalho Chehab MC_CHANNEL_ERROR_INJECT, injectmask);
998276b824cSMauro Carvalho Chehab
999276b824cSMauro Carvalho Chehab /*
1000276b824cSMauro Carvalho Chehab * This is something undocumented, based on my tests
1001276b824cSMauro Carvalho Chehab * Without writing 8 to this register, errors aren't injected. Not sure
1002276b824cSMauro Carvalho Chehab * why.
1003276b824cSMauro Carvalho Chehab */
1004f4742949SMauro Carvalho Chehab pci_write_config_dword(pvt->pci_noncore,
1005276b824cSMauro Carvalho Chehab MC_CFG_CONTROL, 8);
1006276b824cSMauro Carvalho Chehab
1007956b9ba1SJoe Perches edac_dbg(0, "Error inject addr match 0x%016llx, ecc 0x%08x, inject 0x%08x\n",
1008194a40feSMauro Carvalho Chehab mask, pvt->inject.eccmask, injectmask);
1009194a40feSMauro Carvalho Chehab
10107b029d03SMauro Carvalho Chehab
1011194a40feSMauro Carvalho Chehab return count;
1012194a40feSMauro Carvalho Chehab }
1013194a40feSMauro Carvalho Chehab
i7core_inject_enable_show(struct device * dev,struct device_attribute * mattr,char * data)10145c4cdb5aSMauro Carvalho Chehab static ssize_t i7core_inject_enable_show(struct device *dev,
10155c4cdb5aSMauro Carvalho Chehab struct device_attribute *mattr,
1016194a40feSMauro Carvalho Chehab char *data)
1017194a40feSMauro Carvalho Chehab {
10185c4cdb5aSMauro Carvalho Chehab struct mem_ctl_info *mci = to_mci(dev);
1019194a40feSMauro Carvalho Chehab struct i7core_pvt *pvt = mci->pvt_info;
10207b029d03SMauro Carvalho Chehab u32 injectmask;
10217b029d03SMauro Carvalho Chehab
102252a2e4fcSMauro Carvalho Chehab if (!pvt->pci_ch[pvt->inject.channel][0])
102352a2e4fcSMauro Carvalho Chehab return 0;
102452a2e4fcSMauro Carvalho Chehab
1025f4742949SMauro Carvalho Chehab pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
10264157d9f5SMauro Carvalho Chehab MC_CHANNEL_ERROR_INJECT, &injectmask);
10277b029d03SMauro Carvalho Chehab
1028956b9ba1SJoe Perches edac_dbg(0, "Inject error read: 0x%018x\n", injectmask);
10297b029d03SMauro Carvalho Chehab
10307b029d03SMauro Carvalho Chehab if (injectmask & 0x0c)
10317b029d03SMauro Carvalho Chehab pvt->inject.enable = 1;
10327b029d03SMauro Carvalho Chehab
1033194a40feSMauro Carvalho Chehab return sprintf(data, "%d\n", pvt->inject.enable);
1034194a40feSMauro Carvalho Chehab }
1035194a40feSMauro Carvalho Chehab
1036f338d736SMauro Carvalho Chehab #define DECLARE_COUNTER(param) \
1037f338d736SMauro Carvalho Chehab static ssize_t i7core_show_counter_##param( \
10385c4cdb5aSMauro Carvalho Chehab struct device *dev, \
10395c4cdb5aSMauro Carvalho Chehab struct device_attribute *mattr, \
1040f338d736SMauro Carvalho Chehab char *data) \
1041f338d736SMauro Carvalho Chehab { \
104242709efbSPrarit Bhargava struct mem_ctl_info *mci = dev_get_drvdata(dev); \
1043f338d736SMauro Carvalho Chehab struct i7core_pvt *pvt = mci->pvt_info; \
1044f338d736SMauro Carvalho Chehab \
1045956b9ba1SJoe Perches edac_dbg(1, "\n"); \
1046f338d736SMauro Carvalho Chehab if (!pvt->ce_count_available || (pvt->is_registered)) \
1047f338d736SMauro Carvalho Chehab return sprintf(data, "data unavailable\n"); \
1048f338d736SMauro Carvalho Chehab return sprintf(data, "%lu\n", \
1049f338d736SMauro Carvalho Chehab pvt->udimm_ce_count[param]); \
1050d88b8507SMauro Carvalho Chehab }
1051442305b1SMauro Carvalho Chehab
1052f338d736SMauro Carvalho Chehab #define ATTR_COUNTER(param) \
10535c4cdb5aSMauro Carvalho Chehab static DEVICE_ATTR(udimm##param, S_IRUGO | S_IWUSR, \
10545c4cdb5aSMauro Carvalho Chehab i7core_show_counter_##param, \
10555c4cdb5aSMauro Carvalho Chehab NULL)
1056442305b1SMauro Carvalho Chehab
1057f338d736SMauro Carvalho Chehab DECLARE_COUNTER(0);
1058f338d736SMauro Carvalho Chehab DECLARE_COUNTER(1);
1059f338d736SMauro Carvalho Chehab DECLARE_COUNTER(2);
1060f338d736SMauro Carvalho Chehab
10615c4cdb5aSMauro Carvalho Chehab ATTR_COUNTER(0);
10625c4cdb5aSMauro Carvalho Chehab ATTR_COUNTER(1);
10635c4cdb5aSMauro Carvalho Chehab ATTR_COUNTER(2);
10645c4cdb5aSMauro Carvalho Chehab
1065194a40feSMauro Carvalho Chehab /*
10665c4cdb5aSMauro Carvalho Chehab * inject_addrmatch device sysfs struct
1067194a40feSMauro Carvalho Chehab */
1068a5538e53SMauro Carvalho Chehab
10695c4cdb5aSMauro Carvalho Chehab static struct attribute *i7core_addrmatch_attrs[] = {
10705c4cdb5aSMauro Carvalho Chehab &dev_attr_channel.attr,
10715c4cdb5aSMauro Carvalho Chehab &dev_attr_dimm.attr,
10725c4cdb5aSMauro Carvalho Chehab &dev_attr_rank.attr,
10735c4cdb5aSMauro Carvalho Chehab &dev_attr_bank.attr,
10745c4cdb5aSMauro Carvalho Chehab &dev_attr_page.attr,
10755c4cdb5aSMauro Carvalho Chehab &dev_attr_col.attr,
10765c4cdb5aSMauro Carvalho Chehab NULL
1077a5538e53SMauro Carvalho Chehab };
1078a5538e53SMauro Carvalho Chehab
10791c18be5aSArvind Yadav static const struct attribute_group addrmatch_grp = {
10805c4cdb5aSMauro Carvalho Chehab .attrs = i7core_addrmatch_attrs,
1081a5538e53SMauro Carvalho Chehab };
1082a5538e53SMauro Carvalho Chehab
10835c4cdb5aSMauro Carvalho Chehab static const struct attribute_group *addrmatch_groups[] = {
10845c4cdb5aSMauro Carvalho Chehab &addrmatch_grp,
10855c4cdb5aSMauro Carvalho Chehab NULL
1086f338d736SMauro Carvalho Chehab };
1087f338d736SMauro Carvalho Chehab
addrmatch_release(struct device * device)10885c4cdb5aSMauro Carvalho Chehab static void addrmatch_release(struct device *device)
1089194a40feSMauro Carvalho Chehab {
1090956b9ba1SJoe Perches edac_dbg(1, "Releasing device %s\n", dev_name(device));
1091356f0a30SMauro Carvalho Chehab kfree(device);
10925c4cdb5aSMauro Carvalho Chehab }
10935c4cdb5aSMauro Carvalho Chehab
1094b2b3e736SBhumika Goyal static const struct device_type addrmatch_type = {
10955c4cdb5aSMauro Carvalho Chehab .groups = addrmatch_groups,
10965c4cdb5aSMauro Carvalho Chehab .release = addrmatch_release,
10971288c18fSMauro Carvalho Chehab };
10981288c18fSMauro Carvalho Chehab
10995c4cdb5aSMauro Carvalho Chehab /*
11005c4cdb5aSMauro Carvalho Chehab * all_channel_counts sysfs struct
11015c4cdb5aSMauro Carvalho Chehab */
11025c4cdb5aSMauro Carvalho Chehab
11035c4cdb5aSMauro Carvalho Chehab static struct attribute *i7core_udimm_counters_attrs[] = {
11045c4cdb5aSMauro Carvalho Chehab &dev_attr_udimm0.attr,
11055c4cdb5aSMauro Carvalho Chehab &dev_attr_udimm1.attr,
11065c4cdb5aSMauro Carvalho Chehab &dev_attr_udimm2.attr,
11075c4cdb5aSMauro Carvalho Chehab NULL
1108194a40feSMauro Carvalho Chehab };
1109194a40feSMauro Carvalho Chehab
11101c18be5aSArvind Yadav static const struct attribute_group all_channel_counts_grp = {
11115c4cdb5aSMauro Carvalho Chehab .attrs = i7core_udimm_counters_attrs,
11125c4cdb5aSMauro Carvalho Chehab };
11135c4cdb5aSMauro Carvalho Chehab
11145c4cdb5aSMauro Carvalho Chehab static const struct attribute_group *all_channel_counts_groups[] = {
11155c4cdb5aSMauro Carvalho Chehab &all_channel_counts_grp,
11165c4cdb5aSMauro Carvalho Chehab NULL
11175c4cdb5aSMauro Carvalho Chehab };
11185c4cdb5aSMauro Carvalho Chehab
all_channel_counts_release(struct device * device)11195c4cdb5aSMauro Carvalho Chehab static void all_channel_counts_release(struct device *device)
11205c4cdb5aSMauro Carvalho Chehab {
1121956b9ba1SJoe Perches edac_dbg(1, "Releasing device %s\n", dev_name(device));
1122356f0a30SMauro Carvalho Chehab kfree(device);
11235c4cdb5aSMauro Carvalho Chehab }
11245c4cdb5aSMauro Carvalho Chehab
1125b2b3e736SBhumika Goyal static const struct device_type all_channel_counts_type = {
11265c4cdb5aSMauro Carvalho Chehab .groups = all_channel_counts_groups,
11275c4cdb5aSMauro Carvalho Chehab .release = all_channel_counts_release,
11285c4cdb5aSMauro Carvalho Chehab };
11295c4cdb5aSMauro Carvalho Chehab
11305c4cdb5aSMauro Carvalho Chehab /*
11315c4cdb5aSMauro Carvalho Chehab * inject sysfs attributes
11325c4cdb5aSMauro Carvalho Chehab */
11335c4cdb5aSMauro Carvalho Chehab
11345c4cdb5aSMauro Carvalho Chehab static DEVICE_ATTR(inject_section, S_IRUGO | S_IWUSR,
11355c4cdb5aSMauro Carvalho Chehab i7core_inject_section_show, i7core_inject_section_store);
11365c4cdb5aSMauro Carvalho Chehab
11375c4cdb5aSMauro Carvalho Chehab static DEVICE_ATTR(inject_type, S_IRUGO | S_IWUSR,
11385c4cdb5aSMauro Carvalho Chehab i7core_inject_type_show, i7core_inject_type_store);
11395c4cdb5aSMauro Carvalho Chehab
11405c4cdb5aSMauro Carvalho Chehab
11415c4cdb5aSMauro Carvalho Chehab static DEVICE_ATTR(inject_eccmask, S_IRUGO | S_IWUSR,
11425c4cdb5aSMauro Carvalho Chehab i7core_inject_eccmask_show, i7core_inject_eccmask_store);
11435c4cdb5aSMauro Carvalho Chehab
11445c4cdb5aSMauro Carvalho Chehab static DEVICE_ATTR(inject_enable, S_IRUGO | S_IWUSR,
11455c4cdb5aSMauro Carvalho Chehab i7core_inject_enable_show, i7core_inject_enable_store);
11465c4cdb5aSMauro Carvalho Chehab
11472eace188STakashi Iwai static struct attribute *i7core_dev_attrs[] = {
11482eace188STakashi Iwai &dev_attr_inject_section.attr,
11492eace188STakashi Iwai &dev_attr_inject_type.attr,
11502eace188STakashi Iwai &dev_attr_inject_eccmask.attr,
11512eace188STakashi Iwai &dev_attr_inject_enable.attr,
11522eace188STakashi Iwai NULL
11532eace188STakashi Iwai };
11542eace188STakashi Iwai
11552eace188STakashi Iwai ATTRIBUTE_GROUPS(i7core_dev);
11562eace188STakashi Iwai
i7core_create_sysfs_devices(struct mem_ctl_info * mci)11575c4cdb5aSMauro Carvalho Chehab static int i7core_create_sysfs_devices(struct mem_ctl_info *mci)
11585c4cdb5aSMauro Carvalho Chehab {
11595c4cdb5aSMauro Carvalho Chehab struct i7core_pvt *pvt = mci->pvt_info;
11605c4cdb5aSMauro Carvalho Chehab int rc;
11615c4cdb5aSMauro Carvalho Chehab
1162356f0a30SMauro Carvalho Chehab pvt->addrmatch_dev = kzalloc(sizeof(*pvt->addrmatch_dev), GFP_KERNEL);
1163356f0a30SMauro Carvalho Chehab if (!pvt->addrmatch_dev)
1164e97d7e38STakashi Iwai return -ENOMEM;
1165356f0a30SMauro Carvalho Chehab
1166356f0a30SMauro Carvalho Chehab pvt->addrmatch_dev->type = &addrmatch_type;
1167356f0a30SMauro Carvalho Chehab pvt->addrmatch_dev->bus = mci->dev.bus;
1168356f0a30SMauro Carvalho Chehab device_initialize(pvt->addrmatch_dev);
1169356f0a30SMauro Carvalho Chehab pvt->addrmatch_dev->parent = &mci->dev;
1170356f0a30SMauro Carvalho Chehab dev_set_name(pvt->addrmatch_dev, "inject_addrmatch");
1171356f0a30SMauro Carvalho Chehab dev_set_drvdata(pvt->addrmatch_dev, mci);
11725c4cdb5aSMauro Carvalho Chehab
1173956b9ba1SJoe Perches edac_dbg(1, "creating %s\n", dev_name(pvt->addrmatch_dev));
11745c4cdb5aSMauro Carvalho Chehab
1175356f0a30SMauro Carvalho Chehab rc = device_add(pvt->addrmatch_dev);
11765c4cdb5aSMauro Carvalho Chehab if (rc < 0)
11776c974d4dSJohan Hovold goto err_put_addrmatch;
11785c4cdb5aSMauro Carvalho Chehab
11795c4cdb5aSMauro Carvalho Chehab if (!pvt->is_registered) {
1180356f0a30SMauro Carvalho Chehab pvt->chancounts_dev = kzalloc(sizeof(*pvt->chancounts_dev),
1181356f0a30SMauro Carvalho Chehab GFP_KERNEL);
1182356f0a30SMauro Carvalho Chehab if (!pvt->chancounts_dev) {
11836c974d4dSJohan Hovold rc = -ENOMEM;
11846c974d4dSJohan Hovold goto err_del_addrmatch;
1185356f0a30SMauro Carvalho Chehab }
1186356f0a30SMauro Carvalho Chehab
1187356f0a30SMauro Carvalho Chehab pvt->chancounts_dev->type = &all_channel_counts_type;
1188356f0a30SMauro Carvalho Chehab pvt->chancounts_dev->bus = mci->dev.bus;
1189356f0a30SMauro Carvalho Chehab device_initialize(pvt->chancounts_dev);
1190356f0a30SMauro Carvalho Chehab pvt->chancounts_dev->parent = &mci->dev;
1191356f0a30SMauro Carvalho Chehab dev_set_name(pvt->chancounts_dev, "all_channel_counts");
1192356f0a30SMauro Carvalho Chehab dev_set_drvdata(pvt->chancounts_dev, mci);
11935c4cdb5aSMauro Carvalho Chehab
1194956b9ba1SJoe Perches edac_dbg(1, "creating %s\n", dev_name(pvt->chancounts_dev));
11955c4cdb5aSMauro Carvalho Chehab
1196356f0a30SMauro Carvalho Chehab rc = device_add(pvt->chancounts_dev);
11975c4cdb5aSMauro Carvalho Chehab if (rc < 0)
11986c974d4dSJohan Hovold goto err_put_chancounts;
11995c4cdb5aSMauro Carvalho Chehab }
12005c4cdb5aSMauro Carvalho Chehab return 0;
12016c974d4dSJohan Hovold
12026c974d4dSJohan Hovold err_put_chancounts:
12036c974d4dSJohan Hovold put_device(pvt->chancounts_dev);
12046c974d4dSJohan Hovold err_del_addrmatch:
12056c974d4dSJohan Hovold device_del(pvt->addrmatch_dev);
12066c974d4dSJohan Hovold err_put_addrmatch:
12076c974d4dSJohan Hovold put_device(pvt->addrmatch_dev);
12086c974d4dSJohan Hovold
12096c974d4dSJohan Hovold return rc;
12105c4cdb5aSMauro Carvalho Chehab }
12115c4cdb5aSMauro Carvalho Chehab
i7core_delete_sysfs_devices(struct mem_ctl_info * mci)12125c4cdb5aSMauro Carvalho Chehab static void i7core_delete_sysfs_devices(struct mem_ctl_info *mci)
12135c4cdb5aSMauro Carvalho Chehab {
12145c4cdb5aSMauro Carvalho Chehab struct i7core_pvt *pvt = mci->pvt_info;
12155c4cdb5aSMauro Carvalho Chehab
1216956b9ba1SJoe Perches edac_dbg(1, "\n");
12175c4cdb5aSMauro Carvalho Chehab
12185c4cdb5aSMauro Carvalho Chehab if (!pvt->is_registered) {
1219356f0a30SMauro Carvalho Chehab device_del(pvt->chancounts_dev);
12206c974d4dSJohan Hovold put_device(pvt->chancounts_dev);
12215c4cdb5aSMauro Carvalho Chehab }
1222356f0a30SMauro Carvalho Chehab device_del(pvt->addrmatch_dev);
12236c974d4dSJohan Hovold put_device(pvt->addrmatch_dev);
12245c4cdb5aSMauro Carvalho Chehab }
12255c4cdb5aSMauro Carvalho Chehab
1226194a40feSMauro Carvalho Chehab /****************************************************************************
1227a0c36a1fSMauro Carvalho Chehab Device initialization routines: put/get, init/exit
1228a0c36a1fSMauro Carvalho Chehab ****************************************************************************/
1229a0c36a1fSMauro Carvalho Chehab
1230a0c36a1fSMauro Carvalho Chehab /*
123164c10f6eSHidetoshi Seto * i7core_put_all_devices 'put' all the devices that we have
1232a0c36a1fSMauro Carvalho Chehab * reserved via 'get'
1233a0c36a1fSMauro Carvalho Chehab */
i7core_put_devices(struct i7core_dev * i7core_dev)123413d6e9b6SMauro Carvalho Chehab static void i7core_put_devices(struct i7core_dev *i7core_dev)
1235a0c36a1fSMauro Carvalho Chehab {
123613d6e9b6SMauro Carvalho Chehab int i;
1237a0c36a1fSMauro Carvalho Chehab
1238956b9ba1SJoe Perches edac_dbg(0, "\n");
1239de06eeefSMauro Carvalho Chehab for (i = 0; i < i7core_dev->n_devs; i++) {
124022e6bcbdSMauro Carvalho Chehab struct pci_dev *pdev = i7core_dev->pdev[i];
124122e6bcbdSMauro Carvalho Chehab if (!pdev)
124222e6bcbdSMauro Carvalho Chehab continue;
1243956b9ba1SJoe Perches edac_dbg(0, "Removing dev %02x:%02x.%d\n",
124422e6bcbdSMauro Carvalho Chehab pdev->bus->number,
124522e6bcbdSMauro Carvalho Chehab PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
124622e6bcbdSMauro Carvalho Chehab pci_dev_put(pdev);
124722e6bcbdSMauro Carvalho Chehab }
124866607706SMauro Carvalho Chehab }
124913d6e9b6SMauro Carvalho Chehab
i7core_put_all_devices(void)125013d6e9b6SMauro Carvalho Chehab static void i7core_put_all_devices(void)
125113d6e9b6SMauro Carvalho Chehab {
125242538680SMauro Carvalho Chehab struct i7core_dev *i7core_dev, *tmp;
125313d6e9b6SMauro Carvalho Chehab
125439300e71SMauro Carvalho Chehab list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) {
125513d6e9b6SMauro Carvalho Chehab i7core_put_devices(i7core_dev);
12562aa9be44SHidetoshi Seto free_i7core_dev(i7core_dev);
125739300e71SMauro Carvalho Chehab }
1258a0c36a1fSMauro Carvalho Chehab }
1259a0c36a1fSMauro Carvalho Chehab
i7core_xeon_pci_fixup(const struct pci_id_table * table)12601288c18fSMauro Carvalho Chehab static void __init i7core_xeon_pci_fixup(const struct pci_id_table *table)
1261bc2d7245SKeith Mannthey {
1262bc2d7245SKeith Mannthey struct pci_dev *pdev = NULL;
1263bc2d7245SKeith Mannthey int i;
126454a08ab1SMauro Carvalho Chehab
1265bc2d7245SKeith Mannthey /*
1266e7bf068aSDavid Sterba * On Xeon 55xx, the Intel Quick Path Arch Generic Non-core pci buses
1267bc2d7245SKeith Mannthey * aren't announced by acpi. So, we need to use a legacy scan probing
1268bc2d7245SKeith Mannthey * to detect them
1269bc2d7245SKeith Mannthey */
1270bd9e19caSVernon Mauery while (table && table->descr) {
1271bd9e19caSVernon Mauery pdev = pci_get_device(PCI_VENDOR_ID_INTEL, table->descr[0].dev_id, NULL);
1272bc2d7245SKeith Mannthey if (unlikely(!pdev)) {
1273f4742949SMauro Carvalho Chehab for (i = 0; i < MAX_SOCKET_BUSES; i++)
1274bc2d7245SKeith Mannthey pcibios_scan_specific_bus(255-i);
1275bc2d7245SKeith Mannthey }
1276bda14289SMauro Carvalho Chehab pci_dev_put(pdev);
1277bd9e19caSVernon Mauery table++;
1278bd9e19caSVernon Mauery }
1279bc2d7245SKeith Mannthey }
1280bc2d7245SKeith Mannthey
i7core_pci_lastbus(void)1281bda14289SMauro Carvalho Chehab static unsigned i7core_pci_lastbus(void)
1282bda14289SMauro Carvalho Chehab {
1283bda14289SMauro Carvalho Chehab int last_bus = 0, bus;
1284bda14289SMauro Carvalho Chehab struct pci_bus *b = NULL;
1285bda14289SMauro Carvalho Chehab
1286bda14289SMauro Carvalho Chehab while ((b = pci_find_next_bus(b)) != NULL) {
1287bda14289SMauro Carvalho Chehab bus = b->number;
1288956b9ba1SJoe Perches edac_dbg(0, "Found bus %d\n", bus);
1289bda14289SMauro Carvalho Chehab if (bus > last_bus)
1290bda14289SMauro Carvalho Chehab last_bus = bus;
1291bda14289SMauro Carvalho Chehab }
1292bda14289SMauro Carvalho Chehab
1293956b9ba1SJoe Perches edac_dbg(0, "Last bus %d\n", last_bus);
1294bda14289SMauro Carvalho Chehab
1295bda14289SMauro Carvalho Chehab return last_bus;
1296bda14289SMauro Carvalho Chehab }
1297bda14289SMauro Carvalho Chehab
1298a0c36a1fSMauro Carvalho Chehab /*
129964c10f6eSHidetoshi Seto * i7core_get_all_devices Find and perform 'get' operation on the MCH's
1300a0c36a1fSMauro Carvalho Chehab * device/functions we want to reference for this driver
1301a0c36a1fSMauro Carvalho Chehab *
1302a0c36a1fSMauro Carvalho Chehab * Need to 'get' device 16 func 1 and func 2
1303a0c36a1fSMauro Carvalho Chehab */
i7core_get_onedevice(struct pci_dev ** prev,const struct pci_id_table * table,const unsigned devno,const unsigned last_bus)1304b197cba0SHidetoshi Seto static int i7core_get_onedevice(struct pci_dev **prev,
1305b197cba0SHidetoshi Seto const struct pci_id_table *table,
1306b197cba0SHidetoshi Seto const unsigned devno,
13071288c18fSMauro Carvalho Chehab const unsigned last_bus)
1308a0c36a1fSMauro Carvalho Chehab {
130966607706SMauro Carvalho Chehab struct i7core_dev *i7core_dev;
1310b197cba0SHidetoshi Seto const struct pci_id_descr *dev_descr = &table->descr[devno];
131166607706SMauro Carvalho Chehab
13128f331907SMauro Carvalho Chehab struct pci_dev *pdev = NULL;
131367166af4SMauro Carvalho Chehab u8 bus = 0;
131467166af4SMauro Carvalho Chehab u8 socket = 0;
1315a0c36a1fSMauro Carvalho Chehab
1316a0c36a1fSMauro Carvalho Chehab pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1317de06eeefSMauro Carvalho Chehab dev_descr->dev_id, *prev);
1318d1fd4fb6SMauro Carvalho Chehab
1319224e871fSMauro Carvalho Chehab /*
132015ed103aSDavid Mackey * On Xeon 55xx, the Intel QuickPath Arch Generic Non-core regs
1321224e871fSMauro Carvalho Chehab * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1322224e871fSMauro Carvalho Chehab * to probe for the alternate address in case of failure
1323224e871fSMauro Carvalho Chehab */
1324c0f5eeedSJean Delvare if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_I7_NONCORE && !pdev) {
1325c0f5eeedSJean Delvare pci_dev_get(*prev); /* pci_get_device will put it */
1326224e871fSMauro Carvalho Chehab pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1327224e871fSMauro Carvalho Chehab PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT, *prev);
1328c0f5eeedSJean Delvare }
1329224e871fSMauro Carvalho Chehab
1330c0f5eeedSJean Delvare if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE &&
1331c0f5eeedSJean Delvare !pdev) {
1332c0f5eeedSJean Delvare pci_dev_get(*prev); /* pci_get_device will put it */
1333224e871fSMauro Carvalho Chehab pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1334224e871fSMauro Carvalho Chehab PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT,
1335224e871fSMauro Carvalho Chehab *prev);
1336c0f5eeedSJean Delvare }
1337224e871fSMauro Carvalho Chehab
1338c77720b9SMauro Carvalho Chehab if (!pdev) {
1339c77720b9SMauro Carvalho Chehab if (*prev) {
1340c77720b9SMauro Carvalho Chehab *prev = pdev;
1341c77720b9SMauro Carvalho Chehab return 0;
1342c77720b9SMauro Carvalho Chehab }
1343c77720b9SMauro Carvalho Chehab
1344de06eeefSMauro Carvalho Chehab if (dev_descr->optional)
1345c77720b9SMauro Carvalho Chehab return 0;
1346c77720b9SMauro Carvalho Chehab
1347bd9e19caSVernon Mauery if (devno == 0)
1348bd9e19caSVernon Mauery return -ENODEV;
1349bd9e19caSVernon Mauery
1350ab089374SDaniel J Blueman i7core_printk(KERN_INFO,
1351c77720b9SMauro Carvalho Chehab "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
1352de06eeefSMauro Carvalho Chehab dev_descr->dev, dev_descr->func,
1353de06eeefSMauro Carvalho Chehab PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
1354c77720b9SMauro Carvalho Chehab
1355c77720b9SMauro Carvalho Chehab /* End of list, leave */
1356c77720b9SMauro Carvalho Chehab return -ENODEV;
1357c77720b9SMauro Carvalho Chehab }
135867166af4SMauro Carvalho Chehab bus = pdev->bus->number;
135967166af4SMauro Carvalho Chehab
1360bda14289SMauro Carvalho Chehab socket = last_bus - bus;
136167166af4SMauro Carvalho Chehab
136266607706SMauro Carvalho Chehab i7core_dev = get_i7core_dev(socket);
136366607706SMauro Carvalho Chehab if (!i7core_dev) {
1364848b2f7eSHidetoshi Seto i7core_dev = alloc_i7core_dev(socket, table);
13652896637bSHidetoshi Seto if (!i7core_dev) {
13662896637bSHidetoshi Seto pci_dev_put(pdev);
136766607706SMauro Carvalho Chehab return -ENOMEM;
136867166af4SMauro Carvalho Chehab }
13692896637bSHidetoshi Seto }
137067166af4SMauro Carvalho Chehab
137166607706SMauro Carvalho Chehab if (i7core_dev->pdev[devno]) {
137267166af4SMauro Carvalho Chehab i7core_printk(KERN_ERR,
1373c77720b9SMauro Carvalho Chehab "Duplicated device for "
137467166af4SMauro Carvalho Chehab "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1375de06eeefSMauro Carvalho Chehab bus, dev_descr->dev, dev_descr->func,
1376de06eeefSMauro Carvalho Chehab PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
1377c77720b9SMauro Carvalho Chehab pci_dev_put(pdev);
1378c77720b9SMauro Carvalho Chehab return -ENODEV;
13798f331907SMauro Carvalho Chehab }
1380ef708b53SMauro Carvalho Chehab
138166607706SMauro Carvalho Chehab i7core_dev->pdev[devno] = pdev;
1382c77720b9SMauro Carvalho Chehab
13838f331907SMauro Carvalho Chehab /* Sanity check */
1384de06eeefSMauro Carvalho Chehab if (unlikely(PCI_SLOT(pdev->devfn) != dev_descr->dev ||
1385de06eeefSMauro Carvalho Chehab PCI_FUNC(pdev->devfn) != dev_descr->func)) {
13868f331907SMauro Carvalho Chehab i7core_printk(KERN_ERR,
13878f331907SMauro Carvalho Chehab "Device PCI ID %04x:%04x "
1388c77720b9SMauro Carvalho Chehab "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
1389de06eeefSMauro Carvalho Chehab PCI_VENDOR_ID_INTEL, dev_descr->dev_id,
1390c77720b9SMauro Carvalho Chehab bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1391de06eeefSMauro Carvalho Chehab bus, dev_descr->dev, dev_descr->func);
1392c77720b9SMauro Carvalho Chehab return -ENODEV;
1393ef708b53SMauro Carvalho Chehab }
1394ef708b53SMauro Carvalho Chehab
1395ef708b53SMauro Carvalho Chehab /* Be sure that the device is enabled */
1396c77720b9SMauro Carvalho Chehab if (unlikely(pci_enable_device(pdev) < 0)) {
1397ef708b53SMauro Carvalho Chehab i7core_printk(KERN_ERR,
1398c77720b9SMauro Carvalho Chehab "Couldn't enable "
1399c77720b9SMauro Carvalho Chehab "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1400de06eeefSMauro Carvalho Chehab bus, dev_descr->dev, dev_descr->func,
1401de06eeefSMauro Carvalho Chehab PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
1402c77720b9SMauro Carvalho Chehab return -ENODEV;
1403ef708b53SMauro Carvalho Chehab }
1404ef708b53SMauro Carvalho Chehab
1405956b9ba1SJoe Perches edac_dbg(0, "Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n",
1406de06eeefSMauro Carvalho Chehab socket, bus, dev_descr->dev,
1407de06eeefSMauro Carvalho Chehab dev_descr->func,
1408de06eeefSMauro Carvalho Chehab PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
1409c77720b9SMauro Carvalho Chehab
1410a3e15416SMauro Carvalho Chehab /*
1411a3e15416SMauro Carvalho Chehab * As stated on drivers/pci/search.c, the reference count for
1412a3e15416SMauro Carvalho Chehab * @from is always decremented if it is not %NULL. So, as we need
1413a3e15416SMauro Carvalho Chehab * to get all devices up to null, we need to do a get for the device
1414a3e15416SMauro Carvalho Chehab */
1415a3e15416SMauro Carvalho Chehab pci_dev_get(pdev);
1416a3e15416SMauro Carvalho Chehab
1417c77720b9SMauro Carvalho Chehab *prev = pdev;
1418ef708b53SMauro Carvalho Chehab
1419ef708b53SMauro Carvalho Chehab return 0;
1420c77720b9SMauro Carvalho Chehab }
1421ef708b53SMauro Carvalho Chehab
i7core_get_all_devices(void)142264c10f6eSHidetoshi Seto static int i7core_get_all_devices(void)
1423c77720b9SMauro Carvalho Chehab {
14243c52cc57SMauro Carvalho Chehab int i, rc, last_bus;
1425c77720b9SMauro Carvalho Chehab struct pci_dev *pdev = NULL;
14263c52cc57SMauro Carvalho Chehab const struct pci_id_table *table = pci_dev_table;
1427c77720b9SMauro Carvalho Chehab
1428bda14289SMauro Carvalho Chehab last_bus = i7core_pci_lastbus();
1429bda14289SMauro Carvalho Chehab
14303c52cc57SMauro Carvalho Chehab while (table && table->descr) {
1431bd9e19caSVernon Mauery for (i = 0; i < table->n_devs; i++) {
1432c77720b9SMauro Carvalho Chehab pdev = NULL;
1433c77720b9SMauro Carvalho Chehab do {
1434b197cba0SHidetoshi Seto rc = i7core_get_onedevice(&pdev, table, i,
1435bda14289SMauro Carvalho Chehab last_bus);
1436de06eeefSMauro Carvalho Chehab if (rc < 0) {
1437bd9e19caSVernon Mauery if (i == 0) {
1438bd9e19caSVernon Mauery i = table->n_devs;
1439bd9e19caSVernon Mauery break;
1440bd9e19caSVernon Mauery }
144113d6e9b6SMauro Carvalho Chehab i7core_put_all_devices();
1442c77720b9SMauro Carvalho Chehab return -ENODEV;
1443c77720b9SMauro Carvalho Chehab }
1444c77720b9SMauro Carvalho Chehab } while (pdev);
1445c77720b9SMauro Carvalho Chehab }
14463c52cc57SMauro Carvalho Chehab table++;
1447bd9e19caSVernon Mauery }
144866607706SMauro Carvalho Chehab
1449c77720b9SMauro Carvalho Chehab return 0;
14508f331907SMauro Carvalho Chehab }
14518f331907SMauro Carvalho Chehab
mci_bind_devs(struct mem_ctl_info * mci,struct i7core_dev * i7core_dev)1452f4742949SMauro Carvalho Chehab static int mci_bind_devs(struct mem_ctl_info *mci,
1453f4742949SMauro Carvalho Chehab struct i7core_dev *i7core_dev)
1454ef708b53SMauro Carvalho Chehab {
1455ef708b53SMauro Carvalho Chehab struct i7core_pvt *pvt = mci->pvt_info;
1456ef708b53SMauro Carvalho Chehab struct pci_dev *pdev;
1457f4742949SMauro Carvalho Chehab int i, func, slot;
145827100db0SMauro Carvalho Chehab char *family;
1459ef708b53SMauro Carvalho Chehab
146027100db0SMauro Carvalho Chehab pvt->is_registered = false;
146127100db0SMauro Carvalho Chehab pvt->enable_scrub = false;
1462de06eeefSMauro Carvalho Chehab for (i = 0; i < i7core_dev->n_devs; i++) {
1463f4742949SMauro Carvalho Chehab pdev = i7core_dev->pdev[i];
1464ef708b53SMauro Carvalho Chehab if (!pdev)
1465ef708b53SMauro Carvalho Chehab continue;
14668f331907SMauro Carvalho Chehab
14678f331907SMauro Carvalho Chehab func = PCI_FUNC(pdev->devfn);
1468ef708b53SMauro Carvalho Chehab slot = PCI_SLOT(pdev->devfn);
1469ef708b53SMauro Carvalho Chehab if (slot == 3) {
1470ef708b53SMauro Carvalho Chehab if (unlikely(func > MAX_MCR_FUNC))
1471ef708b53SMauro Carvalho Chehab goto error;
1472f4742949SMauro Carvalho Chehab pvt->pci_mcr[func] = pdev;
1473ef708b53SMauro Carvalho Chehab } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1474ef708b53SMauro Carvalho Chehab if (unlikely(func > MAX_CHAN_FUNC))
1475ef708b53SMauro Carvalho Chehab goto error;
1476f4742949SMauro Carvalho Chehab pvt->pci_ch[slot - 4][func] = pdev;
147727100db0SMauro Carvalho Chehab } else if (!slot && !func) {
1478f4742949SMauro Carvalho Chehab pvt->pci_noncore = pdev;
147927100db0SMauro Carvalho Chehab
148027100db0SMauro Carvalho Chehab /* Detect the processor family */
148127100db0SMauro Carvalho Chehab switch (pdev->device) {
148227100db0SMauro Carvalho Chehab case PCI_DEVICE_ID_INTEL_I7_NONCORE:
148327100db0SMauro Carvalho Chehab family = "Xeon 35xx/ i7core";
148427100db0SMauro Carvalho Chehab pvt->enable_scrub = false;
148527100db0SMauro Carvalho Chehab break;
148627100db0SMauro Carvalho Chehab case PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT:
148727100db0SMauro Carvalho Chehab family = "i7-800/i5-700";
148827100db0SMauro Carvalho Chehab pvt->enable_scrub = false;
148927100db0SMauro Carvalho Chehab break;
149027100db0SMauro Carvalho Chehab case PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE:
149127100db0SMauro Carvalho Chehab family = "Xeon 34xx";
149227100db0SMauro Carvalho Chehab pvt->enable_scrub = false;
149327100db0SMauro Carvalho Chehab break;
149427100db0SMauro Carvalho Chehab case PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT:
149527100db0SMauro Carvalho Chehab family = "Xeon 55xx";
149627100db0SMauro Carvalho Chehab pvt->enable_scrub = true;
149727100db0SMauro Carvalho Chehab break;
149827100db0SMauro Carvalho Chehab case PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2:
149927100db0SMauro Carvalho Chehab family = "Xeon 56xx / i7-900";
150027100db0SMauro Carvalho Chehab pvt->enable_scrub = true;
150127100db0SMauro Carvalho Chehab break;
150227100db0SMauro Carvalho Chehab default:
150327100db0SMauro Carvalho Chehab family = "unknown";
150427100db0SMauro Carvalho Chehab pvt->enable_scrub = false;
150527100db0SMauro Carvalho Chehab }
1506956b9ba1SJoe Perches edac_dbg(0, "Detected a processor type %s\n", family);
150727100db0SMauro Carvalho Chehab } else
1508ef708b53SMauro Carvalho Chehab goto error;
15098f331907SMauro Carvalho Chehab
1510956b9ba1SJoe Perches edac_dbg(0, "Associated fn %d.%d, dev = %p, socket %d\n",
151167166af4SMauro Carvalho Chehab PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1512f4742949SMauro Carvalho Chehab pdev, i7core_dev->socket);
151314d2c083SMauro Carvalho Chehab
151414d2c083SMauro Carvalho Chehab if (PCI_SLOT(pdev->devfn) == 3 &&
151514d2c083SMauro Carvalho Chehab PCI_FUNC(pdev->devfn) == 2)
151627100db0SMauro Carvalho Chehab pvt->is_registered = true;
1517ef708b53SMauro Carvalho Chehab }
1518e9bd2e73SMauro Carvalho Chehab
1519a0c36a1fSMauro Carvalho Chehab return 0;
1520ef708b53SMauro Carvalho Chehab
1521ef708b53SMauro Carvalho Chehab error:
1522ef708b53SMauro Carvalho Chehab i7core_printk(KERN_ERR, "Device %d, function %d "
1523ef708b53SMauro Carvalho Chehab "is out of the expected range\n",
1524ef708b53SMauro Carvalho Chehab slot, func);
1525ef708b53SMauro Carvalho Chehab return -EINVAL;
1526a0c36a1fSMauro Carvalho Chehab }
1527a0c36a1fSMauro Carvalho Chehab
1528442305b1SMauro Carvalho Chehab /****************************************************************************
1529442305b1SMauro Carvalho Chehab Error check routines
1530442305b1SMauro Carvalho Chehab ****************************************************************************/
1531b4e8f0b6SMauro Carvalho Chehab
i7core_rdimm_update_ce_count(struct mem_ctl_info * mci,const int chan,const int new0,const int new1,const int new2)1532b4e8f0b6SMauro Carvalho Chehab static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
15331288c18fSMauro Carvalho Chehab const int chan,
15341288c18fSMauro Carvalho Chehab const int new0,
15351288c18fSMauro Carvalho Chehab const int new1,
15361288c18fSMauro Carvalho Chehab const int new2)
1537b4e8f0b6SMauro Carvalho Chehab {
1538b4e8f0b6SMauro Carvalho Chehab struct i7core_pvt *pvt = mci->pvt_info;
1539b4e8f0b6SMauro Carvalho Chehab int add0 = 0, add1 = 0, add2 = 0;
1540b4e8f0b6SMauro Carvalho Chehab /* Updates CE counters if it is not the first time here */
1541f4742949SMauro Carvalho Chehab if (pvt->ce_count_available) {
1542b4e8f0b6SMauro Carvalho Chehab /* Updates CE counters */
1543b4e8f0b6SMauro Carvalho Chehab
1544f4742949SMauro Carvalho Chehab add2 = new2 - pvt->rdimm_last_ce_count[chan][2];
1545f4742949SMauro Carvalho Chehab add1 = new1 - pvt->rdimm_last_ce_count[chan][1];
1546f4742949SMauro Carvalho Chehab add0 = new0 - pvt->rdimm_last_ce_count[chan][0];
1547b4e8f0b6SMauro Carvalho Chehab
1548b4e8f0b6SMauro Carvalho Chehab if (add2 < 0)
1549b4e8f0b6SMauro Carvalho Chehab add2 += 0x7fff;
1550f4742949SMauro Carvalho Chehab pvt->rdimm_ce_count[chan][2] += add2;
1551b4e8f0b6SMauro Carvalho Chehab
1552b4e8f0b6SMauro Carvalho Chehab if (add1 < 0)
1553b4e8f0b6SMauro Carvalho Chehab add1 += 0x7fff;
1554f4742949SMauro Carvalho Chehab pvt->rdimm_ce_count[chan][1] += add1;
1555b4e8f0b6SMauro Carvalho Chehab
1556b4e8f0b6SMauro Carvalho Chehab if (add0 < 0)
1557b4e8f0b6SMauro Carvalho Chehab add0 += 0x7fff;
1558f4742949SMauro Carvalho Chehab pvt->rdimm_ce_count[chan][0] += add0;
1559b4e8f0b6SMauro Carvalho Chehab } else
1560f4742949SMauro Carvalho Chehab pvt->ce_count_available = 1;
1561b4e8f0b6SMauro Carvalho Chehab
1562b4e8f0b6SMauro Carvalho Chehab /* Store the new values */
1563f4742949SMauro Carvalho Chehab pvt->rdimm_last_ce_count[chan][2] = new2;
1564f4742949SMauro Carvalho Chehab pvt->rdimm_last_ce_count[chan][1] = new1;
1565f4742949SMauro Carvalho Chehab pvt->rdimm_last_ce_count[chan][0] = new0;
1566b4e8f0b6SMauro Carvalho Chehab
1567b4e8f0b6SMauro Carvalho Chehab /*updated the edac core */
1568b4e8f0b6SMauro Carvalho Chehab if (add0 != 0)
156900d18339SMauro Carvalho Chehab edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, add0,
157000d18339SMauro Carvalho Chehab 0, 0, 0,
157100d18339SMauro Carvalho Chehab chan, 0, -1, "error", "");
1572b4e8f0b6SMauro Carvalho Chehab if (add1 != 0)
157300d18339SMauro Carvalho Chehab edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, add1,
157400d18339SMauro Carvalho Chehab 0, 0, 0,
157500d18339SMauro Carvalho Chehab chan, 1, -1, "error", "");
1576b4e8f0b6SMauro Carvalho Chehab if (add2 != 0)
157700d18339SMauro Carvalho Chehab edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, add2,
157800d18339SMauro Carvalho Chehab 0, 0, 0,
157900d18339SMauro Carvalho Chehab chan, 2, -1, "error", "");
1580b4e8f0b6SMauro Carvalho Chehab }
1581b4e8f0b6SMauro Carvalho Chehab
i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info * mci)1582f4742949SMauro Carvalho Chehab static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci)
1583b4e8f0b6SMauro Carvalho Chehab {
1584b4e8f0b6SMauro Carvalho Chehab struct i7core_pvt *pvt = mci->pvt_info;
1585b4e8f0b6SMauro Carvalho Chehab u32 rcv[3][2];
1586b4e8f0b6SMauro Carvalho Chehab int i, new0, new1, new2;
1587b4e8f0b6SMauro Carvalho Chehab
1588b4e8f0b6SMauro Carvalho Chehab /*Read DEV 3: FUN 2: MC_COR_ECC_CNT regs directly*/
1589f4742949SMauro Carvalho Chehab pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_0,
1590b4e8f0b6SMauro Carvalho Chehab &rcv[0][0]);
1591f4742949SMauro Carvalho Chehab pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_1,
1592b4e8f0b6SMauro Carvalho Chehab &rcv[0][1]);
1593f4742949SMauro Carvalho Chehab pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_2,
1594b4e8f0b6SMauro Carvalho Chehab &rcv[1][0]);
1595f4742949SMauro Carvalho Chehab pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_3,
1596b4e8f0b6SMauro Carvalho Chehab &rcv[1][1]);
1597f4742949SMauro Carvalho Chehab pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_4,
1598b4e8f0b6SMauro Carvalho Chehab &rcv[2][0]);
1599f4742949SMauro Carvalho Chehab pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5,
1600b4e8f0b6SMauro Carvalho Chehab &rcv[2][1]);
1601b4e8f0b6SMauro Carvalho Chehab for (i = 0 ; i < 3; i++) {
1602956b9ba1SJoe Perches edac_dbg(3, "MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
1603b4e8f0b6SMauro Carvalho Chehab (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
1604b4e8f0b6SMauro Carvalho Chehab /*if the channel has 3 dimms*/
1605f4742949SMauro Carvalho Chehab if (pvt->channel[i].dimms > 2) {
1606b4e8f0b6SMauro Carvalho Chehab new0 = DIMM_BOT_COR_ERR(rcv[i][0]);
1607b4e8f0b6SMauro Carvalho Chehab new1 = DIMM_TOP_COR_ERR(rcv[i][0]);
1608b4e8f0b6SMauro Carvalho Chehab new2 = DIMM_BOT_COR_ERR(rcv[i][1]);
1609b4e8f0b6SMauro Carvalho Chehab } else {
1610b4e8f0b6SMauro Carvalho Chehab new0 = DIMM_TOP_COR_ERR(rcv[i][0]) +
1611b4e8f0b6SMauro Carvalho Chehab DIMM_BOT_COR_ERR(rcv[i][0]);
1612b4e8f0b6SMauro Carvalho Chehab new1 = DIMM_TOP_COR_ERR(rcv[i][1]) +
1613b4e8f0b6SMauro Carvalho Chehab DIMM_BOT_COR_ERR(rcv[i][1]);
1614b4e8f0b6SMauro Carvalho Chehab new2 = 0;
1615b4e8f0b6SMauro Carvalho Chehab }
1616b4e8f0b6SMauro Carvalho Chehab
1617f4742949SMauro Carvalho Chehab i7core_rdimm_update_ce_count(mci, i, new0, new1, new2);
1618b4e8f0b6SMauro Carvalho Chehab }
1619b4e8f0b6SMauro Carvalho Chehab }
1620442305b1SMauro Carvalho Chehab
1621442305b1SMauro Carvalho Chehab /* This function is based on the device 3 function 4 registers as described on:
1622442305b1SMauro Carvalho Chehab * Intel Xeon Processor 5500 Series Datasheet Volume 2
1623442305b1SMauro Carvalho Chehab * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1624442305b1SMauro Carvalho Chehab * also available at:
1625442305b1SMauro Carvalho Chehab * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1626442305b1SMauro Carvalho Chehab */
i7core_udimm_check_mc_ecc_err(struct mem_ctl_info * mci)1627f4742949SMauro Carvalho Chehab static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci)
1628442305b1SMauro Carvalho Chehab {
1629442305b1SMauro Carvalho Chehab struct i7core_pvt *pvt = mci->pvt_info;
1630442305b1SMauro Carvalho Chehab u32 rcv1, rcv0;
1631442305b1SMauro Carvalho Chehab int new0, new1, new2;
1632442305b1SMauro Carvalho Chehab
1633f4742949SMauro Carvalho Chehab if (!pvt->pci_mcr[4]) {
1634956b9ba1SJoe Perches edac_dbg(0, "MCR registers not found\n");
1635442305b1SMauro Carvalho Chehab return;
1636442305b1SMauro Carvalho Chehab }
1637442305b1SMauro Carvalho Chehab
1638b4e8f0b6SMauro Carvalho Chehab /* Corrected test errors */
1639f4742949SMauro Carvalho Chehab pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1640f4742949SMauro Carvalho Chehab pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
1641442305b1SMauro Carvalho Chehab
1642442305b1SMauro Carvalho Chehab /* Store the new values */
1643442305b1SMauro Carvalho Chehab new2 = DIMM2_COR_ERR(rcv1);
1644442305b1SMauro Carvalho Chehab new1 = DIMM1_COR_ERR(rcv0);
1645442305b1SMauro Carvalho Chehab new0 = DIMM0_COR_ERR(rcv0);
1646442305b1SMauro Carvalho Chehab
1647442305b1SMauro Carvalho Chehab /* Updates CE counters if it is not the first time here */
1648f4742949SMauro Carvalho Chehab if (pvt->ce_count_available) {
1649442305b1SMauro Carvalho Chehab /* Updates CE counters */
1650442305b1SMauro Carvalho Chehab int add0, add1, add2;
1651442305b1SMauro Carvalho Chehab
1652f4742949SMauro Carvalho Chehab add2 = new2 - pvt->udimm_last_ce_count[2];
1653f4742949SMauro Carvalho Chehab add1 = new1 - pvt->udimm_last_ce_count[1];
1654f4742949SMauro Carvalho Chehab add0 = new0 - pvt->udimm_last_ce_count[0];
1655442305b1SMauro Carvalho Chehab
1656442305b1SMauro Carvalho Chehab if (add2 < 0)
1657442305b1SMauro Carvalho Chehab add2 += 0x7fff;
1658f4742949SMauro Carvalho Chehab pvt->udimm_ce_count[2] += add2;
1659442305b1SMauro Carvalho Chehab
1660442305b1SMauro Carvalho Chehab if (add1 < 0)
1661442305b1SMauro Carvalho Chehab add1 += 0x7fff;
1662f4742949SMauro Carvalho Chehab pvt->udimm_ce_count[1] += add1;
1663442305b1SMauro Carvalho Chehab
1664442305b1SMauro Carvalho Chehab if (add0 < 0)
1665442305b1SMauro Carvalho Chehab add0 += 0x7fff;
1666f4742949SMauro Carvalho Chehab pvt->udimm_ce_count[0] += add0;
1667b4e8f0b6SMauro Carvalho Chehab
1668b4e8f0b6SMauro Carvalho Chehab if (add0 | add1 | add2)
1669b4e8f0b6SMauro Carvalho Chehab i7core_printk(KERN_ERR, "New Corrected error(s): "
1670b4e8f0b6SMauro Carvalho Chehab "dimm0: +%d, dimm1: +%d, dimm2 +%d\n",
1671b4e8f0b6SMauro Carvalho Chehab add0, add1, add2);
1672442305b1SMauro Carvalho Chehab } else
1673f4742949SMauro Carvalho Chehab pvt->ce_count_available = 1;
1674442305b1SMauro Carvalho Chehab
1675442305b1SMauro Carvalho Chehab /* Store the new values */
1676f4742949SMauro Carvalho Chehab pvt->udimm_last_ce_count[2] = new2;
1677f4742949SMauro Carvalho Chehab pvt->udimm_last_ce_count[1] = new1;
1678f4742949SMauro Carvalho Chehab pvt->udimm_last_ce_count[0] = new0;
1679442305b1SMauro Carvalho Chehab }
1680442305b1SMauro Carvalho Chehab
16818a2f118eSMauro Carvalho Chehab /*
16828a2f118eSMauro Carvalho Chehab * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
16838a2f118eSMauro Carvalho Chehab * Architectures Software Developer’s Manual Volume 3B.
1684f237fcf2SMauro Carvalho Chehab * Nehalem are defined as family 0x06, model 0x1a
1685f237fcf2SMauro Carvalho Chehab *
1686f237fcf2SMauro Carvalho Chehab * The MCA registers used here are the following ones:
16878a2f118eSMauro Carvalho Chehab * struct mce field MCA Register
1688f237fcf2SMauro Carvalho Chehab * m->status MSR_IA32_MC8_STATUS
1689f237fcf2SMauro Carvalho Chehab * m->addr MSR_IA32_MC8_ADDR
1690f237fcf2SMauro Carvalho Chehab * m->misc MSR_IA32_MC8_MISC
16918a2f118eSMauro Carvalho Chehab * In the case of Nehalem, the error information is masked at .status and .misc
16928a2f118eSMauro Carvalho Chehab * fields
16938a2f118eSMauro Carvalho Chehab */
i7core_mce_output_error(struct mem_ctl_info * mci,const struct mce * m)1694d5381642SMauro Carvalho Chehab static void i7core_mce_output_error(struct mem_ctl_info *mci,
16951288c18fSMauro Carvalho Chehab const struct mce *m)
1696d5381642SMauro Carvalho Chehab {
1697b4e8f0b6SMauro Carvalho Chehab struct i7core_pvt *pvt = mci->pvt_info;
1698f118920bSJean Delvare char *optype, *err;
16990975c16fSMauro Carvalho Chehab enum hw_event_mc_err_type tp_event;
17008a2f118eSMauro Carvalho Chehab unsigned long error = m->status & 0x1ff0000l;
17010975c16fSMauro Carvalho Chehab bool uncorrected_error = m->mcgstatus & 1ll << 61;
17020975c16fSMauro Carvalho Chehab bool ripv = m->mcgstatus & 1;
1703a639539fSMauro Carvalho Chehab u32 optypenum = (m->status >> 4) & 0x07;
17048cf2d239SMathias Krause u32 core_err_cnt = (m->status >> 38) & 0x7fff;
17058a2f118eSMauro Carvalho Chehab u32 dimm = (m->misc >> 16) & 0x3;
17068a2f118eSMauro Carvalho Chehab u32 channel = (m->misc >> 18) & 0x3;
17078a2f118eSMauro Carvalho Chehab u32 syndrome = m->misc >> 32;
17088a2f118eSMauro Carvalho Chehab u32 errnum = find_first_bit(&error, 32);
1709d5381642SMauro Carvalho Chehab
17100975c16fSMauro Carvalho Chehab if (uncorrected_error) {
1711432de7fdSTony Luck core_err_cnt = 1;
1712f118920bSJean Delvare if (ripv)
17130975c16fSMauro Carvalho Chehab tp_event = HW_EVENT_ERR_UNCORRECTED;
171445bc6098STony Luck else
171545bc6098STony Luck tp_event = HW_EVENT_ERR_FATAL;
17160975c16fSMauro Carvalho Chehab } else {
17170975c16fSMauro Carvalho Chehab tp_event = HW_EVENT_ERR_CORRECTED;
17180975c16fSMauro Carvalho Chehab }
1719c5d34528SMauro Carvalho Chehab
1720a639539fSMauro Carvalho Chehab switch (optypenum) {
1721a639539fSMauro Carvalho Chehab case 0:
1722a639539fSMauro Carvalho Chehab optype = "generic undef request";
1723a639539fSMauro Carvalho Chehab break;
1724a639539fSMauro Carvalho Chehab case 1:
1725a639539fSMauro Carvalho Chehab optype = "read error";
1726a639539fSMauro Carvalho Chehab break;
1727a639539fSMauro Carvalho Chehab case 2:
1728a639539fSMauro Carvalho Chehab optype = "write error";
1729a639539fSMauro Carvalho Chehab break;
1730a639539fSMauro Carvalho Chehab case 3:
1731a639539fSMauro Carvalho Chehab optype = "addr/cmd error";
1732a639539fSMauro Carvalho Chehab break;
1733a639539fSMauro Carvalho Chehab case 4:
1734a639539fSMauro Carvalho Chehab optype = "scrubbing error";
1735a639539fSMauro Carvalho Chehab break;
1736a639539fSMauro Carvalho Chehab default:
1737a639539fSMauro Carvalho Chehab optype = "reserved";
1738a639539fSMauro Carvalho Chehab break;
1739a639539fSMauro Carvalho Chehab }
1740a639539fSMauro Carvalho Chehab
17418a2f118eSMauro Carvalho Chehab switch (errnum) {
17428a2f118eSMauro Carvalho Chehab case 16:
17438a2f118eSMauro Carvalho Chehab err = "read ECC error";
17448a2f118eSMauro Carvalho Chehab break;
17458a2f118eSMauro Carvalho Chehab case 17:
17468a2f118eSMauro Carvalho Chehab err = "RAS ECC error";
17478a2f118eSMauro Carvalho Chehab break;
17488a2f118eSMauro Carvalho Chehab case 18:
17498a2f118eSMauro Carvalho Chehab err = "write parity error";
17508a2f118eSMauro Carvalho Chehab break;
17518a2f118eSMauro Carvalho Chehab case 19:
175283e548beSColin Ian King err = "redundancy loss";
17538a2f118eSMauro Carvalho Chehab break;
17548a2f118eSMauro Carvalho Chehab case 20:
17558a2f118eSMauro Carvalho Chehab err = "reserved";
17568a2f118eSMauro Carvalho Chehab break;
17578a2f118eSMauro Carvalho Chehab case 21:
17588a2f118eSMauro Carvalho Chehab err = "memory range error";
17598a2f118eSMauro Carvalho Chehab break;
17608a2f118eSMauro Carvalho Chehab case 22:
17618a2f118eSMauro Carvalho Chehab err = "RTID out of range";
17628a2f118eSMauro Carvalho Chehab break;
17638a2f118eSMauro Carvalho Chehab case 23:
17648a2f118eSMauro Carvalho Chehab err = "address parity error";
17658a2f118eSMauro Carvalho Chehab break;
17668a2f118eSMauro Carvalho Chehab case 24:
17678a2f118eSMauro Carvalho Chehab err = "byte enable parity error";
17688a2f118eSMauro Carvalho Chehab break;
17698a2f118eSMauro Carvalho Chehab default:
17708a2f118eSMauro Carvalho Chehab err = "unknown";
17718a2f118eSMauro Carvalho Chehab }
17728a2f118eSMauro Carvalho Chehab
17730975c16fSMauro Carvalho Chehab /*
17740975c16fSMauro Carvalho Chehab * Call the helper to output message
17750975c16fSMauro Carvalho Chehab * FIXME: what to do if core_err_cnt > 1? Currently, it generates
17760975c16fSMauro Carvalho Chehab * only one event
17770975c16fSMauro Carvalho Chehab */
17780975c16fSMauro Carvalho Chehab if (uncorrected_error || !pvt->is_registered)
177900d18339SMauro Carvalho Chehab edac_mc_handle_error(tp_event, mci, core_err_cnt,
17800975c16fSMauro Carvalho Chehab m->addr >> PAGE_SHIFT,
17810975c16fSMauro Carvalho Chehab m->addr & ~PAGE_MASK,
17820975c16fSMauro Carvalho Chehab syndrome,
17830975c16fSMauro Carvalho Chehab channel, dimm, -1,
178400d18339SMauro Carvalho Chehab err, optype);
1785d5381642SMauro Carvalho Chehab }
1786d5381642SMauro Carvalho Chehab
1787a0c36a1fSMauro Carvalho Chehab /*
178887d1d272SMauro Carvalho Chehab * i7core_check_error Retrieve and process errors reported by the
178987d1d272SMauro Carvalho Chehab * hardware. Called by the Core module.
179087d1d272SMauro Carvalho Chehab */
i7core_check_error(struct mem_ctl_info * mci,struct mce * m)179153595345STony Luck static void i7core_check_error(struct mem_ctl_info *mci, struct mce *m)
179287d1d272SMauro Carvalho Chehab {
1793d5381642SMauro Carvalho Chehab struct i7core_pvt *pvt = mci->pvt_info;
1794d5381642SMauro Carvalho Chehab
179553595345STony Luck i7core_mce_output_error(mci, m);
1796d5381642SMauro Carvalho Chehab
1797ca9c90baSMauro Carvalho Chehab /*
1798ca9c90baSMauro Carvalho Chehab * Now, let's increment CE error counts
1799ca9c90baSMauro Carvalho Chehab */
1800f4742949SMauro Carvalho Chehab if (!pvt->is_registered)
1801f4742949SMauro Carvalho Chehab i7core_udimm_check_mc_ecc_err(mci);
1802b4e8f0b6SMauro Carvalho Chehab else
1803f4742949SMauro Carvalho Chehab i7core_rdimm_check_mc_ecc_err(mci);
180487d1d272SMauro Carvalho Chehab }
180587d1d272SMauro Carvalho Chehab
180687d1d272SMauro Carvalho Chehab /*
180753595345STony Luck * Check that logging is enabled and that this is the right type
180853595345STony Luck * of error for us to handle.
1809d5381642SMauro Carvalho Chehab */
i7core_mce_check_error(struct notifier_block * nb,unsigned long val,void * data)18104140c542SBorislav Petkov static int i7core_mce_check_error(struct notifier_block *nb, unsigned long val,
18114140c542SBorislav Petkov void *data)
1812d5381642SMauro Carvalho Chehab {
18134140c542SBorislav Petkov struct mce *mce = (struct mce *)data;
18144140c542SBorislav Petkov struct i7core_dev *i7_dev;
18154140c542SBorislav Petkov struct mem_ctl_info *mci;
18164140c542SBorislav Petkov
18174140c542SBorislav Petkov i7_dev = get_i7core_dev(mce->socketid);
181823ba710aSTony Luck if (!i7_dev || (mce->kflags & MCE_HANDLED_CEC))
1819c4fc1956STony Luck return NOTIFY_DONE;
18204140c542SBorislav Petkov
18214140c542SBorislav Petkov mci = i7_dev->mci;
1822d5381642SMauro Carvalho Chehab
18238a2f118eSMauro Carvalho Chehab /*
18248a2f118eSMauro Carvalho Chehab * Just let mcelog handle it if the error is
18258a2f118eSMauro Carvalho Chehab * outside the memory controller
18268a2f118eSMauro Carvalho Chehab */
18278a2f118eSMauro Carvalho Chehab if (((mce->status & 0xffff) >> 7) != 1)
18284140c542SBorislav Petkov return NOTIFY_DONE;
18298a2f118eSMauro Carvalho Chehab
1830f237fcf2SMauro Carvalho Chehab /* Bank 8 registers are the only ones that we know how to handle */
1831f237fcf2SMauro Carvalho Chehab if (mce->bank != 8)
18324140c542SBorislav Petkov return NOTIFY_DONE;
1833f237fcf2SMauro Carvalho Chehab
183453595345STony Luck i7core_check_error(mci, mce);
1835c5d34528SMauro Carvalho Chehab
1836e7bf068aSDavid Sterba /* Advise mcelog that the errors were handled */
183723ba710aSTony Luck mce->kflags |= MCE_HANDLED_EDAC;
183823ba710aSTony Luck return NOTIFY_OK;
1839d5381642SMauro Carvalho Chehab }
1840d5381642SMauro Carvalho Chehab
18414140c542SBorislav Petkov static struct notifier_block i7_mce_dec = {
18424140c542SBorislav Petkov .notifier_call = i7core_mce_check_error,
18439026cc82SBorislav Petkov .priority = MCE_PRIO_EDAC,
18444140c542SBorislav Petkov };
18454140c542SBorislav Petkov
1846535e9c78SNils Carlson struct memdev_dmi_entry {
1847535e9c78SNils Carlson u8 type;
1848535e9c78SNils Carlson u8 length;
1849535e9c78SNils Carlson u16 handle;
1850535e9c78SNils Carlson u16 phys_mem_array_handle;
1851535e9c78SNils Carlson u16 mem_err_info_handle;
1852535e9c78SNils Carlson u16 total_width;
1853535e9c78SNils Carlson u16 data_width;
1854535e9c78SNils Carlson u16 size;
1855535e9c78SNils Carlson u8 form;
1856535e9c78SNils Carlson u8 device_set;
1857535e9c78SNils Carlson u8 device_locator;
1858535e9c78SNils Carlson u8 bank_locator;
1859535e9c78SNils Carlson u8 memory_type;
1860535e9c78SNils Carlson u16 type_detail;
1861535e9c78SNils Carlson u16 speed;
1862535e9c78SNils Carlson u8 manufacturer;
1863535e9c78SNils Carlson u8 serial_number;
1864535e9c78SNils Carlson u8 asset_tag;
1865535e9c78SNils Carlson u8 part_number;
1866535e9c78SNils Carlson u8 attributes;
1867535e9c78SNils Carlson u32 extended_size;
1868535e9c78SNils Carlson u16 conf_mem_clk_speed;
1869535e9c78SNils Carlson } __attribute__((__packed__));
1870535e9c78SNils Carlson
1871535e9c78SNils Carlson
1872535e9c78SNils Carlson /*
1873535e9c78SNils Carlson * Decode the DRAM Clock Frequency, be paranoid, make sure that all
1874535e9c78SNils Carlson * memory devices show the same speed, and if they don't then consider
1875535e9c78SNils Carlson * all speeds to be invalid.
1876535e9c78SNils Carlson */
decode_dclk(const struct dmi_header * dh,void * _dclk_freq)1877535e9c78SNils Carlson static void decode_dclk(const struct dmi_header *dh, void *_dclk_freq)
1878535e9c78SNils Carlson {
1879535e9c78SNils Carlson int *dclk_freq = _dclk_freq;
1880535e9c78SNils Carlson u16 dmi_mem_clk_speed;
1881535e9c78SNils Carlson
1882535e9c78SNils Carlson if (*dclk_freq == -1)
1883535e9c78SNils Carlson return;
1884535e9c78SNils Carlson
1885535e9c78SNils Carlson if (dh->type == DMI_ENTRY_MEM_DEVICE) {
1886535e9c78SNils Carlson struct memdev_dmi_entry *memdev_dmi_entry =
1887535e9c78SNils Carlson (struct memdev_dmi_entry *)dh;
1888535e9c78SNils Carlson unsigned long conf_mem_clk_speed_offset =
1889535e9c78SNils Carlson (unsigned long)&memdev_dmi_entry->conf_mem_clk_speed -
1890535e9c78SNils Carlson (unsigned long)&memdev_dmi_entry->type;
1891535e9c78SNils Carlson unsigned long speed_offset =
1892535e9c78SNils Carlson (unsigned long)&memdev_dmi_entry->speed -
1893535e9c78SNils Carlson (unsigned long)&memdev_dmi_entry->type;
1894535e9c78SNils Carlson
1895535e9c78SNils Carlson /* Check that a DIMM is present */
1896535e9c78SNils Carlson if (memdev_dmi_entry->size == 0)
1897535e9c78SNils Carlson return;
1898535e9c78SNils Carlson
1899535e9c78SNils Carlson /*
1900535e9c78SNils Carlson * Pick the configured speed if it's available, otherwise
1901535e9c78SNils Carlson * pick the DIMM speed, or we don't have a speed.
1902535e9c78SNils Carlson */
1903535e9c78SNils Carlson if (memdev_dmi_entry->length > conf_mem_clk_speed_offset) {
1904535e9c78SNils Carlson dmi_mem_clk_speed =
1905535e9c78SNils Carlson memdev_dmi_entry->conf_mem_clk_speed;
1906535e9c78SNils Carlson } else if (memdev_dmi_entry->length > speed_offset) {
1907535e9c78SNils Carlson dmi_mem_clk_speed = memdev_dmi_entry->speed;
1908535e9c78SNils Carlson } else {
1909535e9c78SNils Carlson *dclk_freq = -1;
1910535e9c78SNils Carlson return;
1911535e9c78SNils Carlson }
1912535e9c78SNils Carlson
1913535e9c78SNils Carlson if (*dclk_freq == 0) {
1914535e9c78SNils Carlson /* First pass, speed was 0 */
1915535e9c78SNils Carlson if (dmi_mem_clk_speed > 0) {
1916535e9c78SNils Carlson /* Set speed if a valid speed is read */
1917535e9c78SNils Carlson *dclk_freq = dmi_mem_clk_speed;
1918535e9c78SNils Carlson } else {
1919535e9c78SNils Carlson /* Otherwise we don't have a valid speed */
1920535e9c78SNils Carlson *dclk_freq = -1;
1921535e9c78SNils Carlson }
1922535e9c78SNils Carlson } else if (*dclk_freq > 0 &&
1923535e9c78SNils Carlson *dclk_freq != dmi_mem_clk_speed) {
1924535e9c78SNils Carlson /*
1925535e9c78SNils Carlson * If we have a speed, check that all DIMMS are the same
1926535e9c78SNils Carlson * speed, otherwise set the speed as invalid.
1927535e9c78SNils Carlson */
1928535e9c78SNils Carlson *dclk_freq = -1;
1929535e9c78SNils Carlson }
1930535e9c78SNils Carlson }
1931535e9c78SNils Carlson }
1932535e9c78SNils Carlson
1933535e9c78SNils Carlson /*
1934535e9c78SNils Carlson * The default DCLK frequency is used as a fallback if we
1935535e9c78SNils Carlson * fail to find anything reliable in the DMI. The value
1936535e9c78SNils Carlson * is taken straight from the datasheet.
1937535e9c78SNils Carlson */
1938535e9c78SNils Carlson #define DEFAULT_DCLK_FREQ 800
1939535e9c78SNils Carlson
get_dclk_freq(void)1940535e9c78SNils Carlson static int get_dclk_freq(void)
1941535e9c78SNils Carlson {
1942535e9c78SNils Carlson int dclk_freq = 0;
1943535e9c78SNils Carlson
1944535e9c78SNils Carlson dmi_walk(decode_dclk, (void *)&dclk_freq);
1945535e9c78SNils Carlson
1946535e9c78SNils Carlson if (dclk_freq < 1)
1947535e9c78SNils Carlson return DEFAULT_DCLK_FREQ;
1948535e9c78SNils Carlson
1949535e9c78SNils Carlson return dclk_freq;
1950535e9c78SNils Carlson }
1951535e9c78SNils Carlson
1952e8b6a127SSamuel Gabrielsson /*
1953e8b6a127SSamuel Gabrielsson * set_sdram_scrub_rate This routine sets byte/sec bandwidth scrub rate
1954e8b6a127SSamuel Gabrielsson * to hardware according to SCRUBINTERVAL formula
1955e8b6a127SSamuel Gabrielsson * found in datasheet.
1956e8b6a127SSamuel Gabrielsson */
set_sdram_scrub_rate(struct mem_ctl_info * mci,u32 new_bw)1957e8b6a127SSamuel Gabrielsson static int set_sdram_scrub_rate(struct mem_ctl_info *mci, u32 new_bw)
1958e8b6a127SSamuel Gabrielsson {
1959e8b6a127SSamuel Gabrielsson struct i7core_pvt *pvt = mci->pvt_info;
1960e8b6a127SSamuel Gabrielsson struct pci_dev *pdev;
1961e8b6a127SSamuel Gabrielsson u32 dw_scrub;
1962e8b6a127SSamuel Gabrielsson u32 dw_ssr;
1963e8b6a127SSamuel Gabrielsson
1964e8b6a127SSamuel Gabrielsson /* Get data from the MC register, function 2 */
1965e8b6a127SSamuel Gabrielsson pdev = pvt->pci_mcr[2];
1966e8b6a127SSamuel Gabrielsson if (!pdev)
1967e8b6a127SSamuel Gabrielsson return -ENODEV;
1968e8b6a127SSamuel Gabrielsson
1969e8b6a127SSamuel Gabrielsson pci_read_config_dword(pdev, MC_SCRUB_CONTROL, &dw_scrub);
1970e8b6a127SSamuel Gabrielsson
1971e8b6a127SSamuel Gabrielsson if (new_bw == 0) {
1972e8b6a127SSamuel Gabrielsson /* Prepare to disable petrol scrub */
1973e8b6a127SSamuel Gabrielsson dw_scrub &= ~STARTSCRUB;
1974e8b6a127SSamuel Gabrielsson /* Stop the patrol scrub engine */
1975535e9c78SNils Carlson write_and_test(pdev, MC_SCRUB_CONTROL,
1976535e9c78SNils Carlson dw_scrub & ~SCRUBINTERVAL_MASK);
1977e8b6a127SSamuel Gabrielsson
1978e8b6a127SSamuel Gabrielsson /* Get current status of scrub rate and set bit to disable */
1979e8b6a127SSamuel Gabrielsson pci_read_config_dword(pdev, MC_SSRCONTROL, &dw_ssr);
1980e8b6a127SSamuel Gabrielsson dw_ssr &= ~SSR_MODE_MASK;
1981e8b6a127SSamuel Gabrielsson dw_ssr |= SSR_MODE_DISABLE;
1982e8b6a127SSamuel Gabrielsson } else {
1983535e9c78SNils Carlson const int cache_line_size = 64;
1984535e9c78SNils Carlson const u32 freq_dclk_mhz = pvt->dclk_freq;
1985535e9c78SNils Carlson unsigned long long scrub_interval;
1986e8b6a127SSamuel Gabrielsson /*
1987e8b6a127SSamuel Gabrielsson * Translate the desired scrub rate to a register value and
1988535e9c78SNils Carlson * program the corresponding register value.
1989e8b6a127SSamuel Gabrielsson */
1990535e9c78SNils Carlson scrub_interval = (unsigned long long)freq_dclk_mhz *
19914fad8098SSedat Dilek cache_line_size * 1000000;
19924fad8098SSedat Dilek do_div(scrub_interval, new_bw);
1993535e9c78SNils Carlson
1994535e9c78SNils Carlson if (!scrub_interval || scrub_interval > SCRUBINTERVAL_MASK)
1995535e9c78SNils Carlson return -EINVAL;
1996535e9c78SNils Carlson
1997535e9c78SNils Carlson dw_scrub = SCRUBINTERVAL_MASK & scrub_interval;
1998e8b6a127SSamuel Gabrielsson
1999e8b6a127SSamuel Gabrielsson /* Start the patrol scrub engine */
2000e8b6a127SSamuel Gabrielsson pci_write_config_dword(pdev, MC_SCRUB_CONTROL,
2001e8b6a127SSamuel Gabrielsson STARTSCRUB | dw_scrub);
2002e8b6a127SSamuel Gabrielsson
2003e8b6a127SSamuel Gabrielsson /* Get current status of scrub rate and set bit to enable */
2004e8b6a127SSamuel Gabrielsson pci_read_config_dword(pdev, MC_SSRCONTROL, &dw_ssr);
2005e8b6a127SSamuel Gabrielsson dw_ssr &= ~SSR_MODE_MASK;
2006e8b6a127SSamuel Gabrielsson dw_ssr |= SSR_MODE_ENABLE;
2007e8b6a127SSamuel Gabrielsson }
2008e8b6a127SSamuel Gabrielsson /* Disable or enable scrubbing */
2009e8b6a127SSamuel Gabrielsson pci_write_config_dword(pdev, MC_SSRCONTROL, dw_ssr);
2010e8b6a127SSamuel Gabrielsson
2011e8b6a127SSamuel Gabrielsson return new_bw;
2012e8b6a127SSamuel Gabrielsson }
2013e8b6a127SSamuel Gabrielsson
2014e8b6a127SSamuel Gabrielsson /*
2015e8b6a127SSamuel Gabrielsson * get_sdram_scrub_rate This routine convert current scrub rate value
201615ed103aSDavid Mackey * into byte/sec bandwidth according to
2017e8b6a127SSamuel Gabrielsson * SCRUBINTERVAL formula found in datasheet.
2018e8b6a127SSamuel Gabrielsson */
get_sdram_scrub_rate(struct mem_ctl_info * mci)2019e8b6a127SSamuel Gabrielsson static int get_sdram_scrub_rate(struct mem_ctl_info *mci)
2020e8b6a127SSamuel Gabrielsson {
2021e8b6a127SSamuel Gabrielsson struct i7core_pvt *pvt = mci->pvt_info;
2022e8b6a127SSamuel Gabrielsson struct pci_dev *pdev;
2023e8b6a127SSamuel Gabrielsson const u32 cache_line_size = 64;
2024535e9c78SNils Carlson const u32 freq_dclk_mhz = pvt->dclk_freq;
2025535e9c78SNils Carlson unsigned long long scrub_rate;
2026e8b6a127SSamuel Gabrielsson u32 scrubval;
2027e8b6a127SSamuel Gabrielsson
2028e8b6a127SSamuel Gabrielsson /* Get data from the MC register, function 2 */
2029e8b6a127SSamuel Gabrielsson pdev = pvt->pci_mcr[2];
2030e8b6a127SSamuel Gabrielsson if (!pdev)
2031e8b6a127SSamuel Gabrielsson return -ENODEV;
2032e8b6a127SSamuel Gabrielsson
2033e8b6a127SSamuel Gabrielsson /* Get current scrub control data */
2034e8b6a127SSamuel Gabrielsson pci_read_config_dword(pdev, MC_SCRUB_CONTROL, &scrubval);
2035e8b6a127SSamuel Gabrielsson
2036e8b6a127SSamuel Gabrielsson /* Mask highest 8-bits to 0 */
2037535e9c78SNils Carlson scrubval &= SCRUBINTERVAL_MASK;
2038e8b6a127SSamuel Gabrielsson if (!scrubval)
2039e8b6a127SSamuel Gabrielsson return 0;
2040e8b6a127SSamuel Gabrielsson
2041e8b6a127SSamuel Gabrielsson /* Calculate scrub rate value into byte/sec bandwidth */
2042535e9c78SNils Carlson scrub_rate = (unsigned long long)freq_dclk_mhz *
20434fad8098SSedat Dilek 1000000 * cache_line_size;
20444fad8098SSedat Dilek do_div(scrub_rate, scrubval);
2045535e9c78SNils Carlson return (int)scrub_rate;
2046e8b6a127SSamuel Gabrielsson }
2047e8b6a127SSamuel Gabrielsson
enable_sdram_scrub_setting(struct mem_ctl_info * mci)2048e8b6a127SSamuel Gabrielsson static void enable_sdram_scrub_setting(struct mem_ctl_info *mci)
2049e8b6a127SSamuel Gabrielsson {
2050e8b6a127SSamuel Gabrielsson struct i7core_pvt *pvt = mci->pvt_info;
2051e8b6a127SSamuel Gabrielsson u32 pci_lock;
2052e8b6a127SSamuel Gabrielsson
2053e8b6a127SSamuel Gabrielsson /* Unlock writes to pci registers */
2054e8b6a127SSamuel Gabrielsson pci_read_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, &pci_lock);
2055e8b6a127SSamuel Gabrielsson pci_lock &= ~0x3;
2056e8b6a127SSamuel Gabrielsson pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL,
2057e8b6a127SSamuel Gabrielsson pci_lock | MC_CFG_UNLOCK);
2058e8b6a127SSamuel Gabrielsson
2059e8b6a127SSamuel Gabrielsson mci->set_sdram_scrub_rate = set_sdram_scrub_rate;
2060e8b6a127SSamuel Gabrielsson mci->get_sdram_scrub_rate = get_sdram_scrub_rate;
2061e8b6a127SSamuel Gabrielsson }
2062e8b6a127SSamuel Gabrielsson
disable_sdram_scrub_setting(struct mem_ctl_info * mci)2063e8b6a127SSamuel Gabrielsson static void disable_sdram_scrub_setting(struct mem_ctl_info *mci)
2064e8b6a127SSamuel Gabrielsson {
2065e8b6a127SSamuel Gabrielsson struct i7core_pvt *pvt = mci->pvt_info;
2066e8b6a127SSamuel Gabrielsson u32 pci_lock;
2067e8b6a127SSamuel Gabrielsson
2068e8b6a127SSamuel Gabrielsson /* Lock writes to pci registers */
2069e8b6a127SSamuel Gabrielsson pci_read_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, &pci_lock);
2070e8b6a127SSamuel Gabrielsson pci_lock &= ~0x3;
2071e8b6a127SSamuel Gabrielsson pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL,
2072e8b6a127SSamuel Gabrielsson pci_lock | MC_CFG_LOCK);
2073e8b6a127SSamuel Gabrielsson }
2074e8b6a127SSamuel Gabrielsson
i7core_pci_ctl_create(struct i7core_pvt * pvt)2075a3aa0a4aSHidetoshi Seto static void i7core_pci_ctl_create(struct i7core_pvt *pvt)
2076a3aa0a4aSHidetoshi Seto {
2077a3aa0a4aSHidetoshi Seto pvt->i7core_pci = edac_pci_create_generic_ctl(
2078a3aa0a4aSHidetoshi Seto &pvt->i7core_dev->pdev[0]->dev,
2079a3aa0a4aSHidetoshi Seto EDAC_MOD_STR);
2080a3aa0a4aSHidetoshi Seto if (unlikely(!pvt->i7core_pci))
2081f9902f24SMauro Carvalho Chehab i7core_printk(KERN_WARNING,
2082f9902f24SMauro Carvalho Chehab "Unable to setup PCI error report via EDAC\n");
2083a3aa0a4aSHidetoshi Seto }
2084a3aa0a4aSHidetoshi Seto
i7core_pci_ctl_release(struct i7core_pvt * pvt)2085a3aa0a4aSHidetoshi Seto static void i7core_pci_ctl_release(struct i7core_pvt *pvt)
2086a3aa0a4aSHidetoshi Seto {
2087a3aa0a4aSHidetoshi Seto if (likely(pvt->i7core_pci))
2088a3aa0a4aSHidetoshi Seto edac_pci_release_generic_ctl(pvt->i7core_pci);
2089a3aa0a4aSHidetoshi Seto else
2090a3aa0a4aSHidetoshi Seto i7core_printk(KERN_ERR,
2091a3aa0a4aSHidetoshi Seto "Couldn't find mem_ctl_info for socket %d\n",
2092a3aa0a4aSHidetoshi Seto pvt->i7core_dev->socket);
2093a3aa0a4aSHidetoshi Seto pvt->i7core_pci = NULL;
2094a3aa0a4aSHidetoshi Seto }
2095a3aa0a4aSHidetoshi Seto
i7core_unregister_mci(struct i7core_dev * i7core_dev)20961c6edbbeSHidetoshi Seto static void i7core_unregister_mci(struct i7core_dev *i7core_dev)
20971c6edbbeSHidetoshi Seto {
20981c6edbbeSHidetoshi Seto struct mem_ctl_info *mci = i7core_dev->mci;
20991c6edbbeSHidetoshi Seto struct i7core_pvt *pvt;
21001c6edbbeSHidetoshi Seto
21011c6edbbeSHidetoshi Seto if (unlikely(!mci || !mci->pvt_info)) {
2102956b9ba1SJoe Perches edac_dbg(0, "MC: dev = %p\n", &i7core_dev->pdev[0]->dev);
21031c6edbbeSHidetoshi Seto
21041c6edbbeSHidetoshi Seto i7core_printk(KERN_ERR, "Couldn't find mci handler\n");
21051c6edbbeSHidetoshi Seto return;
21061c6edbbeSHidetoshi Seto }
21071c6edbbeSHidetoshi Seto
21081c6edbbeSHidetoshi Seto pvt = mci->pvt_info;
21091c6edbbeSHidetoshi Seto
2110956b9ba1SJoe Perches edac_dbg(0, "MC: mci = %p, dev = %p\n", mci, &i7core_dev->pdev[0]->dev);
21111c6edbbeSHidetoshi Seto
2112e8b6a127SSamuel Gabrielsson /* Disable scrubrate setting */
211327100db0SMauro Carvalho Chehab if (pvt->enable_scrub)
2114e8b6a127SSamuel Gabrielsson disable_sdram_scrub_setting(mci);
2115e8b6a127SSamuel Gabrielsson
21161c6edbbeSHidetoshi Seto /* Disable EDAC polling */
21171c6edbbeSHidetoshi Seto i7core_pci_ctl_release(pvt);
21181c6edbbeSHidetoshi Seto
21191c6edbbeSHidetoshi Seto /* Remove MC sysfs nodes */
21205c4cdb5aSMauro Carvalho Chehab i7core_delete_sysfs_devices(mci);
2121fd687502SMauro Carvalho Chehab edac_mc_del_mc(mci->pdev);
21221c6edbbeSHidetoshi Seto
2123956b9ba1SJoe Perches edac_dbg(1, "%s: free mci struct\n", mci->ctl_name);
21241c6edbbeSHidetoshi Seto kfree(mci->ctl_name);
21251c6edbbeSHidetoshi Seto edac_mc_free(mci);
21261c6edbbeSHidetoshi Seto i7core_dev->mci = NULL;
21271c6edbbeSHidetoshi Seto }
21281c6edbbeSHidetoshi Seto
i7core_register_mci(struct i7core_dev * i7core_dev)2129aace4283SHidetoshi Seto static int i7core_register_mci(struct i7core_dev *i7core_dev)
2130a0c36a1fSMauro Carvalho Chehab {
2131a0c36a1fSMauro Carvalho Chehab struct mem_ctl_info *mci;
2132a0c36a1fSMauro Carvalho Chehab struct i7core_pvt *pvt;
21330975c16fSMauro Carvalho Chehab int rc;
21340975c16fSMauro Carvalho Chehab struct edac_mc_layer layers[2];
213567166af4SMauro Carvalho Chehab
2136a0c36a1fSMauro Carvalho Chehab /* allocate a new MC control structure */
21370975c16fSMauro Carvalho Chehab
21380975c16fSMauro Carvalho Chehab layers[0].type = EDAC_MC_LAYER_CHANNEL;
21390975c16fSMauro Carvalho Chehab layers[0].size = NUM_CHANS;
21400975c16fSMauro Carvalho Chehab layers[0].is_virt_csrow = false;
21410975c16fSMauro Carvalho Chehab layers[1].type = EDAC_MC_LAYER_SLOT;
21420975c16fSMauro Carvalho Chehab layers[1].size = MAX_DIMMS;
21430975c16fSMauro Carvalho Chehab layers[1].is_virt_csrow = true;
2144ca0907b9SMauro Carvalho Chehab mci = edac_mc_alloc(i7core_dev->socket, ARRAY_SIZE(layers), layers,
21450975c16fSMauro Carvalho Chehab sizeof(*pvt));
2146f4742949SMauro Carvalho Chehab if (unlikely(!mci))
2147f4742949SMauro Carvalho Chehab return -ENOMEM;
2148a0c36a1fSMauro Carvalho Chehab
2149956b9ba1SJoe Perches edac_dbg(0, "MC: mci = %p, dev = %p\n", mci, &i7core_dev->pdev[0]->dev);
2150a0c36a1fSMauro Carvalho Chehab
2151a0c36a1fSMauro Carvalho Chehab pvt = mci->pvt_info;
2152ef708b53SMauro Carvalho Chehab memset(pvt, 0, sizeof(*pvt));
215367166af4SMauro Carvalho Chehab
21546d37d240SMauro Carvalho Chehab /* Associates i7core_dev and mci for future usage */
21556d37d240SMauro Carvalho Chehab pvt->i7core_dev = i7core_dev;
21566d37d240SMauro Carvalho Chehab i7core_dev->mci = mci;
21576d37d240SMauro Carvalho Chehab
215841fcb7feSMauro Carvalho Chehab /*
215941fcb7feSMauro Carvalho Chehab * FIXME: how to handle RDDR3 at MCI level? It is possible to have
216041fcb7feSMauro Carvalho Chehab * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
216141fcb7feSMauro Carvalho Chehab * memory channels
216241fcb7feSMauro Carvalho Chehab */
216341fcb7feSMauro Carvalho Chehab mci->mtype_cap = MEM_FLAG_DDR3;
2164a0c36a1fSMauro Carvalho Chehab mci->edac_ctl_cap = EDAC_FLAG_NONE;
2165a0c36a1fSMauro Carvalho Chehab mci->edac_cap = EDAC_FLAG_NONE;
2166a0c36a1fSMauro Carvalho Chehab mci->mod_name = "i7core_edac.c";
216775f029c3SArvind Yadav
216875f029c3SArvind Yadav mci->ctl_name = kasprintf(GFP_KERNEL, "i7 core #%d", i7core_dev->socket);
216975f029c3SArvind Yadav if (!mci->ctl_name) {
217075f029c3SArvind Yadav rc = -ENOMEM;
217175f029c3SArvind Yadav goto fail1;
217275f029c3SArvind Yadav }
217375f029c3SArvind Yadav
2174f4742949SMauro Carvalho Chehab mci->dev_name = pci_name(i7core_dev->pdev[0]);
2175a0c36a1fSMauro Carvalho Chehab mci->ctl_page_to_phys = NULL;
21761288c18fSMauro Carvalho Chehab
2177ef708b53SMauro Carvalho Chehab /* Store pci devices at mci for faster access */
2178f4742949SMauro Carvalho Chehab rc = mci_bind_devs(mci, i7core_dev);
2179b7c76151SMauro Carvalho Chehab if (unlikely(rc < 0))
2180628c5ddfSHidetoshi Seto goto fail0;
2181ef708b53SMauro Carvalho Chehab
21825939813bSHidetoshi Seto
2183ef708b53SMauro Carvalho Chehab /* Get dimm basic config */
21842e5185f7SHidetoshi Seto get_dimm_config(mci);
21855939813bSHidetoshi Seto /* record ptr to the generic device */
2186fd687502SMauro Carvalho Chehab mci->pdev = &i7core_dev->pdev[0]->dev;
2187ef708b53SMauro Carvalho Chehab
2188e8b6a127SSamuel Gabrielsson /* Enable scrubrate setting */
218927100db0SMauro Carvalho Chehab if (pvt->enable_scrub)
2190e8b6a127SSamuel Gabrielsson enable_sdram_scrub_setting(mci);
2191e8b6a127SSamuel Gabrielsson
2192a0c36a1fSMauro Carvalho Chehab /* add this new MC control structure to EDAC's list of MCs */
21932eace188STakashi Iwai if (unlikely(edac_mc_add_mc_with_groups(mci, i7core_dev_groups))) {
2194956b9ba1SJoe Perches edac_dbg(0, "MC: failed edac_mc_add_mc()\n");
2195a0c36a1fSMauro Carvalho Chehab /* FIXME: perhaps some code should go here that disables error
2196a0c36a1fSMauro Carvalho Chehab * reporting if we just enabled it
2197a0c36a1fSMauro Carvalho Chehab */
2198b7c76151SMauro Carvalho Chehab
2199b7c76151SMauro Carvalho Chehab rc = -EINVAL;
2200628c5ddfSHidetoshi Seto goto fail0;
2201a0c36a1fSMauro Carvalho Chehab }
22025c4cdb5aSMauro Carvalho Chehab if (i7core_create_sysfs_devices(mci)) {
2203956b9ba1SJoe Perches edac_dbg(0, "MC: failed to create sysfs nodes\n");
22045c4cdb5aSMauro Carvalho Chehab edac_mc_del_mc(mci->pdev);
22055c4cdb5aSMauro Carvalho Chehab rc = -EINVAL;
22065c4cdb5aSMauro Carvalho Chehab goto fail0;
22075c4cdb5aSMauro Carvalho Chehab }
2208a0c36a1fSMauro Carvalho Chehab
2209194a40feSMauro Carvalho Chehab /* Default error mask is any memory */
2210ef708b53SMauro Carvalho Chehab pvt->inject.channel = 0;
2211194a40feSMauro Carvalho Chehab pvt->inject.dimm = -1;
2212194a40feSMauro Carvalho Chehab pvt->inject.rank = -1;
2213194a40feSMauro Carvalho Chehab pvt->inject.bank = -1;
2214194a40feSMauro Carvalho Chehab pvt->inject.page = -1;
2215194a40feSMauro Carvalho Chehab pvt->inject.col = -1;
2216194a40feSMauro Carvalho Chehab
2217a3aa0a4aSHidetoshi Seto /* allocating generic PCI control info */
2218a3aa0a4aSHidetoshi Seto i7core_pci_ctl_create(pvt);
2219a3aa0a4aSHidetoshi Seto
2220535e9c78SNils Carlson /* DCLK for scrub rate setting */
2221535e9c78SNils Carlson pvt->dclk_freq = get_dclk_freq();
2222535e9c78SNils Carlson
2223628c5ddfSHidetoshi Seto return 0;
2224628c5ddfSHidetoshi Seto
2225628c5ddfSHidetoshi Seto fail0:
2226628c5ddfSHidetoshi Seto kfree(mci->ctl_name);
222775f029c3SArvind Yadav
222875f029c3SArvind Yadav fail1:
2229f4742949SMauro Carvalho Chehab edac_mc_free(mci);
22301c6edbbeSHidetoshi Seto i7core_dev->mci = NULL;
2231f4742949SMauro Carvalho Chehab return rc;
2232f4742949SMauro Carvalho Chehab }
2233f4742949SMauro Carvalho Chehab
2234f4742949SMauro Carvalho Chehab /*
2235f4742949SMauro Carvalho Chehab * i7core_probe Probe for ONE instance of device to see if it is
2236f4742949SMauro Carvalho Chehab * present.
2237f4742949SMauro Carvalho Chehab * return:
2238f4742949SMauro Carvalho Chehab * 0 for FOUND a device
2239f4742949SMauro Carvalho Chehab * < 0 for error code
2240f4742949SMauro Carvalho Chehab */
22412d95d815SMauro Carvalho Chehab
i7core_probe(struct pci_dev * pdev,const struct pci_device_id * id)22429b3c6e85SGreg Kroah-Hartman static int i7core_probe(struct pci_dev *pdev, const struct pci_device_id *id)
2243f4742949SMauro Carvalho Chehab {
224440557591SMauro Carvalho Chehab int rc, count = 0;
2245f4742949SMauro Carvalho Chehab struct i7core_dev *i7core_dev;
2246f4742949SMauro Carvalho Chehab
22472d95d815SMauro Carvalho Chehab /* get the pci devices we want to reserve for our use */
22482d95d815SMauro Carvalho Chehab mutex_lock(&i7core_edac_lock);
22492d95d815SMauro Carvalho Chehab
2250f4742949SMauro Carvalho Chehab /*
2251d4c27795SMauro Carvalho Chehab * All memory controllers are allocated at the first pass.
2252f4742949SMauro Carvalho Chehab */
22532d95d815SMauro Carvalho Chehab if (unlikely(probed >= 1)) {
22542d95d815SMauro Carvalho Chehab mutex_unlock(&i7core_edac_lock);
225576a7bd81SMauro Carvalho Chehab return -ENODEV;
22562d95d815SMauro Carvalho Chehab }
22572d95d815SMauro Carvalho Chehab probed++;
2258de06eeefSMauro Carvalho Chehab
225964c10f6eSHidetoshi Seto rc = i7core_get_all_devices();
2260f4742949SMauro Carvalho Chehab if (unlikely(rc < 0))
2261f4742949SMauro Carvalho Chehab goto fail0;
2262f4742949SMauro Carvalho Chehab
2263f4742949SMauro Carvalho Chehab list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
226440557591SMauro Carvalho Chehab count++;
2265aace4283SHidetoshi Seto rc = i7core_register_mci(i7core_dev);
2266d4c27795SMauro Carvalho Chehab if (unlikely(rc < 0))
2267d4c27795SMauro Carvalho Chehab goto fail1;
2268d5381642SMauro Carvalho Chehab }
2269d5381642SMauro Carvalho Chehab
227040557591SMauro Carvalho Chehab /*
227140557591SMauro Carvalho Chehab * Nehalem-EX uses a different memory controller. However, as the
227240557591SMauro Carvalho Chehab * memory controller is not visible on some Nehalem/Nehalem-EP, we
227340557591SMauro Carvalho Chehab * need to indirectly probe via a X58 PCI device. The same devices
227440557591SMauro Carvalho Chehab * are found on (some) Nehalem-EX. So, on those machines, the
227540557591SMauro Carvalho Chehab * probe routine needs to return -ENODEV, as the actual Memory
227640557591SMauro Carvalho Chehab * Controller registers won't be detected.
227740557591SMauro Carvalho Chehab */
227840557591SMauro Carvalho Chehab if (!count) {
227940557591SMauro Carvalho Chehab rc = -ENODEV;
228040557591SMauro Carvalho Chehab goto fail1;
228140557591SMauro Carvalho Chehab }
228240557591SMauro Carvalho Chehab
228340557591SMauro Carvalho Chehab i7core_printk(KERN_INFO,
228440557591SMauro Carvalho Chehab "Driver loaded, %d memory controller(s) found.\n",
228540557591SMauro Carvalho Chehab count);
22868f331907SMauro Carvalho Chehab
228766607706SMauro Carvalho Chehab mutex_unlock(&i7core_edac_lock);
2288a0c36a1fSMauro Carvalho Chehab return 0;
2289a0c36a1fSMauro Carvalho Chehab
229066607706SMauro Carvalho Chehab fail1:
229188ef5ea9SMauro Carvalho Chehab list_for_each_entry(i7core_dev, &i7core_edac_list, list)
22921c6edbbeSHidetoshi Seto i7core_unregister_mci(i7core_dev);
229388ef5ea9SMauro Carvalho Chehab
229413d6e9b6SMauro Carvalho Chehab i7core_put_all_devices();
229566607706SMauro Carvalho Chehab fail0:
229666607706SMauro Carvalho Chehab mutex_unlock(&i7core_edac_lock);
2297b7c76151SMauro Carvalho Chehab return rc;
2298a0c36a1fSMauro Carvalho Chehab }
2299a0c36a1fSMauro Carvalho Chehab
2300a0c36a1fSMauro Carvalho Chehab /*
2301a0c36a1fSMauro Carvalho Chehab * i7core_remove destructor for one instance of device
2302a0c36a1fSMauro Carvalho Chehab *
2303a0c36a1fSMauro Carvalho Chehab */
i7core_remove(struct pci_dev * pdev)23049b3c6e85SGreg Kroah-Hartman static void i7core_remove(struct pci_dev *pdev)
2305a0c36a1fSMauro Carvalho Chehab {
230664c10f6eSHidetoshi Seto struct i7core_dev *i7core_dev;
2307a0c36a1fSMauro Carvalho Chehab
2308956b9ba1SJoe Perches edac_dbg(0, "\n");
2309a0c36a1fSMauro Carvalho Chehab
231022e6bcbdSMauro Carvalho Chehab /*
231122e6bcbdSMauro Carvalho Chehab * we have a trouble here: pdev value for removal will be wrong, since
231222e6bcbdSMauro Carvalho Chehab * it will point to the X58 register used to detect that the machine
231322e6bcbdSMauro Carvalho Chehab * is a Nehalem or upper design. However, due to the way several PCI
231422e6bcbdSMauro Carvalho Chehab * devices are grouped together to provide MC functionality, we need
231522e6bcbdSMauro Carvalho Chehab * to use a different method for releasing the devices
231622e6bcbdSMauro Carvalho Chehab */
231787d1d272SMauro Carvalho Chehab
231822e6bcbdSMauro Carvalho Chehab mutex_lock(&i7core_edac_lock);
231971fe0170SHidetoshi Seto
232071fe0170SHidetoshi Seto if (unlikely(!probed)) {
232171fe0170SHidetoshi Seto mutex_unlock(&i7core_edac_lock);
232271fe0170SHidetoshi Seto return;
232371fe0170SHidetoshi Seto }
232471fe0170SHidetoshi Seto
232588ef5ea9SMauro Carvalho Chehab list_for_each_entry(i7core_dev, &i7core_edac_list, list)
23261c6edbbeSHidetoshi Seto i7core_unregister_mci(i7core_dev);
232741ba6c10SMauro Carvalho Chehab
232841ba6c10SMauro Carvalho Chehab /* Release PCI resources */
232964c10f6eSHidetoshi Seto i7core_put_all_devices();
233064c10f6eSHidetoshi Seto
23312d95d815SMauro Carvalho Chehab probed--;
23322d95d815SMauro Carvalho Chehab
233322e6bcbdSMauro Carvalho Chehab mutex_unlock(&i7core_edac_lock);
2334a0c36a1fSMauro Carvalho Chehab }
2335a0c36a1fSMauro Carvalho Chehab
2336a0c36a1fSMauro Carvalho Chehab MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
2337a0c36a1fSMauro Carvalho Chehab
2338a0c36a1fSMauro Carvalho Chehab /*
2339a0c36a1fSMauro Carvalho Chehab * i7core_driver pci_driver structure for this module
2340a0c36a1fSMauro Carvalho Chehab *
2341a0c36a1fSMauro Carvalho Chehab */
2342a0c36a1fSMauro Carvalho Chehab static struct pci_driver i7core_driver = {
2343a0c36a1fSMauro Carvalho Chehab .name = "i7core_edac",
2344a0c36a1fSMauro Carvalho Chehab .probe = i7core_probe,
23459b3c6e85SGreg Kroah-Hartman .remove = i7core_remove,
2346a0c36a1fSMauro Carvalho Chehab .id_table = i7core_pci_tbl,
2347a0c36a1fSMauro Carvalho Chehab };
2348a0c36a1fSMauro Carvalho Chehab
2349a0c36a1fSMauro Carvalho Chehab /*
2350a0c36a1fSMauro Carvalho Chehab * i7core_init Module entry function
2351a0c36a1fSMauro Carvalho Chehab * Try to initialize this module for its devices
2352a0c36a1fSMauro Carvalho Chehab */
i7core_init(void)2353a0c36a1fSMauro Carvalho Chehab static int __init i7core_init(void)
2354a0c36a1fSMauro Carvalho Chehab {
2355a0c36a1fSMauro Carvalho Chehab int pci_rc;
2356a0c36a1fSMauro Carvalho Chehab
2357956b9ba1SJoe Perches edac_dbg(2, "\n");
2358a0c36a1fSMauro Carvalho Chehab
2359a0c36a1fSMauro Carvalho Chehab /* Ensure that the OPSTATE is set correctly for POLL or NMI */
2360a0c36a1fSMauro Carvalho Chehab opstate_init();
2361a0c36a1fSMauro Carvalho Chehab
236254a08ab1SMauro Carvalho Chehab if (use_pci_fixup)
2363bd9e19caSVernon Mauery i7core_xeon_pci_fixup(pci_dev_table);
2364bc2d7245SKeith Mannthey
2365a0c36a1fSMauro Carvalho Chehab pci_rc = pci_register_driver(&i7core_driver);
2366a0c36a1fSMauro Carvalho Chehab
2367e35fca47SChen Gong if (pci_rc >= 0) {
2368e35fca47SChen Gong mce_register_decode_chain(&i7_mce_dec);
23693ef288a9SMauro Carvalho Chehab return 0;
2370e35fca47SChen Gong }
23713ef288a9SMauro Carvalho Chehab
23723ef288a9SMauro Carvalho Chehab i7core_printk(KERN_ERR, "Failed to register device with error %d.\n",
23733ef288a9SMauro Carvalho Chehab pci_rc);
23743ef288a9SMauro Carvalho Chehab
23753ef288a9SMauro Carvalho Chehab return pci_rc;
2376a0c36a1fSMauro Carvalho Chehab }
2377a0c36a1fSMauro Carvalho Chehab
2378a0c36a1fSMauro Carvalho Chehab /*
2379a0c36a1fSMauro Carvalho Chehab * i7core_exit() Module exit function
2380a0c36a1fSMauro Carvalho Chehab * Unregister the driver
2381a0c36a1fSMauro Carvalho Chehab */
i7core_exit(void)2382a0c36a1fSMauro Carvalho Chehab static void __exit i7core_exit(void)
2383a0c36a1fSMauro Carvalho Chehab {
2384956b9ba1SJoe Perches edac_dbg(2, "\n");
2385a0c36a1fSMauro Carvalho Chehab pci_unregister_driver(&i7core_driver);
2386e35fca47SChen Gong mce_unregister_decode_chain(&i7_mce_dec);
2387a0c36a1fSMauro Carvalho Chehab }
2388a0c36a1fSMauro Carvalho Chehab
2389a0c36a1fSMauro Carvalho Chehab module_init(i7core_init);
2390a0c36a1fSMauro Carvalho Chehab module_exit(i7core_exit);
2391a0c36a1fSMauro Carvalho Chehab
2392a0c36a1fSMauro Carvalho Chehab MODULE_LICENSE("GPL");
239337e59f87SMauro Carvalho Chehab MODULE_AUTHOR("Mauro Carvalho Chehab");
2394a0c36a1fSMauro Carvalho Chehab MODULE_AUTHOR("Red Hat Inc. (https://www.redhat.com)");
2395a0c36a1fSMauro Carvalho Chehab MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
2396a0c36a1fSMauro Carvalho Chehab I7CORE_REVISION);
2397a0c36a1fSMauro Carvalho Chehab
2398a0c36a1fSMauro Carvalho Chehab module_param(edac_op_state, int, 0444);
2399a0c36a1fSMauro Carvalho Chehab MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
2400