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