xref: /linux/arch/x86/kernel/cpu/amd_cache_disable.c (revision 785cdec46e9227f9433884ed3b436471e944007c)
1365e960dSAhmed S. Darwish // SPDX-License-Identifier: GPL-2.0
2365e960dSAhmed S. Darwish /*
3365e960dSAhmed S. Darwish  * AMD L3 cache_disable_{0,1} sysfs handling
4365e960dSAhmed S. Darwish  * Documentation/ABI/testing/sysfs-devices-system-cpu
5365e960dSAhmed S. Darwish  */
6365e960dSAhmed S. Darwish 
7365e960dSAhmed S. Darwish #include <linux/cacheinfo.h>
8365e960dSAhmed S. Darwish #include <linux/capability.h>
9365e960dSAhmed S. Darwish #include <linux/pci.h>
10365e960dSAhmed S. Darwish #include <linux/sysfs.h>
11365e960dSAhmed S. Darwish 
12*bcbb6555SIngo Molnar #include <asm/amd/nb.h>
13365e960dSAhmed S. Darwish 
14365e960dSAhmed S. Darwish #include "cpu.h"
15365e960dSAhmed S. Darwish 
16365e960dSAhmed S. Darwish /*
17365e960dSAhmed S. Darwish  * L3 cache descriptors
18365e960dSAhmed S. Darwish  */
19365e960dSAhmed S. Darwish static void amd_calc_l3_indices(struct amd_northbridge *nb)
20365e960dSAhmed S. Darwish {
21365e960dSAhmed S. Darwish 	struct amd_l3_cache *l3 = &nb->l3_cache;
22365e960dSAhmed S. Darwish 	unsigned int sc0, sc1, sc2, sc3;
23365e960dSAhmed S. Darwish 	u32 val = 0;
24365e960dSAhmed S. Darwish 
25365e960dSAhmed S. Darwish 	pci_read_config_dword(nb->misc, 0x1C4, &val);
26365e960dSAhmed S. Darwish 
27365e960dSAhmed S. Darwish 	/* calculate subcache sizes */
28365e960dSAhmed S. Darwish 	l3->subcaches[0] = sc0 = !(val & BIT(0));
29365e960dSAhmed S. Darwish 	l3->subcaches[1] = sc1 = !(val & BIT(4));
30365e960dSAhmed S. Darwish 
31365e960dSAhmed S. Darwish 	if (boot_cpu_data.x86 == 0x15) {
32365e960dSAhmed S. Darwish 		l3->subcaches[0] = sc0 += !(val & BIT(1));
33365e960dSAhmed S. Darwish 		l3->subcaches[1] = sc1 += !(val & BIT(5));
34365e960dSAhmed S. Darwish 	}
35365e960dSAhmed S. Darwish 
36365e960dSAhmed S. Darwish 	l3->subcaches[2] = sc2 = !(val & BIT(8))  + !(val & BIT(9));
37365e960dSAhmed S. Darwish 	l3->subcaches[3] = sc3 = !(val & BIT(12)) + !(val & BIT(13));
38365e960dSAhmed S. Darwish 
39365e960dSAhmed S. Darwish 	l3->indices = (max(max3(sc0, sc1, sc2), sc3) << 10) - 1;
40365e960dSAhmed S. Darwish }
41365e960dSAhmed S. Darwish 
42365e960dSAhmed S. Darwish /*
43365e960dSAhmed S. Darwish  * check whether a slot used for disabling an L3 index is occupied.
44365e960dSAhmed S. Darwish  * @l3: L3 cache descriptor
45365e960dSAhmed S. Darwish  * @slot: slot number (0..1)
46365e960dSAhmed S. Darwish  *
47365e960dSAhmed S. Darwish  * @returns: the disabled index if used or negative value if slot free.
48365e960dSAhmed S. Darwish  */
49365e960dSAhmed S. Darwish static int amd_get_l3_disable_slot(struct amd_northbridge *nb, unsigned int slot)
50365e960dSAhmed S. Darwish {
51365e960dSAhmed S. Darwish 	unsigned int reg = 0;
52365e960dSAhmed S. Darwish 
53365e960dSAhmed S. Darwish 	pci_read_config_dword(nb->misc, 0x1BC + slot * 4, &reg);
54365e960dSAhmed S. Darwish 
55365e960dSAhmed S. Darwish 	/* check whether this slot is activated already */
56365e960dSAhmed S. Darwish 	if (reg & (3UL << 30))
57365e960dSAhmed S. Darwish 		return reg & 0xfff;
58365e960dSAhmed S. Darwish 
59365e960dSAhmed S. Darwish 	return -1;
60365e960dSAhmed S. Darwish }
61365e960dSAhmed S. Darwish 
62365e960dSAhmed S. Darwish static ssize_t show_cache_disable(struct cacheinfo *ci, char *buf, unsigned int slot)
63365e960dSAhmed S. Darwish {
64365e960dSAhmed S. Darwish 	int index;
65365e960dSAhmed S. Darwish 	struct amd_northbridge *nb = ci->priv;
66365e960dSAhmed S. Darwish 
67365e960dSAhmed S. Darwish 	index = amd_get_l3_disable_slot(nb, slot);
68365e960dSAhmed S. Darwish 	if (index >= 0)
69071f4ad6SAhmed S. Darwish 		return sysfs_emit(buf, "%d\n", index);
70365e960dSAhmed S. Darwish 
71071f4ad6SAhmed S. Darwish 	return sysfs_emit(buf, "FREE\n");
72365e960dSAhmed S. Darwish }
73365e960dSAhmed S. Darwish 
74365e960dSAhmed S. Darwish #define SHOW_CACHE_DISABLE(slot)					\
75365e960dSAhmed S. Darwish static ssize_t								\
76365e960dSAhmed S. Darwish cache_disable_##slot##_show(struct device *dev,				\
77365e960dSAhmed S. Darwish 			    struct device_attribute *attr, char *buf)	\
78365e960dSAhmed S. Darwish {									\
79365e960dSAhmed S. Darwish 	struct cacheinfo *ci = dev_get_drvdata(dev);			\
80365e960dSAhmed S. Darwish 	return show_cache_disable(ci, buf, slot);			\
81365e960dSAhmed S. Darwish }
82365e960dSAhmed S. Darwish 
83365e960dSAhmed S. Darwish SHOW_CACHE_DISABLE(0)
84365e960dSAhmed S. Darwish SHOW_CACHE_DISABLE(1)
85365e960dSAhmed S. Darwish 
86365e960dSAhmed S. Darwish static void amd_l3_disable_index(struct amd_northbridge *nb, int cpu,
87365e960dSAhmed S. Darwish 				 unsigned int slot, unsigned long idx)
88365e960dSAhmed S. Darwish {
89365e960dSAhmed S. Darwish 	int i;
90365e960dSAhmed S. Darwish 
91365e960dSAhmed S. Darwish 	idx |= BIT(30);
92365e960dSAhmed S. Darwish 
93365e960dSAhmed S. Darwish 	/*
94365e960dSAhmed S. Darwish 	 *  disable index in all 4 subcaches
95365e960dSAhmed S. Darwish 	 */
96365e960dSAhmed S. Darwish 	for (i = 0; i < 4; i++) {
97365e960dSAhmed S. Darwish 		u32 reg = idx | (i << 20);
98365e960dSAhmed S. Darwish 
99365e960dSAhmed S. Darwish 		if (!nb->l3_cache.subcaches[i])
100365e960dSAhmed S. Darwish 			continue;
101365e960dSAhmed S. Darwish 
102365e960dSAhmed S. Darwish 		pci_write_config_dword(nb->misc, 0x1BC + slot * 4, reg);
103365e960dSAhmed S. Darwish 
104365e960dSAhmed S. Darwish 		/*
105365e960dSAhmed S. Darwish 		 * We need to WBINVD on a core on the node containing the L3
106365e960dSAhmed S. Darwish 		 * cache which indices we disable therefore a simple wbinvd()
107365e960dSAhmed S. Darwish 		 * is not sufficient.
108365e960dSAhmed S. Darwish 		 */
109365e960dSAhmed S. Darwish 		wbinvd_on_cpu(cpu);
110365e960dSAhmed S. Darwish 
111365e960dSAhmed S. Darwish 		reg |= BIT(31);
112365e960dSAhmed S. Darwish 		pci_write_config_dword(nb->misc, 0x1BC + slot * 4, reg);
113365e960dSAhmed S. Darwish 	}
114365e960dSAhmed S. Darwish }
115365e960dSAhmed S. Darwish 
116365e960dSAhmed S. Darwish /*
117365e960dSAhmed S. Darwish  * disable a L3 cache index by using a disable-slot
118365e960dSAhmed S. Darwish  *
119365e960dSAhmed S. Darwish  * @l3:    L3 cache descriptor
120365e960dSAhmed S. Darwish  * @cpu:   A CPU on the node containing the L3 cache
121365e960dSAhmed S. Darwish  * @slot:  slot number (0..1)
122365e960dSAhmed S. Darwish  * @index: index to disable
123365e960dSAhmed S. Darwish  *
124365e960dSAhmed S. Darwish  * @return: 0 on success, error status on failure
125365e960dSAhmed S. Darwish  */
126365e960dSAhmed S. Darwish static int amd_set_l3_disable_slot(struct amd_northbridge *nb, int cpu,
127365e960dSAhmed S. Darwish 				   unsigned int slot, unsigned long index)
128365e960dSAhmed S. Darwish {
129365e960dSAhmed S. Darwish 	int ret = 0;
130365e960dSAhmed S. Darwish 
131365e960dSAhmed S. Darwish 	/*  check if @slot is already used or the index is already disabled */
132365e960dSAhmed S. Darwish 	ret = amd_get_l3_disable_slot(nb, slot);
133365e960dSAhmed S. Darwish 	if (ret >= 0)
134365e960dSAhmed S. Darwish 		return -EEXIST;
135365e960dSAhmed S. Darwish 
136365e960dSAhmed S. Darwish 	if (index > nb->l3_cache.indices)
137365e960dSAhmed S. Darwish 		return -EINVAL;
138365e960dSAhmed S. Darwish 
139365e960dSAhmed S. Darwish 	/* check whether the other slot has disabled the same index already */
140365e960dSAhmed S. Darwish 	if (index == amd_get_l3_disable_slot(nb, !slot))
141365e960dSAhmed S. Darwish 		return -EEXIST;
142365e960dSAhmed S. Darwish 
143365e960dSAhmed S. Darwish 	amd_l3_disable_index(nb, cpu, slot, index);
144365e960dSAhmed S. Darwish 
145365e960dSAhmed S. Darwish 	return 0;
146365e960dSAhmed S. Darwish }
147365e960dSAhmed S. Darwish 
148365e960dSAhmed S. Darwish static ssize_t store_cache_disable(struct cacheinfo *ci, const char *buf,
149365e960dSAhmed S. Darwish 				   size_t count, unsigned int slot)
150365e960dSAhmed S. Darwish {
151365e960dSAhmed S. Darwish 	struct amd_northbridge *nb = ci->priv;
152365e960dSAhmed S. Darwish 	unsigned long val = 0;
153365e960dSAhmed S. Darwish 	int cpu, err = 0;
154365e960dSAhmed S. Darwish 
155365e960dSAhmed S. Darwish 	if (!capable(CAP_SYS_ADMIN))
156365e960dSAhmed S. Darwish 		return -EPERM;
157365e960dSAhmed S. Darwish 
158365e960dSAhmed S. Darwish 	cpu = cpumask_first(&ci->shared_cpu_map);
159365e960dSAhmed S. Darwish 
160365e960dSAhmed S. Darwish 	if (kstrtoul(buf, 10, &val) < 0)
161365e960dSAhmed S. Darwish 		return -EINVAL;
162365e960dSAhmed S. Darwish 
163365e960dSAhmed S. Darwish 	err = amd_set_l3_disable_slot(nb, cpu, slot, val);
164365e960dSAhmed S. Darwish 	if (err) {
165365e960dSAhmed S. Darwish 		if (err == -EEXIST)
166365e960dSAhmed S. Darwish 			pr_warn("L3 slot %d in use/index already disabled!\n",
167365e960dSAhmed S. Darwish 				   slot);
168365e960dSAhmed S. Darwish 		return err;
169365e960dSAhmed S. Darwish 	}
170365e960dSAhmed S. Darwish 	return count;
171365e960dSAhmed S. Darwish }
172365e960dSAhmed S. Darwish 
173365e960dSAhmed S. Darwish #define STORE_CACHE_DISABLE(slot)					\
174365e960dSAhmed S. Darwish static ssize_t								\
175365e960dSAhmed S. Darwish cache_disable_##slot##_store(struct device *dev,			\
176365e960dSAhmed S. Darwish 			     struct device_attribute *attr,		\
177365e960dSAhmed S. Darwish 			     const char *buf, size_t count)		\
178365e960dSAhmed S. Darwish {									\
179365e960dSAhmed S. Darwish 	struct cacheinfo *ci = dev_get_drvdata(dev);			\
180365e960dSAhmed S. Darwish 	return store_cache_disable(ci, buf, count, slot);		\
181365e960dSAhmed S. Darwish }
182365e960dSAhmed S. Darwish 
183365e960dSAhmed S. Darwish STORE_CACHE_DISABLE(0)
184365e960dSAhmed S. Darwish STORE_CACHE_DISABLE(1)
185365e960dSAhmed S. Darwish 
186365e960dSAhmed S. Darwish static ssize_t subcaches_show(struct device *dev, struct device_attribute *attr,
187365e960dSAhmed S. Darwish 			      char *buf)
188365e960dSAhmed S. Darwish {
189365e960dSAhmed S. Darwish 	struct cacheinfo *ci = dev_get_drvdata(dev);
190365e960dSAhmed S. Darwish 	int cpu = cpumask_first(&ci->shared_cpu_map);
191365e960dSAhmed S. Darwish 
192071f4ad6SAhmed S. Darwish 	return sysfs_emit(buf, "%x\n", amd_get_subcaches(cpu));
193365e960dSAhmed S. Darwish }
194365e960dSAhmed S. Darwish 
195365e960dSAhmed S. Darwish static ssize_t subcaches_store(struct device *dev,
196365e960dSAhmed S. Darwish 			       struct device_attribute *attr,
197365e960dSAhmed S. Darwish 			       const char *buf, size_t count)
198365e960dSAhmed S. Darwish {
199365e960dSAhmed S. Darwish 	struct cacheinfo *ci = dev_get_drvdata(dev);
200365e960dSAhmed S. Darwish 	int cpu = cpumask_first(&ci->shared_cpu_map);
201365e960dSAhmed S. Darwish 	unsigned long val;
202365e960dSAhmed S. Darwish 
203365e960dSAhmed S. Darwish 	if (!capable(CAP_SYS_ADMIN))
204365e960dSAhmed S. Darwish 		return -EPERM;
205365e960dSAhmed S. Darwish 
206365e960dSAhmed S. Darwish 	if (kstrtoul(buf, 16, &val) < 0)
207365e960dSAhmed S. Darwish 		return -EINVAL;
208365e960dSAhmed S. Darwish 
209365e960dSAhmed S. Darwish 	if (amd_set_subcaches(cpu, val))
210365e960dSAhmed S. Darwish 		return -EINVAL;
211365e960dSAhmed S. Darwish 
212365e960dSAhmed S. Darwish 	return count;
213365e960dSAhmed S. Darwish }
214365e960dSAhmed S. Darwish 
215365e960dSAhmed S. Darwish static DEVICE_ATTR_RW(cache_disable_0);
216365e960dSAhmed S. Darwish static DEVICE_ATTR_RW(cache_disable_1);
217365e960dSAhmed S. Darwish static DEVICE_ATTR_RW(subcaches);
218365e960dSAhmed S. Darwish 
219365e960dSAhmed S. Darwish static umode_t cache_private_attrs_is_visible(struct kobject *kobj,
220365e960dSAhmed S. Darwish 					      struct attribute *attr, int unused)
221365e960dSAhmed S. Darwish {
222365e960dSAhmed S. Darwish 	struct device *dev = kobj_to_dev(kobj);
223365e960dSAhmed S. Darwish 	struct cacheinfo *ci = dev_get_drvdata(dev);
224365e960dSAhmed S. Darwish 	umode_t mode = attr->mode;
225365e960dSAhmed S. Darwish 
226365e960dSAhmed S. Darwish 	if (!ci->priv)
227365e960dSAhmed S. Darwish 		return 0;
228365e960dSAhmed S. Darwish 
229365e960dSAhmed S. Darwish 	if ((attr == &dev_attr_subcaches.attr) &&
230365e960dSAhmed S. Darwish 	    amd_nb_has_feature(AMD_NB_L3_PARTITIONING))
231365e960dSAhmed S. Darwish 		return mode;
232365e960dSAhmed S. Darwish 
233365e960dSAhmed S. Darwish 	if ((attr == &dev_attr_cache_disable_0.attr ||
234365e960dSAhmed S. Darwish 	     attr == &dev_attr_cache_disable_1.attr) &&
235365e960dSAhmed S. Darwish 	    amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE))
236365e960dSAhmed S. Darwish 		return mode;
237365e960dSAhmed S. Darwish 
238365e960dSAhmed S. Darwish 	return 0;
239365e960dSAhmed S. Darwish }
240365e960dSAhmed S. Darwish 
241365e960dSAhmed S. Darwish static struct attribute_group cache_private_group = {
242365e960dSAhmed S. Darwish 	.is_visible = cache_private_attrs_is_visible,
243365e960dSAhmed S. Darwish };
244365e960dSAhmed S. Darwish 
245365e960dSAhmed S. Darwish static void init_amd_l3_attrs(void)
246365e960dSAhmed S. Darwish {
247365e960dSAhmed S. Darwish 	static struct attribute **amd_l3_attrs;
248365e960dSAhmed S. Darwish 	int n = 1;
249365e960dSAhmed S. Darwish 
250365e960dSAhmed S. Darwish 	if (amd_l3_attrs) /* already initialized */
251365e960dSAhmed S. Darwish 		return;
252365e960dSAhmed S. Darwish 
253365e960dSAhmed S. Darwish 	if (amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE))
254365e960dSAhmed S. Darwish 		n += 2;
255365e960dSAhmed S. Darwish 	if (amd_nb_has_feature(AMD_NB_L3_PARTITIONING))
256365e960dSAhmed S. Darwish 		n += 1;
257365e960dSAhmed S. Darwish 
258365e960dSAhmed S. Darwish 	amd_l3_attrs = kcalloc(n, sizeof(*amd_l3_attrs), GFP_KERNEL);
259365e960dSAhmed S. Darwish 	if (!amd_l3_attrs)
260365e960dSAhmed S. Darwish 		return;
261365e960dSAhmed S. Darwish 
262365e960dSAhmed S. Darwish 	n = 0;
263365e960dSAhmed S. Darwish 	if (amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE)) {
264365e960dSAhmed S. Darwish 		amd_l3_attrs[n++] = &dev_attr_cache_disable_0.attr;
265365e960dSAhmed S. Darwish 		amd_l3_attrs[n++] = &dev_attr_cache_disable_1.attr;
266365e960dSAhmed S. Darwish 	}
267365e960dSAhmed S. Darwish 	if (amd_nb_has_feature(AMD_NB_L3_PARTITIONING))
268365e960dSAhmed S. Darwish 		amd_l3_attrs[n++] = &dev_attr_subcaches.attr;
269365e960dSAhmed S. Darwish 
270365e960dSAhmed S. Darwish 	cache_private_group.attrs = amd_l3_attrs;
271365e960dSAhmed S. Darwish }
272365e960dSAhmed S. Darwish 
273365e960dSAhmed S. Darwish const struct attribute_group *cache_get_priv_group(struct cacheinfo *ci)
274365e960dSAhmed S. Darwish {
275365e960dSAhmed S. Darwish 	struct amd_northbridge *nb = ci->priv;
276365e960dSAhmed S. Darwish 
277365e960dSAhmed S. Darwish 	if (ci->level < 3 || !nb)
278365e960dSAhmed S. Darwish 		return NULL;
279365e960dSAhmed S. Darwish 
280365e960dSAhmed S. Darwish 	if (nb && nb->l3_cache.indices)
281365e960dSAhmed S. Darwish 		init_amd_l3_attrs();
282365e960dSAhmed S. Darwish 
283365e960dSAhmed S. Darwish 	return &cache_private_group;
284365e960dSAhmed S. Darwish }
285365e960dSAhmed S. Darwish 
286365e960dSAhmed S. Darwish struct amd_northbridge *amd_init_l3_cache(int index)
287365e960dSAhmed S. Darwish {
288365e960dSAhmed S. Darwish 	struct amd_northbridge *nb;
289365e960dSAhmed S. Darwish 	int node;
290365e960dSAhmed S. Darwish 
291365e960dSAhmed S. Darwish 	/* only for L3, and not in virtualized environments */
292365e960dSAhmed S. Darwish 	if (index < 3)
293365e960dSAhmed S. Darwish 		return NULL;
294365e960dSAhmed S. Darwish 
295365e960dSAhmed S. Darwish 	node = topology_amd_node_id(smp_processor_id());
296365e960dSAhmed S. Darwish 	nb = node_to_amd_nb(node);
297365e960dSAhmed S. Darwish 	if (nb && !nb->l3_cache.indices)
298365e960dSAhmed S. Darwish 		amd_calc_l3_indices(nb);
299365e960dSAhmed S. Darwish 
300365e960dSAhmed S. Darwish 	return nb;
301365e960dSAhmed S. Darwish }
302