xref: /linux/drivers/misc/cxl/sysfs.c (revision c2be663d5307fb9751a562ac664fa78cd7a00e2b)
1f204e0b8SIan Munsie /*
2f204e0b8SIan Munsie  * Copyright 2014 IBM Corp.
3f204e0b8SIan Munsie  *
4f204e0b8SIan Munsie  * This program is free software; you can redistribute it and/or
5f204e0b8SIan Munsie  * modify it under the terms of the GNU General Public License
6f204e0b8SIan Munsie  * as published by the Free Software Foundation; either version
7f204e0b8SIan Munsie  * 2 of the License, or (at your option) any later version.
8f204e0b8SIan Munsie  */
9f204e0b8SIan Munsie 
10f204e0b8SIan Munsie #include <linux/kernel.h>
11f204e0b8SIan Munsie #include <linux/device.h>
12f204e0b8SIan Munsie #include <linux/sysfs.h>
13b087e619SIan Munsie #include <linux/pci_regs.h>
14f204e0b8SIan Munsie 
15f204e0b8SIan Munsie #include "cxl.h"
16f204e0b8SIan Munsie 
17f204e0b8SIan Munsie #define to_afu_chardev_m(d) dev_get_drvdata(d)
18f204e0b8SIan Munsie 
19f204e0b8SIan Munsie /*********  Adapter attributes  **********************************************/
20f204e0b8SIan Munsie 
21f204e0b8SIan Munsie static ssize_t caia_version_show(struct device *device,
22f204e0b8SIan Munsie 				 struct device_attribute *attr,
23f204e0b8SIan Munsie 				 char *buf)
24f204e0b8SIan Munsie {
25f204e0b8SIan Munsie 	struct cxl *adapter = to_cxl_adapter(device);
26f204e0b8SIan Munsie 
27f204e0b8SIan Munsie 	return scnprintf(buf, PAGE_SIZE, "%i.%i\n", adapter->caia_major,
28f204e0b8SIan Munsie 			 adapter->caia_minor);
29f204e0b8SIan Munsie }
30f204e0b8SIan Munsie 
31f204e0b8SIan Munsie static ssize_t psl_revision_show(struct device *device,
32f204e0b8SIan Munsie 				 struct device_attribute *attr,
33f204e0b8SIan Munsie 				 char *buf)
34f204e0b8SIan Munsie {
35f204e0b8SIan Munsie 	struct cxl *adapter = to_cxl_adapter(device);
36f204e0b8SIan Munsie 
37f204e0b8SIan Munsie 	return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->psl_rev);
38f204e0b8SIan Munsie }
39f204e0b8SIan Munsie 
40f204e0b8SIan Munsie static ssize_t base_image_show(struct device *device,
41f204e0b8SIan Munsie 			       struct device_attribute *attr,
42f204e0b8SIan Munsie 			       char *buf)
43f204e0b8SIan Munsie {
44f204e0b8SIan Munsie 	struct cxl *adapter = to_cxl_adapter(device);
45f204e0b8SIan Munsie 
46f204e0b8SIan Munsie 	return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->base_image);
47f204e0b8SIan Munsie }
48f204e0b8SIan Munsie 
49f204e0b8SIan Munsie static ssize_t image_loaded_show(struct device *device,
50f204e0b8SIan Munsie 				 struct device_attribute *attr,
51f204e0b8SIan Munsie 				 char *buf)
52f204e0b8SIan Munsie {
53f204e0b8SIan Munsie 	struct cxl *adapter = to_cxl_adapter(device);
54f204e0b8SIan Munsie 
55f204e0b8SIan Munsie 	if (adapter->user_image_loaded)
56f204e0b8SIan Munsie 		return scnprintf(buf, PAGE_SIZE, "user\n");
57f204e0b8SIan Munsie 	return scnprintf(buf, PAGE_SIZE, "factory\n");
58f204e0b8SIan Munsie }
59f204e0b8SIan Munsie 
60e009a7e8SFrederic Barrat static ssize_t psl_timebase_synced_show(struct device *device,
61e009a7e8SFrederic Barrat 					struct device_attribute *attr,
62e009a7e8SFrederic Barrat 					char *buf)
63e009a7e8SFrederic Barrat {
64e009a7e8SFrederic Barrat 	struct cxl *adapter = to_cxl_adapter(device);
65*c2be663dSChristophe Lombard 	u64 psl_tb, delta;
66e009a7e8SFrederic Barrat 
67*c2be663dSChristophe Lombard 	/* Recompute the status only in native mode */
68*c2be663dSChristophe Lombard 	if (cpu_has_feature(CPU_FTR_HVMODE)) {
69*c2be663dSChristophe Lombard 		psl_tb = adapter->native->sl_ops->timebase_read(adapter);
70*c2be663dSChristophe Lombard 		delta = abs(mftb() - psl_tb);
71*c2be663dSChristophe Lombard 
72*c2be663dSChristophe Lombard 		/* CORE TB and PSL TB difference <= 16usecs ? */
73*c2be663dSChristophe Lombard 		adapter->psl_timebase_synced = (tb_to_ns(delta) < 16000) ? true : false;
74*c2be663dSChristophe Lombard 		pr_devel("PSL timebase %s - delta: 0x%016llx\n",
75*c2be663dSChristophe Lombard 			 (tb_to_ns(delta) < 16000) ? "synchronized" :
76*c2be663dSChristophe Lombard 			 "not synchronized", tb_to_ns(delta));
77*c2be663dSChristophe Lombard 	}
78e009a7e8SFrederic Barrat 	return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->psl_timebase_synced);
79e009a7e8SFrederic Barrat }
80e009a7e8SFrederic Barrat 
8162fa19d4SRyan Grimm static ssize_t reset_adapter_store(struct device *device,
8262fa19d4SRyan Grimm 				   struct device_attribute *attr,
8362fa19d4SRyan Grimm 				   const char *buf, size_t count)
8462fa19d4SRyan Grimm {
8562fa19d4SRyan Grimm 	struct cxl *adapter = to_cxl_adapter(device);
8662fa19d4SRyan Grimm 	int rc;
8762fa19d4SRyan Grimm 	int val;
8862fa19d4SRyan Grimm 
8962fa19d4SRyan Grimm 	rc = sscanf(buf, "%i", &val);
9070b565bbSVaibhav Jain 	if ((rc != 1) || (val != 1 && val != -1))
9162fa19d4SRyan Grimm 		return -EINVAL;
9262fa19d4SRyan Grimm 
9370b565bbSVaibhav Jain 	/*
9470b565bbSVaibhav Jain 	 * See if we can lock the context mapping that's only allowed
9570b565bbSVaibhav Jain 	 * when there are no contexts attached to the adapter. Once
9670b565bbSVaibhav Jain 	 * taken this will also prevent any context from getting activated.
9770b565bbSVaibhav Jain 	 */
9870b565bbSVaibhav Jain 	if (val == 1) {
9970b565bbSVaibhav Jain 		rc =  cxl_adapter_context_lock(adapter);
10070b565bbSVaibhav Jain 		if (rc)
10170b565bbSVaibhav Jain 			goto out;
10270b565bbSVaibhav Jain 
10370b565bbSVaibhav Jain 		rc = cxl_ops->adapter_reset(adapter);
10470b565bbSVaibhav Jain 		/* In case reset failed release context lock */
10570b565bbSVaibhav Jain 		if (rc)
10670b565bbSVaibhav Jain 			cxl_adapter_context_unlock(adapter);
10770b565bbSVaibhav Jain 
10870b565bbSVaibhav Jain 	} else if (val == -1) {
10970b565bbSVaibhav Jain 		/* Perform a forced adapter reset */
11070b565bbSVaibhav Jain 		rc = cxl_ops->adapter_reset(adapter);
11170b565bbSVaibhav Jain 	}
11270b565bbSVaibhav Jain 
11370b565bbSVaibhav Jain out:
11470b565bbSVaibhav Jain 	return rc ? rc : count;
11562fa19d4SRyan Grimm }
11662fa19d4SRyan Grimm 
11795bc11bcSRyan Grimm static ssize_t load_image_on_perst_show(struct device *device,
11895bc11bcSRyan Grimm 				 struct device_attribute *attr,
11995bc11bcSRyan Grimm 				 char *buf)
12095bc11bcSRyan Grimm {
12195bc11bcSRyan Grimm 	struct cxl *adapter = to_cxl_adapter(device);
12295bc11bcSRyan Grimm 
12395bc11bcSRyan Grimm 	if (!adapter->perst_loads_image)
12495bc11bcSRyan Grimm 		return scnprintf(buf, PAGE_SIZE, "none\n");
12595bc11bcSRyan Grimm 
12695bc11bcSRyan Grimm 	if (adapter->perst_select_user)
12795bc11bcSRyan Grimm 		return scnprintf(buf, PAGE_SIZE, "user\n");
12895bc11bcSRyan Grimm 	return scnprintf(buf, PAGE_SIZE, "factory\n");
12995bc11bcSRyan Grimm }
13095bc11bcSRyan Grimm 
13195bc11bcSRyan Grimm static ssize_t load_image_on_perst_store(struct device *device,
13295bc11bcSRyan Grimm 				 struct device_attribute *attr,
13395bc11bcSRyan Grimm 				 const char *buf, size_t count)
13495bc11bcSRyan Grimm {
13595bc11bcSRyan Grimm 	struct cxl *adapter = to_cxl_adapter(device);
13695bc11bcSRyan Grimm 	int rc;
13795bc11bcSRyan Grimm 
13895bc11bcSRyan Grimm 	if (!strncmp(buf, "none", 4))
13995bc11bcSRyan Grimm 		adapter->perst_loads_image = false;
14095bc11bcSRyan Grimm 	else if (!strncmp(buf, "user", 4)) {
14195bc11bcSRyan Grimm 		adapter->perst_select_user = true;
14295bc11bcSRyan Grimm 		adapter->perst_loads_image = true;
14395bc11bcSRyan Grimm 	} else if (!strncmp(buf, "factory", 7)) {
14495bc11bcSRyan Grimm 		adapter->perst_select_user = false;
14595bc11bcSRyan Grimm 		adapter->perst_loads_image = true;
14695bc11bcSRyan Grimm 	} else
14795bc11bcSRyan Grimm 		return -EINVAL;
14895bc11bcSRyan Grimm 
14995bc11bcSRyan Grimm 	if ((rc = cxl_update_image_control(adapter)))
15095bc11bcSRyan Grimm 		return rc;
15195bc11bcSRyan Grimm 
15295bc11bcSRyan Grimm 	return count;
15395bc11bcSRyan Grimm }
15495bc11bcSRyan Grimm 
15513e68d8bSDaniel Axtens static ssize_t perst_reloads_same_image_show(struct device *device,
15613e68d8bSDaniel Axtens 				 struct device_attribute *attr,
15713e68d8bSDaniel Axtens 				 char *buf)
15813e68d8bSDaniel Axtens {
15913e68d8bSDaniel Axtens 	struct cxl *adapter = to_cxl_adapter(device);
16013e68d8bSDaniel Axtens 
16113e68d8bSDaniel Axtens 	return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->perst_same_image);
16213e68d8bSDaniel Axtens }
16313e68d8bSDaniel Axtens 
16413e68d8bSDaniel Axtens static ssize_t perst_reloads_same_image_store(struct device *device,
16513e68d8bSDaniel Axtens 				 struct device_attribute *attr,
16613e68d8bSDaniel Axtens 				 const char *buf, size_t count)
16713e68d8bSDaniel Axtens {
16813e68d8bSDaniel Axtens 	struct cxl *adapter = to_cxl_adapter(device);
16913e68d8bSDaniel Axtens 	int rc;
17013e68d8bSDaniel Axtens 	int val;
17113e68d8bSDaniel Axtens 
17213e68d8bSDaniel Axtens 	rc = sscanf(buf, "%i", &val);
17313e68d8bSDaniel Axtens 	if ((rc != 1) || !(val == 1 || val == 0))
17413e68d8bSDaniel Axtens 		return -EINVAL;
17513e68d8bSDaniel Axtens 
17613e68d8bSDaniel Axtens 	adapter->perst_same_image = (val == 1 ? true : false);
17713e68d8bSDaniel Axtens 	return count;
17813e68d8bSDaniel Axtens }
17913e68d8bSDaniel Axtens 
180f204e0b8SIan Munsie static struct device_attribute adapter_attrs[] = {
181f204e0b8SIan Munsie 	__ATTR_RO(caia_version),
182f204e0b8SIan Munsie 	__ATTR_RO(psl_revision),
183f204e0b8SIan Munsie 	__ATTR_RO(base_image),
184f204e0b8SIan Munsie 	__ATTR_RO(image_loaded),
185e009a7e8SFrederic Barrat 	__ATTR_RO(psl_timebase_synced),
18695bc11bcSRyan Grimm 	__ATTR_RW(load_image_on_perst),
18713e68d8bSDaniel Axtens 	__ATTR_RW(perst_reloads_same_image),
18862fa19d4SRyan Grimm 	__ATTR(reset, S_IWUSR, NULL, reset_adapter_store),
189f204e0b8SIan Munsie };
190f204e0b8SIan Munsie 
191f204e0b8SIan Munsie 
192f204e0b8SIan Munsie /*********  AFU master specific attributes  **********************************/
193f204e0b8SIan Munsie 
194f204e0b8SIan Munsie static ssize_t mmio_size_show_master(struct device *device,
195f204e0b8SIan Munsie 				     struct device_attribute *attr,
196f204e0b8SIan Munsie 				     char *buf)
197f204e0b8SIan Munsie {
198f204e0b8SIan Munsie 	struct cxl_afu *afu = to_afu_chardev_m(device);
199f204e0b8SIan Munsie 
200f204e0b8SIan Munsie 	return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->adapter->ps_size);
201f204e0b8SIan Munsie }
202f204e0b8SIan Munsie 
203f204e0b8SIan Munsie static ssize_t pp_mmio_off_show(struct device *device,
204f204e0b8SIan Munsie 				struct device_attribute *attr,
205f204e0b8SIan Munsie 				char *buf)
206f204e0b8SIan Munsie {
207f204e0b8SIan Munsie 	struct cxl_afu *afu = to_afu_chardev_m(device);
208f204e0b8SIan Munsie 
209cbffa3a5SChristophe Lombard 	return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->native->pp_offset);
210f204e0b8SIan Munsie }
211f204e0b8SIan Munsie 
212f204e0b8SIan Munsie static ssize_t pp_mmio_len_show(struct device *device,
213f204e0b8SIan Munsie 				struct device_attribute *attr,
214f204e0b8SIan Munsie 				char *buf)
215f204e0b8SIan Munsie {
216f204e0b8SIan Munsie 	struct cxl_afu *afu = to_afu_chardev_m(device);
217f204e0b8SIan Munsie 
218f204e0b8SIan Munsie 	return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->pp_size);
219f204e0b8SIan Munsie }
220f204e0b8SIan Munsie 
221f204e0b8SIan Munsie static struct device_attribute afu_master_attrs[] = {
222f204e0b8SIan Munsie 	__ATTR(mmio_size, S_IRUGO, mmio_size_show_master, NULL),
223f204e0b8SIan Munsie 	__ATTR_RO(pp_mmio_off),
224f204e0b8SIan Munsie 	__ATTR_RO(pp_mmio_len),
225f204e0b8SIan Munsie };
226f204e0b8SIan Munsie 
227f204e0b8SIan Munsie 
228f204e0b8SIan Munsie /*********  AFU attributes  **************************************************/
229f204e0b8SIan Munsie 
230f204e0b8SIan Munsie static ssize_t mmio_size_show(struct device *device,
231f204e0b8SIan Munsie 			      struct device_attribute *attr,
232f204e0b8SIan Munsie 			      char *buf)
233f204e0b8SIan Munsie {
234f204e0b8SIan Munsie 	struct cxl_afu *afu = to_cxl_afu(device);
235f204e0b8SIan Munsie 
236f204e0b8SIan Munsie 	if (afu->pp_size)
237f204e0b8SIan Munsie 		return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->pp_size);
238f204e0b8SIan Munsie 	return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->adapter->ps_size);
239f204e0b8SIan Munsie }
240f204e0b8SIan Munsie 
241f204e0b8SIan Munsie static ssize_t reset_store_afu(struct device *device,
242f204e0b8SIan Munsie 			       struct device_attribute *attr,
243f204e0b8SIan Munsie 			       const char *buf, size_t count)
244f204e0b8SIan Munsie {
245f204e0b8SIan Munsie 	struct cxl_afu *afu = to_cxl_afu(device);
246f204e0b8SIan Munsie 	int rc;
247f204e0b8SIan Munsie 
248f204e0b8SIan Munsie 	/* Not safe to reset if it is currently in use */
249ee41d11dSIan Munsie 	mutex_lock(&afu->contexts_lock);
250f204e0b8SIan Munsie 	if (!idr_is_empty(&afu->contexts_idr)) {
251f204e0b8SIan Munsie 		rc = -EBUSY;
252f204e0b8SIan Munsie 		goto err;
253f204e0b8SIan Munsie 	}
254f204e0b8SIan Munsie 
2555be587b1SFrederic Barrat 	if ((rc = cxl_ops->afu_reset(afu)))
256f204e0b8SIan Munsie 		goto err;
257f204e0b8SIan Munsie 
258f204e0b8SIan Munsie 	rc = count;
259f204e0b8SIan Munsie err:
260ee41d11dSIan Munsie 	mutex_unlock(&afu->contexts_lock);
261f204e0b8SIan Munsie 	return rc;
262f204e0b8SIan Munsie }
263f204e0b8SIan Munsie 
264f204e0b8SIan Munsie static ssize_t irqs_min_show(struct device *device,
265f204e0b8SIan Munsie 			     struct device_attribute *attr,
266f204e0b8SIan Munsie 			     char *buf)
267f204e0b8SIan Munsie {
268f204e0b8SIan Munsie 	struct cxl_afu *afu = to_cxl_afu(device);
269f204e0b8SIan Munsie 
270f204e0b8SIan Munsie 	return scnprintf(buf, PAGE_SIZE, "%i\n", afu->pp_irqs);
271f204e0b8SIan Munsie }
272f204e0b8SIan Munsie 
273f204e0b8SIan Munsie static ssize_t irqs_max_show(struct device *device,
274f204e0b8SIan Munsie 				  struct device_attribute *attr,
275f204e0b8SIan Munsie 				  char *buf)
276f204e0b8SIan Munsie {
277f204e0b8SIan Munsie 	struct cxl_afu *afu = to_cxl_afu(device);
278f204e0b8SIan Munsie 
279f204e0b8SIan Munsie 	return scnprintf(buf, PAGE_SIZE, "%i\n", afu->irqs_max);
280f204e0b8SIan Munsie }
281f204e0b8SIan Munsie 
282f204e0b8SIan Munsie static ssize_t irqs_max_store(struct device *device,
283f204e0b8SIan Munsie 				  struct device_attribute *attr,
284f204e0b8SIan Munsie 				  const char *buf, size_t count)
285f204e0b8SIan Munsie {
286f204e0b8SIan Munsie 	struct cxl_afu *afu = to_cxl_afu(device);
287f204e0b8SIan Munsie 	ssize_t ret;
288f204e0b8SIan Munsie 	int irqs_max;
289f204e0b8SIan Munsie 
290f204e0b8SIan Munsie 	ret = sscanf(buf, "%i", &irqs_max);
291f204e0b8SIan Munsie 	if (ret != 1)
292f204e0b8SIan Munsie 		return -EINVAL;
293f204e0b8SIan Munsie 
294f204e0b8SIan Munsie 	if (irqs_max < afu->pp_irqs)
295f204e0b8SIan Munsie 		return -EINVAL;
296f204e0b8SIan Munsie 
2974752876cSChristophe Lombard 	if (cpu_has_feature(CPU_FTR_HVMODE)) {
298f204e0b8SIan Munsie 		if (irqs_max > afu->adapter->user_irqs)
299f204e0b8SIan Munsie 			return -EINVAL;
3004752876cSChristophe Lombard 	} else {
3014752876cSChristophe Lombard 		/* pHyp sets a per-AFU limit */
3024752876cSChristophe Lombard 		if (irqs_max > afu->guest->max_ints)
3034752876cSChristophe Lombard 			return -EINVAL;
3044752876cSChristophe Lombard 	}
305f204e0b8SIan Munsie 
306f204e0b8SIan Munsie 	afu->irqs_max = irqs_max;
307f204e0b8SIan Munsie 	return count;
308f204e0b8SIan Munsie }
309f204e0b8SIan Munsie 
310f204e0b8SIan Munsie static ssize_t modes_supported_show(struct device *device,
311f204e0b8SIan Munsie 				    struct device_attribute *attr, char *buf)
312f204e0b8SIan Munsie {
313f204e0b8SIan Munsie 	struct cxl_afu *afu = to_cxl_afu(device);
314f204e0b8SIan Munsie 	char *p = buf, *end = buf + PAGE_SIZE;
315f204e0b8SIan Munsie 
316f204e0b8SIan Munsie 	if (afu->modes_supported & CXL_MODE_DEDICATED)
317f204e0b8SIan Munsie 		p += scnprintf(p, end - p, "dedicated_process\n");
318f204e0b8SIan Munsie 	if (afu->modes_supported & CXL_MODE_DIRECTED)
319f204e0b8SIan Munsie 		p += scnprintf(p, end - p, "afu_directed\n");
320f204e0b8SIan Munsie 	return (p - buf);
321f204e0b8SIan Munsie }
322f204e0b8SIan Munsie 
323f204e0b8SIan Munsie static ssize_t prefault_mode_show(struct device *device,
324f204e0b8SIan Munsie 				  struct device_attribute *attr,
325f204e0b8SIan Munsie 				  char *buf)
326f204e0b8SIan Munsie {
327f204e0b8SIan Munsie 	struct cxl_afu *afu = to_cxl_afu(device);
328f204e0b8SIan Munsie 
329f204e0b8SIan Munsie 	switch (afu->prefault_mode) {
330f204e0b8SIan Munsie 	case CXL_PREFAULT_WED:
331f204e0b8SIan Munsie 		return scnprintf(buf, PAGE_SIZE, "work_element_descriptor\n");
332f204e0b8SIan Munsie 	case CXL_PREFAULT_ALL:
333f204e0b8SIan Munsie 		return scnprintf(buf, PAGE_SIZE, "all\n");
334f204e0b8SIan Munsie 	default:
335f204e0b8SIan Munsie 		return scnprintf(buf, PAGE_SIZE, "none\n");
336f204e0b8SIan Munsie 	}
337f204e0b8SIan Munsie }
338f204e0b8SIan Munsie 
339f204e0b8SIan Munsie static ssize_t prefault_mode_store(struct device *device,
340f204e0b8SIan Munsie 			  struct device_attribute *attr,
341f204e0b8SIan Munsie 			  const char *buf, size_t count)
342f204e0b8SIan Munsie {
343f204e0b8SIan Munsie 	struct cxl_afu *afu = to_cxl_afu(device);
344f204e0b8SIan Munsie 	enum prefault_modes mode = -1;
345f204e0b8SIan Munsie 
346f204e0b8SIan Munsie 	if (!strncmp(buf, "work_element_descriptor", 23))
347f204e0b8SIan Munsie 		mode = CXL_PREFAULT_WED;
348f204e0b8SIan Munsie 	if (!strncmp(buf, "all", 3))
349f204e0b8SIan Munsie 		mode = CXL_PREFAULT_ALL;
350f204e0b8SIan Munsie 	if (!strncmp(buf, "none", 4))
351f204e0b8SIan Munsie 		mode = CXL_PREFAULT_NONE;
352f204e0b8SIan Munsie 
353f204e0b8SIan Munsie 	if (mode == -1)
354f204e0b8SIan Munsie 		return -EINVAL;
355f204e0b8SIan Munsie 
356f204e0b8SIan Munsie 	afu->prefault_mode = mode;
357f204e0b8SIan Munsie 	return count;
358f204e0b8SIan Munsie }
359f204e0b8SIan Munsie 
360f204e0b8SIan Munsie static ssize_t mode_show(struct device *device,
361f204e0b8SIan Munsie 			 struct device_attribute *attr,
362f204e0b8SIan Munsie 			 char *buf)
363f204e0b8SIan Munsie {
364f204e0b8SIan Munsie 	struct cxl_afu *afu = to_cxl_afu(device);
365f204e0b8SIan Munsie 
366f204e0b8SIan Munsie 	if (afu->current_mode == CXL_MODE_DEDICATED)
367f204e0b8SIan Munsie 		return scnprintf(buf, PAGE_SIZE, "dedicated_process\n");
368f204e0b8SIan Munsie 	if (afu->current_mode == CXL_MODE_DIRECTED)
369f204e0b8SIan Munsie 		return scnprintf(buf, PAGE_SIZE, "afu_directed\n");
370f204e0b8SIan Munsie 	return scnprintf(buf, PAGE_SIZE, "none\n");
371f204e0b8SIan Munsie }
372f204e0b8SIan Munsie 
373f204e0b8SIan Munsie static ssize_t mode_store(struct device *device, struct device_attribute *attr,
374f204e0b8SIan Munsie 			  const char *buf, size_t count)
375f204e0b8SIan Munsie {
376f204e0b8SIan Munsie 	struct cxl_afu *afu = to_cxl_afu(device);
377f204e0b8SIan Munsie 	int old_mode, mode = -1;
378f204e0b8SIan Munsie 	int rc = -EBUSY;
379f204e0b8SIan Munsie 
380f204e0b8SIan Munsie 	/* can't change this if we have a user */
381ee41d11dSIan Munsie 	mutex_lock(&afu->contexts_lock);
382f204e0b8SIan Munsie 	if (!idr_is_empty(&afu->contexts_idr))
383f204e0b8SIan Munsie 		goto err;
384f204e0b8SIan Munsie 
385f204e0b8SIan Munsie 	if (!strncmp(buf, "dedicated_process", 17))
386f204e0b8SIan Munsie 		mode = CXL_MODE_DEDICATED;
387f204e0b8SIan Munsie 	if (!strncmp(buf, "afu_directed", 12))
388f204e0b8SIan Munsie 		mode = CXL_MODE_DIRECTED;
389f204e0b8SIan Munsie 	if (!strncmp(buf, "none", 4))
390f204e0b8SIan Munsie 		mode = 0;
391f204e0b8SIan Munsie 
392f204e0b8SIan Munsie 	if (mode == -1) {
393f204e0b8SIan Munsie 		rc = -EINVAL;
394f204e0b8SIan Munsie 		goto err;
395f204e0b8SIan Munsie 	}
396f204e0b8SIan Munsie 
397f204e0b8SIan Munsie 	/*
3985be587b1SFrederic Barrat 	 * afu_deactivate_mode needs to be done outside the lock, prevent
399f204e0b8SIan Munsie 	 * other contexts coming in before we are ready:
400f204e0b8SIan Munsie 	 */
401f204e0b8SIan Munsie 	old_mode = afu->current_mode;
402f204e0b8SIan Munsie 	afu->current_mode = 0;
403f204e0b8SIan Munsie 	afu->num_procs = 0;
404f204e0b8SIan Munsie 
405ee41d11dSIan Munsie 	mutex_unlock(&afu->contexts_lock);
406f204e0b8SIan Munsie 
4075be587b1SFrederic Barrat 	if ((rc = cxl_ops->afu_deactivate_mode(afu, old_mode)))
408f204e0b8SIan Munsie 		return rc;
4095be587b1SFrederic Barrat 	if ((rc = cxl_ops->afu_activate_mode(afu, mode)))
410f204e0b8SIan Munsie 		return rc;
411f204e0b8SIan Munsie 
412f204e0b8SIan Munsie 	return count;
413f204e0b8SIan Munsie err:
414ee41d11dSIan Munsie 	mutex_unlock(&afu->contexts_lock);
415f204e0b8SIan Munsie 	return rc;
416f204e0b8SIan Munsie }
417f204e0b8SIan Munsie 
418f204e0b8SIan Munsie static ssize_t api_version_show(struct device *device,
419f204e0b8SIan Munsie 				struct device_attribute *attr,
420f204e0b8SIan Munsie 				char *buf)
421f204e0b8SIan Munsie {
422f204e0b8SIan Munsie 	return scnprintf(buf, PAGE_SIZE, "%i\n", CXL_API_VERSION);
423f204e0b8SIan Munsie }
424f204e0b8SIan Munsie 
425f204e0b8SIan Munsie static ssize_t api_version_compatible_show(struct device *device,
426f204e0b8SIan Munsie 					   struct device_attribute *attr,
427f204e0b8SIan Munsie 					   char *buf)
428f204e0b8SIan Munsie {
429f204e0b8SIan Munsie 	return scnprintf(buf, PAGE_SIZE, "%i\n", CXL_API_VERSION_COMPATIBLE);
430f204e0b8SIan Munsie }
431f204e0b8SIan Munsie 
432e36f6fe1SVaibhav Jain static ssize_t afu_eb_read(struct file *filp, struct kobject *kobj,
433e36f6fe1SVaibhav Jain 			       struct bin_attribute *bin_attr, char *buf,
434e36f6fe1SVaibhav Jain 			       loff_t off, size_t count)
435e36f6fe1SVaibhav Jain {
43685016ff3SGeliang Tang 	struct cxl_afu *afu = to_cxl_afu(kobj_to_dev(kobj));
437e36f6fe1SVaibhav Jain 
4385be587b1SFrederic Barrat 	return cxl_ops->afu_read_err_buffer(afu, buf, off, count);
439e36f6fe1SVaibhav Jain }
440e36f6fe1SVaibhav Jain 
441f204e0b8SIan Munsie static struct device_attribute afu_attrs[] = {
442f204e0b8SIan Munsie 	__ATTR_RO(mmio_size),
443f204e0b8SIan Munsie 	__ATTR_RO(irqs_min),
444f204e0b8SIan Munsie 	__ATTR_RW(irqs_max),
445f204e0b8SIan Munsie 	__ATTR_RO(modes_supported),
446f204e0b8SIan Munsie 	__ATTR_RW(mode),
447f204e0b8SIan Munsie 	__ATTR_RW(prefault_mode),
448f204e0b8SIan Munsie 	__ATTR_RO(api_version),
449f204e0b8SIan Munsie 	__ATTR_RO(api_version_compatible),
450f204e0b8SIan Munsie 	__ATTR(reset, S_IWUSR, NULL, reset_store_afu),
451f204e0b8SIan Munsie };
452f204e0b8SIan Munsie 
453f204e0b8SIan Munsie int cxl_sysfs_adapter_add(struct cxl *adapter)
454f204e0b8SIan Munsie {
4554752876cSChristophe Lombard 	struct device_attribute *dev_attr;
456f204e0b8SIan Munsie 	int i, rc;
457f204e0b8SIan Munsie 
458f204e0b8SIan Munsie 	for (i = 0; i < ARRAY_SIZE(adapter_attrs); i++) {
4594752876cSChristophe Lombard 		dev_attr = &adapter_attrs[i];
4604752876cSChristophe Lombard 		if (cxl_ops->support_attributes(dev_attr->attr.name,
4614752876cSChristophe Lombard 						CXL_ADAPTER_ATTRS)) {
4624752876cSChristophe Lombard 			if ((rc = device_create_file(&adapter->dev, dev_attr)))
463f204e0b8SIan Munsie 				goto err;
464f204e0b8SIan Munsie 		}
4654752876cSChristophe Lombard 	}
466f204e0b8SIan Munsie 	return 0;
467f204e0b8SIan Munsie err:
4684752876cSChristophe Lombard 	for (i--; i >= 0; i--) {
4694752876cSChristophe Lombard 		dev_attr = &adapter_attrs[i];
4704752876cSChristophe Lombard 		if (cxl_ops->support_attributes(dev_attr->attr.name,
4714752876cSChristophe Lombard 						CXL_ADAPTER_ATTRS))
4724752876cSChristophe Lombard 			device_remove_file(&adapter->dev, dev_attr);
4734752876cSChristophe Lombard 	}
474f204e0b8SIan Munsie 	return rc;
475f204e0b8SIan Munsie }
4764752876cSChristophe Lombard 
477f204e0b8SIan Munsie void cxl_sysfs_adapter_remove(struct cxl *adapter)
478f204e0b8SIan Munsie {
4794752876cSChristophe Lombard 	struct device_attribute *dev_attr;
480f204e0b8SIan Munsie 	int i;
481f204e0b8SIan Munsie 
4824752876cSChristophe Lombard 	for (i = 0; i < ARRAY_SIZE(adapter_attrs); i++) {
4834752876cSChristophe Lombard 		dev_attr = &adapter_attrs[i];
4844752876cSChristophe Lombard 		if (cxl_ops->support_attributes(dev_attr->attr.name,
4854752876cSChristophe Lombard 						CXL_ADAPTER_ATTRS))
4864752876cSChristophe Lombard 			device_remove_file(&adapter->dev, dev_attr);
4874752876cSChristophe Lombard 	}
488f204e0b8SIan Munsie }
489f204e0b8SIan Munsie 
490b087e619SIan Munsie struct afu_config_record {
491b087e619SIan Munsie 	struct kobject kobj;
492b087e619SIan Munsie 	struct bin_attribute config_attr;
493b087e619SIan Munsie 	struct list_head list;
494b087e619SIan Munsie 	int cr;
495b087e619SIan Munsie 	u16 device;
496b087e619SIan Munsie 	u16 vendor;
497b087e619SIan Munsie 	u32 class;
498b087e619SIan Munsie };
499b087e619SIan Munsie 
500b087e619SIan Munsie #define to_cr(obj) container_of(obj, struct afu_config_record, kobj)
501b087e619SIan Munsie 
502b087e619SIan Munsie static ssize_t vendor_show(struct kobject *kobj,
503b087e619SIan Munsie 			   struct kobj_attribute *attr, char *buf)
504b087e619SIan Munsie {
505b087e619SIan Munsie 	struct afu_config_record *cr = to_cr(kobj);
506b087e619SIan Munsie 
507b087e619SIan Munsie 	return scnprintf(buf, PAGE_SIZE, "0x%.4x\n", cr->vendor);
508b087e619SIan Munsie }
509b087e619SIan Munsie 
510b087e619SIan Munsie static ssize_t device_show(struct kobject *kobj,
511b087e619SIan Munsie 			   struct kobj_attribute *attr, char *buf)
512b087e619SIan Munsie {
513b087e619SIan Munsie 	struct afu_config_record *cr = to_cr(kobj);
514b087e619SIan Munsie 
515b087e619SIan Munsie 	return scnprintf(buf, PAGE_SIZE, "0x%.4x\n", cr->device);
516b087e619SIan Munsie }
517b087e619SIan Munsie 
518b087e619SIan Munsie static ssize_t class_show(struct kobject *kobj,
519b087e619SIan Munsie 			  struct kobj_attribute *attr, char *buf)
520b087e619SIan Munsie {
521b087e619SIan Munsie 	struct afu_config_record *cr = to_cr(kobj);
522b087e619SIan Munsie 
523b087e619SIan Munsie 	return scnprintf(buf, PAGE_SIZE, "0x%.6x\n", cr->class);
524b087e619SIan Munsie }
525b087e619SIan Munsie 
526b087e619SIan Munsie static ssize_t afu_read_config(struct file *filp, struct kobject *kobj,
527b087e619SIan Munsie 			       struct bin_attribute *bin_attr, char *buf,
528b087e619SIan Munsie 			       loff_t off, size_t count)
529b087e619SIan Munsie {
530b087e619SIan Munsie 	struct afu_config_record *cr = to_cr(kobj);
53185016ff3SGeliang Tang 	struct cxl_afu *afu = to_cxl_afu(kobj_to_dev(kobj->parent));
532b087e619SIan Munsie 
5335be587b1SFrederic Barrat 	u64 i, j, val, rc;
534b087e619SIan Munsie 
535b087e619SIan Munsie 	for (i = 0; i < count;) {
5365be587b1SFrederic Barrat 		rc = cxl_ops->afu_cr_read64(afu, cr->cr, off & ~0x7, &val);
5375be587b1SFrederic Barrat 		if (rc)
5385be587b1SFrederic Barrat 			val = ~0ULL;
539b087e619SIan Munsie 		for (j = off & 0x7; j < 8 && i < count; i++, j++, off++)
540b087e619SIan Munsie 			buf[i] = (val >> (j * 8)) & 0xff;
541b087e619SIan Munsie 	}
542b087e619SIan Munsie 
543b087e619SIan Munsie 	return count;
544b087e619SIan Munsie }
545b087e619SIan Munsie 
546b087e619SIan Munsie static struct kobj_attribute vendor_attribute =
547b087e619SIan Munsie 	__ATTR_RO(vendor);
548b087e619SIan Munsie static struct kobj_attribute device_attribute =
549b087e619SIan Munsie 	__ATTR_RO(device);
550b087e619SIan Munsie static struct kobj_attribute class_attribute =
551b087e619SIan Munsie 	__ATTR_RO(class);
552b087e619SIan Munsie 
553b087e619SIan Munsie static struct attribute *afu_cr_attrs[] = {
554b087e619SIan Munsie 	&vendor_attribute.attr,
555b087e619SIan Munsie 	&device_attribute.attr,
556b087e619SIan Munsie 	&class_attribute.attr,
557b087e619SIan Munsie 	NULL,
558b087e619SIan Munsie };
559b087e619SIan Munsie 
560b087e619SIan Munsie static void release_afu_config_record(struct kobject *kobj)
561b087e619SIan Munsie {
562b087e619SIan Munsie 	struct afu_config_record *cr = to_cr(kobj);
563b087e619SIan Munsie 
564b087e619SIan Munsie 	kfree(cr);
565b087e619SIan Munsie }
566b087e619SIan Munsie 
567b087e619SIan Munsie static struct kobj_type afu_config_record_type = {
568b087e619SIan Munsie 	.sysfs_ops = &kobj_sysfs_ops,
569b087e619SIan Munsie 	.release = release_afu_config_record,
570b087e619SIan Munsie 	.default_attrs = afu_cr_attrs,
571b087e619SIan Munsie };
572b087e619SIan Munsie 
573b087e619SIan Munsie static struct afu_config_record *cxl_sysfs_afu_new_cr(struct cxl_afu *afu, int cr_idx)
574b087e619SIan Munsie {
575b087e619SIan Munsie 	struct afu_config_record *cr;
576b087e619SIan Munsie 	int rc;
577b087e619SIan Munsie 
578b087e619SIan Munsie 	cr = kzalloc(sizeof(struct afu_config_record), GFP_KERNEL);
579b087e619SIan Munsie 	if (!cr)
580b087e619SIan Munsie 		return ERR_PTR(-ENOMEM);
581b087e619SIan Munsie 
582b087e619SIan Munsie 	cr->cr = cr_idx;
5835be587b1SFrederic Barrat 
5845be587b1SFrederic Barrat 	rc = cxl_ops->afu_cr_read16(afu, cr_idx, PCI_DEVICE_ID, &cr->device);
5855be587b1SFrederic Barrat 	if (rc)
5865be587b1SFrederic Barrat 		goto err;
5875be587b1SFrederic Barrat 	rc = cxl_ops->afu_cr_read16(afu, cr_idx, PCI_VENDOR_ID, &cr->vendor);
5885be587b1SFrederic Barrat 	if (rc)
5895be587b1SFrederic Barrat 		goto err;
5905be587b1SFrederic Barrat 	rc = cxl_ops->afu_cr_read32(afu, cr_idx, PCI_CLASS_REVISION, &cr->class);
5915be587b1SFrederic Barrat 	if (rc)
5925be587b1SFrederic Barrat 		goto err;
5935be587b1SFrederic Barrat 	cr->class >>= 8;
594b087e619SIan Munsie 
595b087e619SIan Munsie 	/*
596b087e619SIan Munsie 	 * Export raw AFU PCIe like config record. For now this is read only by
597b087e619SIan Munsie 	 * root - we can expand that later to be readable by non-root and maybe
5984752876cSChristophe Lombard 	 * even writable provided we have a good use-case. Once we support
599b087e619SIan Munsie 	 * exposing AFUs through a virtual PHB they will get that for free from
600b087e619SIan Munsie 	 * Linux' PCI infrastructure, but until then it's not clear that we
601b087e619SIan Munsie 	 * need it for anything since the main use case is just identifying
602b087e619SIan Munsie 	 * AFUs, which can be done via the vendor, device and class attributes.
603b087e619SIan Munsie 	 */
604b087e619SIan Munsie 	sysfs_bin_attr_init(&cr->config_attr);
605b087e619SIan Munsie 	cr->config_attr.attr.name = "config";
606b087e619SIan Munsie 	cr->config_attr.attr.mode = S_IRUSR;
607b087e619SIan Munsie 	cr->config_attr.size = afu->crs_len;
608b087e619SIan Munsie 	cr->config_attr.read = afu_read_config;
609b087e619SIan Munsie 
610b087e619SIan Munsie 	rc = kobject_init_and_add(&cr->kobj, &afu_config_record_type,
611b087e619SIan Munsie 				  &afu->dev.kobj, "cr%i", cr->cr);
612b087e619SIan Munsie 	if (rc)
613b087e619SIan Munsie 		goto err;
614b087e619SIan Munsie 
615b087e619SIan Munsie 	rc = sysfs_create_bin_file(&cr->kobj, &cr->config_attr);
616b087e619SIan Munsie 	if (rc)
617b087e619SIan Munsie 		goto err1;
618b087e619SIan Munsie 
619b087e619SIan Munsie 	rc = kobject_uevent(&cr->kobj, KOBJ_ADD);
620b087e619SIan Munsie 	if (rc)
621b087e619SIan Munsie 		goto err2;
622b087e619SIan Munsie 
623b087e619SIan Munsie 	return cr;
624b087e619SIan Munsie err2:
625b087e619SIan Munsie 	sysfs_remove_bin_file(&cr->kobj, &cr->config_attr);
626b087e619SIan Munsie err1:
627b087e619SIan Munsie 	kobject_put(&cr->kobj);
628b087e619SIan Munsie 	return ERR_PTR(rc);
629b087e619SIan Munsie err:
630b087e619SIan Munsie 	kfree(cr);
631b087e619SIan Munsie 	return ERR_PTR(rc);
632b087e619SIan Munsie }
633b087e619SIan Munsie 
634b087e619SIan Munsie void cxl_sysfs_afu_remove(struct cxl_afu *afu)
635b087e619SIan Munsie {
6364752876cSChristophe Lombard 	struct device_attribute *dev_attr;
637b087e619SIan Munsie 	struct afu_config_record *cr, *tmp;
638b087e619SIan Munsie 	int i;
639b087e619SIan Munsie 
640e36f6fe1SVaibhav Jain 	/* remove the err buffer bin attribute */
641e36f6fe1SVaibhav Jain 	if (afu->eb_len)
642e36f6fe1SVaibhav Jain 		device_remove_bin_file(&afu->dev, &afu->attr_eb);
643e36f6fe1SVaibhav Jain 
6444752876cSChristophe Lombard 	for (i = 0; i < ARRAY_SIZE(afu_attrs); i++) {
6454752876cSChristophe Lombard 		dev_attr = &afu_attrs[i];
6464752876cSChristophe Lombard 		if (cxl_ops->support_attributes(dev_attr->attr.name,
6474752876cSChristophe Lombard 						CXL_AFU_ATTRS))
648b087e619SIan Munsie 			device_remove_file(&afu->dev, &afu_attrs[i]);
6494752876cSChristophe Lombard 	}
650b087e619SIan Munsie 
651b087e619SIan Munsie 	list_for_each_entry_safe(cr, tmp, &afu->crs, list) {
652b087e619SIan Munsie 		sysfs_remove_bin_file(&cr->kobj, &cr->config_attr);
653b087e619SIan Munsie 		kobject_put(&cr->kobj);
654b087e619SIan Munsie 	}
655b087e619SIan Munsie }
656b087e619SIan Munsie 
657f204e0b8SIan Munsie int cxl_sysfs_afu_add(struct cxl_afu *afu)
658f204e0b8SIan Munsie {
6594752876cSChristophe Lombard 	struct device_attribute *dev_attr;
660b087e619SIan Munsie 	struct afu_config_record *cr;
661f204e0b8SIan Munsie 	int i, rc;
662f204e0b8SIan Munsie 
663b087e619SIan Munsie 	INIT_LIST_HEAD(&afu->crs);
664b087e619SIan Munsie 
665f204e0b8SIan Munsie 	for (i = 0; i < ARRAY_SIZE(afu_attrs); i++) {
6664752876cSChristophe Lombard 		dev_attr = &afu_attrs[i];
6674752876cSChristophe Lombard 		if (cxl_ops->support_attributes(dev_attr->attr.name,
6684752876cSChristophe Lombard 						CXL_AFU_ATTRS)) {
669f204e0b8SIan Munsie 			if ((rc = device_create_file(&afu->dev, &afu_attrs[i])))
670f204e0b8SIan Munsie 				goto err;
671f204e0b8SIan Munsie 		}
6724752876cSChristophe Lombard 	}
673f204e0b8SIan Munsie 
674e36f6fe1SVaibhav Jain 	/* conditionally create the add the binary file for error info buffer */
675e36f6fe1SVaibhav Jain 	if (afu->eb_len) {
676d6eb71a6SVaibhav Jain 		sysfs_attr_init(&afu->attr_eb.attr);
677d6eb71a6SVaibhav Jain 
678e36f6fe1SVaibhav Jain 		afu->attr_eb.attr.name = "afu_err_buff";
679e36f6fe1SVaibhav Jain 		afu->attr_eb.attr.mode = S_IRUGO;
680e36f6fe1SVaibhav Jain 		afu->attr_eb.size = afu->eb_len;
681e36f6fe1SVaibhav Jain 		afu->attr_eb.read = afu_eb_read;
682e36f6fe1SVaibhav Jain 
683e36f6fe1SVaibhav Jain 		rc = device_create_bin_file(&afu->dev, &afu->attr_eb);
684e36f6fe1SVaibhav Jain 		if (rc) {
685e36f6fe1SVaibhav Jain 			dev_err(&afu->dev,
686e36f6fe1SVaibhav Jain 				"Unable to create eb attr for the afu. Err(%d)\n",
687e36f6fe1SVaibhav Jain 				rc);
688e36f6fe1SVaibhav Jain 			goto err;
689e36f6fe1SVaibhav Jain 		}
690e36f6fe1SVaibhav Jain 	}
691e36f6fe1SVaibhav Jain 
692b087e619SIan Munsie 	for (i = 0; i < afu->crs_num; i++) {
693b087e619SIan Munsie 		cr = cxl_sysfs_afu_new_cr(afu, i);
694b087e619SIan Munsie 		if (IS_ERR(cr)) {
695b087e619SIan Munsie 			rc = PTR_ERR(cr);
696b087e619SIan Munsie 			goto err1;
697b087e619SIan Munsie 		}
698b087e619SIan Munsie 		list_add(&cr->list, &afu->crs);
699b087e619SIan Munsie 	}
700b087e619SIan Munsie 
701f204e0b8SIan Munsie 	return 0;
702f204e0b8SIan Munsie 
703b087e619SIan Munsie err1:
704b087e619SIan Munsie 	cxl_sysfs_afu_remove(afu);
705b087e619SIan Munsie 	return rc;
706f204e0b8SIan Munsie err:
707e36f6fe1SVaibhav Jain 	/* reset the eb_len as we havent created the bin attr */
708e36f6fe1SVaibhav Jain 	afu->eb_len = 0;
709e36f6fe1SVaibhav Jain 
7104752876cSChristophe Lombard 	for (i--; i >= 0; i--) {
7114752876cSChristophe Lombard 		dev_attr = &afu_attrs[i];
7124752876cSChristophe Lombard 		if (cxl_ops->support_attributes(dev_attr->attr.name,
7134752876cSChristophe Lombard 						CXL_AFU_ATTRS))
714f204e0b8SIan Munsie 		device_remove_file(&afu->dev, &afu_attrs[i]);
7154752876cSChristophe Lombard 	}
716f204e0b8SIan Munsie 	return rc;
717f204e0b8SIan Munsie }
718f204e0b8SIan Munsie 
719f204e0b8SIan Munsie int cxl_sysfs_afu_m_add(struct cxl_afu *afu)
720f204e0b8SIan Munsie {
7214752876cSChristophe Lombard 	struct device_attribute *dev_attr;
722f204e0b8SIan Munsie 	int i, rc;
723f204e0b8SIan Munsie 
724f204e0b8SIan Munsie 	for (i = 0; i < ARRAY_SIZE(afu_master_attrs); i++) {
7254752876cSChristophe Lombard 		dev_attr = &afu_master_attrs[i];
7264752876cSChristophe Lombard 		if (cxl_ops->support_attributes(dev_attr->attr.name,
7274752876cSChristophe Lombard 						CXL_AFU_MASTER_ATTRS)) {
728f204e0b8SIan Munsie 			if ((rc = device_create_file(afu->chardev_m, &afu_master_attrs[i])))
729f204e0b8SIan Munsie 				goto err;
730f204e0b8SIan Munsie 		}
7314752876cSChristophe Lombard 	}
732f204e0b8SIan Munsie 
733f204e0b8SIan Munsie 	return 0;
734f204e0b8SIan Munsie 
735f204e0b8SIan Munsie err:
7364752876cSChristophe Lombard 	for (i--; i >= 0; i--) {
7374752876cSChristophe Lombard 		dev_attr = &afu_master_attrs[i];
7384752876cSChristophe Lombard 		if (cxl_ops->support_attributes(dev_attr->attr.name,
7394752876cSChristophe Lombard 						CXL_AFU_MASTER_ATTRS))
740f204e0b8SIan Munsie 			device_remove_file(afu->chardev_m, &afu_master_attrs[i]);
7414752876cSChristophe Lombard 	}
742f204e0b8SIan Munsie 	return rc;
743f204e0b8SIan Munsie }
744f204e0b8SIan Munsie 
745f204e0b8SIan Munsie void cxl_sysfs_afu_m_remove(struct cxl_afu *afu)
746f204e0b8SIan Munsie {
7474752876cSChristophe Lombard 	struct device_attribute *dev_attr;
748f204e0b8SIan Munsie 	int i;
749f204e0b8SIan Munsie 
7504752876cSChristophe Lombard 	for (i = 0; i < ARRAY_SIZE(afu_master_attrs); i++) {
7514752876cSChristophe Lombard 		dev_attr = &afu_master_attrs[i];
7524752876cSChristophe Lombard 		if (cxl_ops->support_attributes(dev_attr->attr.name,
7534752876cSChristophe Lombard 						CXL_AFU_MASTER_ATTRS))
754f204e0b8SIan Munsie 			device_remove_file(afu->chardev_m, &afu_master_attrs[i]);
755f204e0b8SIan Munsie 	}
7564752876cSChristophe Lombard }
757