18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include "amd64_edac.h" 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_cistatic ssize_t amd64_inject_section_show(struct device *dev, 58c2ecf20Sopenharmony_ci struct device_attribute *mattr, 68c2ecf20Sopenharmony_ci char *buf) 78c2ecf20Sopenharmony_ci{ 88c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = to_mci(dev); 98c2ecf20Sopenharmony_ci struct amd64_pvt *pvt = mci->pvt_info; 108c2ecf20Sopenharmony_ci return sprintf(buf, "0x%x\n", pvt->injection.section); 118c2ecf20Sopenharmony_ci} 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/* 148c2ecf20Sopenharmony_ci * store error injection section value which refers to one of 4 16-byte sections 158c2ecf20Sopenharmony_ci * within a 64-byte cacheline 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * range: 0..3 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_cistatic ssize_t amd64_inject_section_store(struct device *dev, 208c2ecf20Sopenharmony_ci struct device_attribute *mattr, 218c2ecf20Sopenharmony_ci const char *data, size_t count) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = to_mci(dev); 248c2ecf20Sopenharmony_ci struct amd64_pvt *pvt = mci->pvt_info; 258c2ecf20Sopenharmony_ci unsigned long value; 268c2ecf20Sopenharmony_ci int ret; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci ret = kstrtoul(data, 10, &value); 298c2ecf20Sopenharmony_ci if (ret < 0) 308c2ecf20Sopenharmony_ci return ret; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci if (value > 3) { 338c2ecf20Sopenharmony_ci amd64_warn("%s: invalid section 0x%lx\n", __func__, value); 348c2ecf20Sopenharmony_ci return -EINVAL; 358c2ecf20Sopenharmony_ci } 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci pvt->injection.section = (u32) value; 388c2ecf20Sopenharmony_ci return count; 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic ssize_t amd64_inject_word_show(struct device *dev, 428c2ecf20Sopenharmony_ci struct device_attribute *mattr, 438c2ecf20Sopenharmony_ci char *buf) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = to_mci(dev); 468c2ecf20Sopenharmony_ci struct amd64_pvt *pvt = mci->pvt_info; 478c2ecf20Sopenharmony_ci return sprintf(buf, "0x%x\n", pvt->injection.word); 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* 518c2ecf20Sopenharmony_ci * store error injection word value which refers to one of 9 16-bit word of the 528c2ecf20Sopenharmony_ci * 16-byte (128-bit + ECC bits) section 538c2ecf20Sopenharmony_ci * 548c2ecf20Sopenharmony_ci * range: 0..8 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_cistatic ssize_t amd64_inject_word_store(struct device *dev, 578c2ecf20Sopenharmony_ci struct device_attribute *mattr, 588c2ecf20Sopenharmony_ci const char *data, size_t count) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = to_mci(dev); 618c2ecf20Sopenharmony_ci struct amd64_pvt *pvt = mci->pvt_info; 628c2ecf20Sopenharmony_ci unsigned long value; 638c2ecf20Sopenharmony_ci int ret; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci ret = kstrtoul(data, 10, &value); 668c2ecf20Sopenharmony_ci if (ret < 0) 678c2ecf20Sopenharmony_ci return ret; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (value > 8) { 708c2ecf20Sopenharmony_ci amd64_warn("%s: invalid word 0x%lx\n", __func__, value); 718c2ecf20Sopenharmony_ci return -EINVAL; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci pvt->injection.word = (u32) value; 758c2ecf20Sopenharmony_ci return count; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic ssize_t amd64_inject_ecc_vector_show(struct device *dev, 798c2ecf20Sopenharmony_ci struct device_attribute *mattr, 808c2ecf20Sopenharmony_ci char *buf) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = to_mci(dev); 838c2ecf20Sopenharmony_ci struct amd64_pvt *pvt = mci->pvt_info; 848c2ecf20Sopenharmony_ci return sprintf(buf, "0x%x\n", pvt->injection.bit_map); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* 888c2ecf20Sopenharmony_ci * store 16 bit error injection vector which enables injecting errors to the 898c2ecf20Sopenharmony_ci * corresponding bit within the error injection word above. When used during a 908c2ecf20Sopenharmony_ci * DRAM ECC read, it holds the contents of the of the DRAM ECC bits. 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_cistatic ssize_t amd64_inject_ecc_vector_store(struct device *dev, 938c2ecf20Sopenharmony_ci struct device_attribute *mattr, 948c2ecf20Sopenharmony_ci const char *data, size_t count) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = to_mci(dev); 978c2ecf20Sopenharmony_ci struct amd64_pvt *pvt = mci->pvt_info; 988c2ecf20Sopenharmony_ci unsigned long value; 998c2ecf20Sopenharmony_ci int ret; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci ret = kstrtoul(data, 16, &value); 1028c2ecf20Sopenharmony_ci if (ret < 0) 1038c2ecf20Sopenharmony_ci return ret; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (value & 0xFFFF0000) { 1068c2ecf20Sopenharmony_ci amd64_warn("%s: invalid EccVector: 0x%lx\n", __func__, value); 1078c2ecf20Sopenharmony_ci return -EINVAL; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci pvt->injection.bit_map = (u32) value; 1118c2ecf20Sopenharmony_ci return count; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/* 1158c2ecf20Sopenharmony_ci * Do a DRAM ECC read. Assemble staged values in the pvt area, format into 1168c2ecf20Sopenharmony_ci * fields needed by the injection registers and read the NB Array Data Port. 1178c2ecf20Sopenharmony_ci */ 1188c2ecf20Sopenharmony_cistatic ssize_t amd64_inject_read_store(struct device *dev, 1198c2ecf20Sopenharmony_ci struct device_attribute *mattr, 1208c2ecf20Sopenharmony_ci const char *data, size_t count) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = to_mci(dev); 1238c2ecf20Sopenharmony_ci struct amd64_pvt *pvt = mci->pvt_info; 1248c2ecf20Sopenharmony_ci unsigned long value; 1258c2ecf20Sopenharmony_ci u32 section, word_bits; 1268c2ecf20Sopenharmony_ci int ret; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci ret = kstrtoul(data, 10, &value); 1298c2ecf20Sopenharmony_ci if (ret < 0) 1308c2ecf20Sopenharmony_ci return ret; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* Form value to choose 16-byte section of cacheline */ 1338c2ecf20Sopenharmony_ci section = F10_NB_ARRAY_DRAM | SET_NB_ARRAY_ADDR(pvt->injection.section); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_ADDR, section); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci word_bits = SET_NB_DRAM_INJECTION_READ(pvt->injection); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* Issue 'word' and 'bit' along with the READ request */ 1408c2ecf20Sopenharmony_ci amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci edac_dbg(0, "section=0x%x word_bits=0x%x\n", section, word_bits); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return count; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/* 1488c2ecf20Sopenharmony_ci * Do a DRAM ECC write. Assemble staged values in the pvt area and format into 1498c2ecf20Sopenharmony_ci * fields needed by the injection registers. 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_cistatic ssize_t amd64_inject_write_store(struct device *dev, 1528c2ecf20Sopenharmony_ci struct device_attribute *mattr, 1538c2ecf20Sopenharmony_ci const char *data, size_t count) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = to_mci(dev); 1568c2ecf20Sopenharmony_ci struct amd64_pvt *pvt = mci->pvt_info; 1578c2ecf20Sopenharmony_ci u32 section, word_bits, tmp; 1588c2ecf20Sopenharmony_ci unsigned long value; 1598c2ecf20Sopenharmony_ci int ret; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci ret = kstrtoul(data, 10, &value); 1628c2ecf20Sopenharmony_ci if (ret < 0) 1638c2ecf20Sopenharmony_ci return ret; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* Form value to choose 16-byte section of cacheline */ 1668c2ecf20Sopenharmony_ci section = F10_NB_ARRAY_DRAM | SET_NB_ARRAY_ADDR(pvt->injection.section); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_ADDR, section); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci word_bits = SET_NB_DRAM_INJECTION_WRITE(pvt->injection); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci pr_notice_once("Don't forget to decrease MCE polling interval in\n" 1738c2ecf20Sopenharmony_ci "/sys/bus/machinecheck/devices/machinecheck<CPUNUM>/check_interval\n" 1748c2ecf20Sopenharmony_ci "so that you can get the error report faster.\n"); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci on_each_cpu(disable_caches, NULL, 1); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* Issue 'word' and 'bit' along with the READ request */ 1798c2ecf20Sopenharmony_ci amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci retry: 1828c2ecf20Sopenharmony_ci /* wait until injection happens */ 1838c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, &tmp); 1848c2ecf20Sopenharmony_ci if (tmp & F10_NB_ARR_ECC_WR_REQ) { 1858c2ecf20Sopenharmony_ci cpu_relax(); 1868c2ecf20Sopenharmony_ci goto retry; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci on_each_cpu(enable_caches, NULL, 1); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci edac_dbg(0, "section=0x%x word_bits=0x%x\n", section, word_bits); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return count; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/* 1978c2ecf20Sopenharmony_ci * update NUM_INJ_ATTRS in case you add new members 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic DEVICE_ATTR(inject_section, S_IRUGO | S_IWUSR, 2018c2ecf20Sopenharmony_ci amd64_inject_section_show, amd64_inject_section_store); 2028c2ecf20Sopenharmony_cistatic DEVICE_ATTR(inject_word, S_IRUGO | S_IWUSR, 2038c2ecf20Sopenharmony_ci amd64_inject_word_show, amd64_inject_word_store); 2048c2ecf20Sopenharmony_cistatic DEVICE_ATTR(inject_ecc_vector, S_IRUGO | S_IWUSR, 2058c2ecf20Sopenharmony_ci amd64_inject_ecc_vector_show, amd64_inject_ecc_vector_store); 2068c2ecf20Sopenharmony_cistatic DEVICE_ATTR(inject_write, S_IWUSR, 2078c2ecf20Sopenharmony_ci NULL, amd64_inject_write_store); 2088c2ecf20Sopenharmony_cistatic DEVICE_ATTR(inject_read, S_IWUSR, 2098c2ecf20Sopenharmony_ci NULL, amd64_inject_read_store); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic struct attribute *amd64_edac_inj_attrs[] = { 2128c2ecf20Sopenharmony_ci &dev_attr_inject_section.attr, 2138c2ecf20Sopenharmony_ci &dev_attr_inject_word.attr, 2148c2ecf20Sopenharmony_ci &dev_attr_inject_ecc_vector.attr, 2158c2ecf20Sopenharmony_ci &dev_attr_inject_write.attr, 2168c2ecf20Sopenharmony_ci &dev_attr_inject_read.attr, 2178c2ecf20Sopenharmony_ci NULL 2188c2ecf20Sopenharmony_ci}; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic umode_t amd64_edac_inj_is_visible(struct kobject *kobj, 2218c2ecf20Sopenharmony_ci struct attribute *attr, int idx) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 2248c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev); 2258c2ecf20Sopenharmony_ci struct amd64_pvt *pvt = mci->pvt_info; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (pvt->fam < 0x10) 2288c2ecf20Sopenharmony_ci return 0; 2298c2ecf20Sopenharmony_ci return attr->mode; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ciconst struct attribute_group amd64_edac_inj_group = { 2338c2ecf20Sopenharmony_ci .attrs = amd64_edac_inj_attrs, 2348c2ecf20Sopenharmony_ci .is_visible = amd64_edac_inj_is_visible, 2358c2ecf20Sopenharmony_ci}; 236