amd64_edac_inj.c 6.4 KB
Newer Older
1 2
#include "amd64_edac.h"

3 4 5
static ssize_t amd64_inject_section_show(struct device *dev,
					 struct device_attribute *mattr,
					 char *buf)
6
{
7
	struct mem_ctl_info *mci = to_mci(dev);
8 9 10 11
	struct amd64_pvt *pvt = mci->pvt_info;
	return sprintf(buf, "0x%x\n", pvt->injection.section);
}

12 13 14 15 16 17
/*
 * store error injection section value which refers to one of 4 16-byte sections
 * within a 64-byte cacheline
 *
 * range: 0..3
 */
18 19
static ssize_t amd64_inject_section_store(struct device *dev,
					  struct device_attribute *mattr,
20 21
					  const char *data, size_t count)
{
22
	struct mem_ctl_info *mci = to_mci(dev);
23 24
	struct amd64_pvt *pvt = mci->pvt_info;
	unsigned long value;
25
	int ret;
26 27

	ret = strict_strtoul(data, 10, &value);
28 29
	if (ret < 0)
		return ret;
30

31 32 33
	if (value > 3) {
		amd64_warn("%s: invalid section 0x%lx\n", __func__, value);
		return -EINVAL;
34
	}
35 36 37

	pvt->injection.section = (u32) value;
	return count;
38 39
}

40 41 42
static ssize_t amd64_inject_word_show(struct device *dev,
					struct device_attribute *mattr,
					char *buf)
43
{
44
	struct mem_ctl_info *mci = to_mci(dev);
45 46 47 48
	struct amd64_pvt *pvt = mci->pvt_info;
	return sprintf(buf, "0x%x\n", pvt->injection.word);
}

49 50 51 52 53 54
/*
 * store error injection word value which refers to one of 9 16-bit word of the
 * 16-byte (128-bit + ECC bits) section
 *
 * range: 0..8
 */
55 56 57
static ssize_t amd64_inject_word_store(struct device *dev,
				       struct device_attribute *mattr,
				       const char *data, size_t count)
58
{
59
	struct mem_ctl_info *mci = to_mci(dev);
60 61
	struct amd64_pvt *pvt = mci->pvt_info;
	unsigned long value;
62
	int ret;
63 64

	ret = strict_strtoul(data, 10, &value);
65 66
	if (ret < 0)
		return ret;
67

68 69 70
	if (value > 8) {
		amd64_warn("%s: invalid word 0x%lx\n", __func__, value);
		return -EINVAL;
71
	}
72 73 74

	pvt->injection.word = (u32) value;
	return count;
75 76
}

77 78 79
static ssize_t amd64_inject_ecc_vector_show(struct device *dev,
					    struct device_attribute *mattr,
					    char *buf)
80
{
81
	struct mem_ctl_info *mci = to_mci(dev);
82 83 84 85
	struct amd64_pvt *pvt = mci->pvt_info;
	return sprintf(buf, "0x%x\n", pvt->injection.bit_map);
}

86 87 88 89 90
/*
 * store 16 bit error injection vector which enables injecting errors to the
 * corresponding bit within the error injection word above. When used during a
 * DRAM ECC read, it holds the contents of the of the DRAM ECC bits.
 */
91 92 93
static ssize_t amd64_inject_ecc_vector_store(struct device *dev,
				       struct device_attribute *mattr,
				       const char *data, size_t count)
94
{
95
	struct mem_ctl_info *mci = to_mci(dev);
96 97
	struct amd64_pvt *pvt = mci->pvt_info;
	unsigned long value;
98
	int ret;
99 100

	ret = strict_strtoul(data, 16, &value);
101 102
	if (ret < 0)
		return ret;
103

104 105 106
	if (value & 0xFFFF0000) {
		amd64_warn("%s: invalid EccVector: 0x%lx\n", __func__, value);
		return -EINVAL;
107
	}
108 109 110

	pvt->injection.bit_map = (u32) value;
	return count;
111 112 113 114 115 116
}

/*
 * Do a DRAM ECC read. Assemble staged values in the pvt area, format into
 * fields needed by the injection registers and read the NB Array Data Port.
 */
117 118 119
static ssize_t amd64_inject_read_store(struct device *dev,
				       struct device_attribute *mattr,
				       const char *data, size_t count)
