Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
eb919690 DT |
2 | #include "amd64_edac.h" |
3 | ||
c5608759 MCC |
4 | static ssize_t amd64_inject_section_show(struct device *dev, |
5 | struct device_attribute *mattr, | |
6 | char *buf) | |
94baaee4 | 7 | { |
c5608759 | 8 | struct mem_ctl_info *mci = to_mci(dev); |
94baaee4 BP |
9 | struct amd64_pvt *pvt = mci->pvt_info; |
10 | return sprintf(buf, "0x%x\n", pvt->injection.section); | |
11 | } | |
12 | ||
eb919690 DT |
13 | /* |
14 | * store error injection section value which refers to one of 4 16-byte sections | |
15 | * within a 64-byte cacheline | |
16 | * | |
17 | * range: 0..3 | |
18 | */ | |
c5608759 MCC |
19 | static ssize_t amd64_inject_section_store(struct device *dev, |
20 | struct device_attribute *mattr, | |
eb919690 DT |
21 | const char *data, size_t count) |
22 | { | |
c5608759 | 23 | struct mem_ctl_info *mci = to_mci(dev); |
eb919690 DT |
24 | struct amd64_pvt *pvt = mci->pvt_info; |
25 | unsigned long value; | |
6e71a870 | 26 | int ret; |
eb919690 | 27 | |
c7f62fc8 | 28 | ret = kstrtoul(data, 10, &value); |
6e71a870 BP |
29 | if (ret < 0) |
30 | return ret; | |
94baaee4 | 31 | |
6e71a870 BP |
32 | if (value > 3) { |
33 | amd64_warn("%s: invalid section 0x%lx\n", __func__, value); | |
34 | return -EINVAL; | |
eb919690 | 35 | } |
6e71a870 BP |
36 | |
37 | pvt->injection.section = (u32) value; | |
38 | return count; | |
eb919690 DT |
39 | } |
40 | ||
c5608759 MCC |
41 | static ssize_t amd64_inject_word_show(struct device *dev, |
42 | struct device_attribute *mattr, | |
43 | char *buf) | |
94baaee4 | 44 | { |
c5608759 | 45 | struct mem_ctl_info *mci = to_mci(dev); |
94baaee4 BP |
46 | struct amd64_pvt *pvt = mci->pvt_info; |
47 | return sprintf(buf, "0x%x\n", pvt->injection.word); | |
48 | } | |
49 | ||
eb919690 DT |
50 | /* |
51 | * store error injection word value which refers to one of 9 16-bit word of the | |
52 | * 16-byte (128-bit + ECC bits) section | |
53 | * | |
54 | * range: 0..8 | |
55 | */ | |
c5608759 MCC |
56 | static ssize_t amd64_inject_word_store(struct device *dev, |
57 | struct device_attribute *mattr, | |
58 | const char *data, size_t count) | |
eb919690 | 59 | { |
c5608759 | 60 | struct mem_ctl_info *mci = to_mci(dev); |
eb919690 DT |
61 | struct amd64_pvt *pvt = mci->pvt_info; |
62 | unsigned long value; | |
6e71a870 | 63 | int ret; |
eb919690 | 64 | |
c7f62fc8 | 65 | ret = kstrtoul(data, 10, &value); |
6e71a870 BP |
66 | if (ret < 0) |
67 | return ret; | |
eb919690 | 68 | |
6e71a870 BP |
69 | if (value > 8) { |
70 | amd64_warn("%s: invalid word 0x%lx\n", __func__, value); | |
71 | return -EINVAL; | |
eb919690 | 72 | } |
6e71a870 BP |
73 | |
74 | pvt->injection.word = (u32) value; | |
75 | return count; | |
eb919690 DT |
76 | } |
77 | ||
c5608759 MCC |
78 | static ssize_t amd64_inject_ecc_vector_show(struct device *dev, |
79 | struct device_attribute *mattr, | |
80 | char *buf) | |
94baaee4 | 81 | { |
c5608759 | 82 | struct mem_ctl_info *mci = to_mci(dev); |
94baaee4 BP |
83 | struct amd64_pvt *pvt = mci->pvt_info; |
84 | return sprintf(buf, "0x%x\n", pvt->injection.bit_map); | |
85 | } | |
86 | ||
eb919690 DT |
87 | /* |
88 | * store 16 bit error injection vector which enables injecting errors to the | |
89 | * corresponding bit within the error injection word above. When used during a | |
90 | * DRAM ECC read, it holds the contents of the of the DRAM ECC bits. | |
91 | */ | |
c5608759 MCC |
92 | static ssize_t amd64_inject_ecc_vector_store(struct device *dev, |
93 | struct device_attribute *mattr, | |
94 | const char *data, size_t count) | |
eb919690 | 95 | { |
c5608759 | 96 | struct mem_ctl_info *mci = to_mci(dev); |
eb919690 DT |
97 | struct amd64_pvt *pvt = mci->pvt_info; |
98 | unsigned long value; | |
6e71a870 | 99 | int ret; |
eb919690 | 100 | |
c7f62fc8 | 101 | ret = kstrtoul(data, 16, &value); |
6e71a870 BP |
102 | if (ret < 0) |
103 | return ret; | |
eb919690 | 104 | |
6e71a870 BP |
105 | if (value & 0xFFFF0000) { |
106 | amd64_warn("%s: invalid EccVector: 0x%lx\n", __func__, value); | |
107 | return -EINVAL; | |
eb919690 | 108 | } |
6e71a870 BP |
109 | |
110 | pvt->injection.bit_map = (u32) value; | |
111 | return count; | |
eb919690 DT |
112 | } |
113 | ||
114 | /* | |
115 | * Do a DRAM ECC read. Assemble staged values in the pvt area, format into | |
116 | * fields needed by the injection registers and read the NB Array Data Port. | |
117 | */ | |
c5608759 MCC |
118 | static ssize_t amd64_inject_read_store(struct device *dev, |
119 | struct device_attribute *mattr, | |
120 | const char *data, size_t count) | |
eb919690 | 121 | { |
c5608759 | 122 | struct mem_ctl_info *mci = to_mci(dev); |
eb919690 DT |
123 | struct amd64_pvt *pvt = mci->pvt_info; |
124 | unsigned long value; | |
125 | u32 section, word_bits; | |
6e71a870 | 126 | int ret; |
eb919690 | 127 | |
c7f62fc8 | 128 | ret = kstrtoul(data, 10, &value); |
6e71a870 BP |
129 | if (ret < 0) |
130 | return ret; | |
eb919690 | 131 | |
6e71a870 BP |
132 | /* Form value to choose 16-byte section of cacheline */ |
133 | section = F10_NB_ARRAY_DRAM | SET_NB_ARRAY_ADDR(pvt->injection.section); | |
eb919690 | 134 | |
6e71a870 | 135 | amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_ADDR, section); |
eb919690 | 136 | |
6e71a870 | 137 | word_bits = SET_NB_DRAM_INJECTION_READ(pvt->injection); |
eb919690 | 138 | |
6e71a870 BP |
139 | /* Issue 'word' and 'bit' along with the READ request */ |
140 | amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits); | |
eb919690 | 141 | |
6e71a870 BP |
142 | edac_dbg(0, "section=0x%x word_bits=0x%x\n", section, word_bits); |
143 | ||
144 | return count; | |
eb919690 DT |
145 | } |
146 | ||
147 | /* | |
148 | * Do a DRAM ECC write. Assemble staged values in the pvt area and format into | |
149 | * fields needed by the injection registers. | |
150 | */ | |
c5608759 MCC |
151 | static ssize_t amd64_inject_write_store(struct device *dev, |
152 | struct device_attribute *mattr, | |
eb919690 DT |
153 | const char *data, size_t count) |
154 | { | |
c5608759 | 155 | struct mem_ctl_info *mci = to_mci(dev); |
eb919690 | 156 | struct amd64_pvt *pvt = mci->pvt_info; |
66fed2d4 | 157 | u32 section, word_bits, tmp; |
eb919690 | 158 | unsigned long value; |
6e71a870 | 159 | int ret; |
eb919690 | 160 | |
c7f62fc8 | 161 | ret = kstrtoul(data, 10, &value); |
6e71a870 BP |
162 | if (ret < 0) |
163 | return ret; | |
eb919690 | 164 | |
6e71a870 BP |
165 | /* Form value to choose 16-byte section of cacheline */ |
166 | section = F10_NB_ARRAY_DRAM | SET_NB_ARRAY_ADDR(pvt->injection.section); | |
eb919690 | 167 | |
6e71a870 | 168 | amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_ADDR, section); |
eb919690 | 169 | |
6e71a870 | 170 | word_bits = SET_NB_DRAM_INJECTION_WRITE(pvt->injection); |
eb919690 | 171 | |
66fed2d4 BP |
172 | pr_notice_once("Don't forget to decrease MCE polling interval in\n" |
173 | "/sys/bus/machinecheck/devices/machinecheck<CPUNUM>/check_interval\n" | |
174 | "so that you can get the error report faster.\n"); | |
175 | ||
176 | on_each_cpu(disable_caches, NULL, 1); | |
177 | ||
6e71a870 BP |
178 | /* Issue 'word' and 'bit' along with the READ request */ |
179 | amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits); | |
eb919690 | 180 | |
66fed2d4 BP |
181 | retry: |
182 | /* wait until injection happens */ | |
183 | amd64_read_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, &tmp); | |
184 | if (tmp & F10_NB_ARR_ECC_WR_REQ) { | |
185 | cpu_relax(); | |
186 | goto retry; | |
187 | } | |
188 | ||
189 | on_each_cpu(enable_caches, NULL, 1); | |
190 | ||
6e71a870 BP |
191 | edac_dbg(0, "section=0x%x word_bits=0x%x\n", section, word_bits); |
192 | ||
193 | return count; | |
eb919690 DT |
194 | } |
195 | ||
196 | /* | |
197 | * update NUM_INJ_ATTRS in case you add new members | |
198 | */ | |
c5608759 MCC |
199 | |
200 | static DEVICE_ATTR(inject_section, S_IRUGO | S_IWUSR, | |
201 | amd64_inject_section_show, amd64_inject_section_store); | |
202 | static DEVICE_ATTR(inject_word, S_IRUGO | S_IWUSR, | |
203 | amd64_inject_word_show, amd64_inject_word_store); | |
204 | static DEVICE_ATTR(inject_ecc_vector, S_IRUGO | S_IWUSR, | |
205 | amd64_inject_ecc_vector_show, amd64_inject_ecc_vector_store); | |
bbb013b9 | 206 | static DEVICE_ATTR(inject_write, S_IWUSR, |
c5608759 | 207 | NULL, amd64_inject_write_store); |
bbb013b9 | 208 | static DEVICE_ATTR(inject_read, S_IWUSR, |
c5608759 MCC |
209 | NULL, amd64_inject_read_store); |
210 | ||
e339f1ec TI |
211 | static struct attribute *amd64_edac_inj_attrs[] = { |
212 | &dev_attr_inject_section.attr, | |
213 | &dev_attr_inject_word.attr, | |
214 | &dev_attr_inject_ecc_vector.attr, | |
215 | &dev_attr_inject_write.attr, | |
216 | &dev_attr_inject_read.attr, | |
217 | NULL | |
218 | }; | |
219 | ||
220 | static umode_t amd64_edac_inj_is_visible(struct kobject *kobj, | |
221 | struct attribute *attr, int idx) | |
c5608759 | 222 | { |
e339f1ec TI |
223 | struct device *dev = kobj_to_dev(kobj); |
224 | struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev); | |
225 | struct amd64_pvt *pvt = mci->pvt_info; | |
c5608759 | 226 | |
e339f1ec TI |
227 | if (pvt->fam < 0x10) |
228 | return 0; | |
229 | return attr->mode; | |
c5608759 | 230 | } |
e339f1ec TI |
231 | |
232 | const struct attribute_group amd64_edac_inj_group = { | |
233 | .attrs = amd64_edac_inj_attrs, | |
234 | .is_visible = amd64_edac_inj_is_visible, | |
235 | }; |