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