120
{
121
	struct mem_ctl_info *mci = to_mci(dev);
122 123 124
	struct amd64_pvt *pvt = mci->pvt_info;
	unsigned long value;
	u32 section, word_bits;
125
	int ret;
126 127

	ret = strict_strtoul(data, 10, &value);
128 129
	if (ret < 0)
		return ret;
130

131 132
	/* Form value to choose 16-byte section of cacheline */
	section = F10_NB_ARRAY_DRAM | SET_NB_ARRAY_ADDR(pvt->injection.section);
133

134
	amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_ADDR, section);
135

136
	word_bits = SET_NB_DRAM_INJECTION_READ(pvt->injection);
137

138 139
	/* Issue 'word' and 'bit' along with the READ request */
	amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits);
140

141 142 143
	edac_dbg(0, "section=0x%x word_bits=0x%x\n", section, word_bits);

	return count;
144 145 146 147 148 149
}

/*
 * Do a DRAM ECC write. Assemble staged values in the pvt area and format into
 * fields needed by the injection registers.
 */
150 151
static ssize_t amd64_inject_write_store(struct device *dev,
					struct device_attribute *mattr,
152 153
					const char *data, size_t count)
{
154
	struct mem_ctl_info *mci = to_mci(dev);
155
	struct amd64_pvt *pvt = mci->pvt_info;
156
	u32 section, word_bits, tmp;
157
	unsigned long value;
158
	int ret;
159 160

	ret = strict_strtoul(data, 10, &value);
161 162
	if (ret < 0)
		return ret;
163

164 165
	/* Form value to choose 16-byte section of cacheline */
	section = F10_NB_ARRAY_DRAM | SET_NB_ARRAY_ADDR(pvt->injection.section);
166

167
	amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_ADDR, section);
168

169
	word_bits = SET_NB_DRAM_INJECTION_WRITE(pvt->injection);
170

171 172 173 174 175 176
	pr_notice_once("Don't forget to decrease MCE polling interval in\n"
			"/sys/bus/machinecheck/devices/machinecheck<CPUNUM>/check_interval\n"
			"so that you can get the error report faster.\n");

	on_each_cpu(disable_caches, NULL, 1);

177 178
	/* Issue 'word' and 'bit' along with the READ request */
	amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits);
179

180 181 182 183 184 185 186 187 188 189
 retry:
	/* wait until injection happens */
	amd64_read_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, &tmp);
	if (tmp & F10_NB_ARR_ECC_WR_REQ) {
		cpu_relax();
		goto retry;
	}

	on_each_cpu(enable_caches, NULL, 1);

190 191 192
	edac_dbg(0, "section=0x%x word_bits=0x%x\n", section, word_bits);

	return count;
193 194 195 196 197
}

/*
 * update NUM_INJ_ATTRS in case you add new members
 */
198 199 200 201 202 203 204

static DEVICE_ATTR(inject_section, S_IRUGO | S_IWUSR,
		   amd64_inject_section_show, amd64_inject_section_store);
static DEVICE_ATTR(inject_word, S_IRUGO | S_IWUSR,
		   amd64_inject_word_show, amd64_inject_word_store);
static DEVICE_ATTR(inject_ecc_vector, S_IRUGO | S_IWUSR,
		   amd64_inject_ecc_vector_show, amd64_inject_ecc_vector_store);
205
static DEVICE_ATTR(inject_write, S_IWUSR,
206
		   NULL, amd64_inject_write_store);
207
static DEVICE_ATTR(inject_read,  S_IWUSR,
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
		   NULL, amd64_inject_read_store);


int amd64_create_sysfs_inject_files(struct mem_ctl_info *mci)
{
	int rc;

	rc = device_create_file(&mci->dev, &dev_attr_inject_section);
	if (rc < 0)
		return rc;
	rc = device_create_file(&mci->dev, &dev_attr_inject_word);
	if (rc < 0)
		return rc;
	rc = device_create_file(&mci->dev, &dev_attr_inject_ecc_vector);
	if (rc < 0)
		return rc;
	rc = device_create_file(&mci->dev, &dev_attr_inject_write);
	if (rc < 0)
		return rc;
	rc = device_create_file(&mci->dev, &dev_attr_inject_read);
	if (rc < 0)
		return rc;

	return 0;
}

void amd64_remove_sysfs_inject_files(struct mem_ctl_info *mci)
{
	device_remove_file(&mci->dev, &dev_attr_inject_section);
	device_remove_file(&mci->dev, &dev_attr_inject_word);
	device_remove_file(&mci->dev, &dev_attr_inject_ecc_vector);
	device_remove_file(&mci->dev, &dev_attr_inject_write);
	device_remove_file(&mci->dev, &dev_attr_inject_read);
}