xref: /linux/drivers/misc/cxl/file.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2f204e0b8SIan Munsie /*
3f204e0b8SIan Munsie  * Copyright 2014 IBM Corp.
4f204e0b8SIan Munsie  */
5f204e0b8SIan Munsie 
6f204e0b8SIan Munsie #include <linux/spinlock.h>
7f204e0b8SIan Munsie #include <linux/module.h>
8f204e0b8SIan Munsie #include <linux/export.h>
9f204e0b8SIan Munsie #include <linux/kernel.h>
10f204e0b8SIan Munsie #include <linux/bitmap.h>
11174cd4b1SIngo Molnar #include <linux/sched/signal.h>
12f204e0b8SIan Munsie #include <linux/poll.h>
13f204e0b8SIan Munsie #include <linux/pid.h>
14f204e0b8SIan Munsie #include <linux/fs.h>
15f204e0b8SIan Munsie #include <linux/mm.h>
16f204e0b8SIan Munsie #include <linux/slab.h>
176dd2d234SChristophe Lombard #include <linux/sched/mm.h>
1803b8abedSFrederic Barrat #include <linux/mmu_context.h>
19f204e0b8SIan Munsie #include <asm/cputable.h>
20f204e0b8SIan Munsie #include <asm/current.h>
21f204e0b8SIan Munsie #include <asm/copro.h>
22f204e0b8SIan Munsie 
23f204e0b8SIan Munsie #include "cxl.h"
249bcf28cdSIan Munsie #include "trace.h"
25f204e0b8SIan Munsie 
26f204e0b8SIan Munsie #define CXL_NUM_MINORS 256 /* Total to reserve */
27f204e0b8SIan Munsie 
28f204e0b8SIan Munsie #define CXL_AFU_MINOR_D(afu) (CXL_CARD_MINOR(afu->adapter) + 1 + (3 * afu->slice))
29f204e0b8SIan Munsie #define CXL_AFU_MINOR_M(afu) (CXL_AFU_MINOR_D(afu) + 1)
30f204e0b8SIan Munsie #define CXL_AFU_MINOR_S(afu) (CXL_AFU_MINOR_D(afu) + 2)
31f204e0b8SIan Munsie #define CXL_AFU_MKDEV_D(afu) MKDEV(MAJOR(cxl_dev), CXL_AFU_MINOR_D(afu))
32f204e0b8SIan Munsie #define CXL_AFU_MKDEV_M(afu) MKDEV(MAJOR(cxl_dev), CXL_AFU_MINOR_M(afu))
33f204e0b8SIan Munsie #define CXL_AFU_MKDEV_S(afu) MKDEV(MAJOR(cxl_dev), CXL_AFU_MINOR_S(afu))
34f204e0b8SIan Munsie 
35f204e0b8SIan Munsie #define CXL_DEVT_AFU(dev) ((MINOR(dev) % CXL_DEV_MINORS - 1) / 3)
36f204e0b8SIan Munsie 
37f204e0b8SIan Munsie #define CXL_DEVT_IS_CARD(dev) (MINOR(dev) % CXL_DEV_MINORS == 0)
38f204e0b8SIan Munsie 
39f204e0b8SIan Munsie static dev_t cxl_dev;
40f204e0b8SIan Munsie 
__afu_open(struct inode * inode,struct file * file,bool master)41f204e0b8SIan Munsie static int __afu_open(struct inode *inode, struct file *file, bool master)
42f204e0b8SIan Munsie {
43f204e0b8SIan Munsie 	struct cxl *adapter;
44f204e0b8SIan Munsie 	struct cxl_afu *afu;
45f204e0b8SIan Munsie 	struct cxl_context *ctx;
46f204e0b8SIan Munsie 	int adapter_num = CXL_DEVT_ADAPTER(inode->i_rdev);
47f204e0b8SIan Munsie 	int slice = CXL_DEVT_AFU(inode->i_rdev);
48f204e0b8SIan Munsie 	int rc = -ENODEV;
49f204e0b8SIan Munsie 
50f204e0b8SIan Munsie 	pr_devel("afu_open afu%i.%i\n", slice, adapter_num);
51f204e0b8SIan Munsie 
52f204e0b8SIan Munsie 	if (!(adapter = get_cxl_adapter(adapter_num)))
53f204e0b8SIan Munsie 		return -ENODEV;
54f204e0b8SIan Munsie 
55f204e0b8SIan Munsie 	if (slice > adapter->slices)
56f204e0b8SIan Munsie 		goto err_put_adapter;
57f204e0b8SIan Munsie 
58f204e0b8SIan Munsie 	spin_lock(&adapter->afu_list_lock);
59f204e0b8SIan Munsie 	if (!(afu = adapter->afu[slice])) {
60f204e0b8SIan Munsie 		spin_unlock(&adapter->afu_list_lock);
61f204e0b8SIan Munsie 		goto err_put_adapter;
62f204e0b8SIan Munsie 	}
631b5df59eSVaibhav Jain 
641b5df59eSVaibhav Jain 	/*
651b5df59eSVaibhav Jain 	 * taking a ref to the afu so that it doesn't go away
661b5df59eSVaibhav Jain 	 * for rest of the function. This ref is released before
671b5df59eSVaibhav Jain 	 * we return.
681b5df59eSVaibhav Jain 	 */
691b5df59eSVaibhav Jain 	cxl_afu_get(afu);
70f204e0b8SIan Munsie 	spin_unlock(&adapter->afu_list_lock);
71f204e0b8SIan Munsie 
72f204e0b8SIan Munsie 	if (!afu->current_mode)
73f204e0b8SIan Munsie 		goto err_put_afu;
74f204e0b8SIan Munsie 
750d400f77SChristophe Lombard 	if (!cxl_ops->link_ok(adapter, afu)) {
760b3f9c75SDaniel Axtens 		rc = -EIO;
770b3f9c75SDaniel Axtens 		goto err_put_afu;
780b3f9c75SDaniel Axtens 	}
790b3f9c75SDaniel Axtens 
80f204e0b8SIan Munsie 	if (!(ctx = cxl_context_alloc())) {
81f204e0b8SIan Munsie 		rc = -ENOMEM;
82f204e0b8SIan Munsie 		goto err_put_afu;
83f204e0b8SIan Munsie 	}
84f204e0b8SIan Munsie 
85bdecf76eSFrederic Barrat 	rc = cxl_context_init(ctx, afu, master);
86bdecf76eSFrederic Barrat 	if (rc)
87f204e0b8SIan Munsie 		goto err_put_afu;
88f204e0b8SIan Munsie 
89bdecf76eSFrederic Barrat 	cxl_context_set_mapping(ctx, inode->i_mapping);
90bdecf76eSFrederic Barrat 
91f204e0b8SIan Munsie 	pr_devel("afu_open pe: %i\n", ctx->pe);
92f204e0b8SIan Munsie 	file->private_data = ctx;
93f204e0b8SIan Munsie 
941b5df59eSVaibhav Jain 	/* indicate success */
951b5df59eSVaibhav Jain 	rc = 0;
96f204e0b8SIan Munsie 
97f204e0b8SIan Munsie err_put_afu:
981b5df59eSVaibhav Jain 	/* release the ref taken earlier */
991b5df59eSVaibhav Jain 	cxl_afu_put(afu);
100f204e0b8SIan Munsie err_put_adapter:
101f204e0b8SIan Munsie 	put_device(&adapter->dev);
102f204e0b8SIan Munsie 	return rc;
103f204e0b8SIan Munsie }
1040520336aSMichael Neuling 
afu_open(struct inode * inode,struct file * file)1050520336aSMichael Neuling int afu_open(struct inode *inode, struct file *file)
106f204e0b8SIan Munsie {
107f204e0b8SIan Munsie 	return __afu_open(inode, file, false);
108f204e0b8SIan Munsie }
109f204e0b8SIan Munsie 
afu_master_open(struct inode * inode,struct file * file)110f204e0b8SIan Munsie static int afu_master_open(struct inode *inode, struct file *file)
111f204e0b8SIan Munsie {
112f204e0b8SIan Munsie 	return __afu_open(inode, file, true);
113f204e0b8SIan Munsie }
114f204e0b8SIan Munsie 
afu_release(struct inode * inode,struct file * file)1150520336aSMichael Neuling int afu_release(struct inode *inode, struct file *file)
116f204e0b8SIan Munsie {
117f204e0b8SIan Munsie 	struct cxl_context *ctx = file->private_data;
118f204e0b8SIan Munsie 
119f204e0b8SIan Munsie 	pr_devel("%s: closing cxl file descriptor. pe: %i\n",
120f204e0b8SIan Munsie 		 __func__, ctx->pe);
121f204e0b8SIan Munsie 	cxl_context_detach(ctx);
122f204e0b8SIan Munsie 
1235f81b95fSAndrew Donnellan 
1245f81b95fSAndrew Donnellan 	/*
1255f81b95fSAndrew Donnellan 	 * Delete the context's mapping pointer, unless it's created by the
1265f81b95fSAndrew Donnellan 	 * kernel API, in which case leave it so it can be freed by reclaim_ctx()
1275f81b95fSAndrew Donnellan 	 */
1285f81b95fSAndrew Donnellan 	if (!ctx->kernelapi) {
129b123429eSIan Munsie 		mutex_lock(&ctx->mapping_lock);
130b123429eSIan Munsie 		ctx->mapping = NULL;
131b123429eSIan Munsie 		mutex_unlock(&ctx->mapping_lock);
1325f81b95fSAndrew Donnellan 	}
133b123429eSIan Munsie 
134f204e0b8SIan Munsie 	/*
135f204e0b8SIan Munsie 	 * At this this point all bottom halfs have finished and we should be
136f204e0b8SIan Munsie 	 * getting no more IRQs from the hardware for this context.  Once it's
137f204e0b8SIan Munsie 	 * removed from the IDR (and RCU synchronised) it's safe to free the
138f204e0b8SIan Munsie 	 * sstp and context.
139f204e0b8SIan Munsie 	 */
140f204e0b8SIan Munsie 	cxl_context_free(ctx);
141f204e0b8SIan Munsie 
142f204e0b8SIan Munsie 	return 0;
143f204e0b8SIan Munsie }
144f204e0b8SIan Munsie 
afu_ioctl_start_work(struct cxl_context * ctx,struct cxl_ioctl_start_work __user * uwork)145f204e0b8SIan Munsie static long afu_ioctl_start_work(struct cxl_context *ctx,
146f204e0b8SIan Munsie 				 struct cxl_ioctl_start_work __user *uwork)
147f204e0b8SIan Munsie {
148f204e0b8SIan Munsie 	struct cxl_ioctl_start_work work;
149f204e0b8SIan Munsie 	u64 amr = 0;
150f204e0b8SIan Munsie 	int rc;
151f204e0b8SIan Munsie 
152f204e0b8SIan Munsie 	pr_devel("%s: pe: %i\n", __func__, ctx->pe);
153f204e0b8SIan Munsie 
1540712dc7eSIan Munsie 	/* Do this outside the status_mutex to avoid a circular dependency with
1550712dc7eSIan Munsie 	 * the locking in cxl_mmap_fault() */
156cec422c1SFrederic Barrat 	if (copy_from_user(&work, uwork, sizeof(work)))
157cec422c1SFrederic Barrat 		return -EFAULT;
158f204e0b8SIan Munsie 
1590712dc7eSIan Munsie 	mutex_lock(&ctx->status_mutex);
1600712dc7eSIan Munsie 	if (ctx->status != OPENED) {
1610712dc7eSIan Munsie 		rc = -EIO;
1620712dc7eSIan Munsie 		goto out;
1630712dc7eSIan Munsie 	}
1640712dc7eSIan Munsie 
165f204e0b8SIan Munsie 	/*
166f204e0b8SIan Munsie 	 * if any of the reserved fields are set or any of the unused
167f204e0b8SIan Munsie 	 * flags are set it's invalid
168f204e0b8SIan Munsie 	 */
169f204e0b8SIan Munsie 	if (work.reserved1 || work.reserved2 || work.reserved3 ||
170b1db5513SChristophe Lombard 	    work.reserved4 || work.reserved5 ||
171f204e0b8SIan Munsie 	    (work.flags & ~CXL_START_WORK_ALL)) {
172f204e0b8SIan Munsie 		rc = -EINVAL;
173f204e0b8SIan Munsie 		goto out;
174f204e0b8SIan Munsie 	}
175f204e0b8SIan Munsie 
176f204e0b8SIan Munsie 	if (!(work.flags & CXL_START_WORK_NUM_IRQS))
177f204e0b8SIan Munsie 		work.num_interrupts = ctx->afu->pp_irqs;
178f204e0b8SIan Munsie 	else if ((work.num_interrupts < ctx->afu->pp_irqs) ||
179f204e0b8SIan Munsie 		 (work.num_interrupts > ctx->afu->irqs_max)) {
180f204e0b8SIan Munsie 		rc =  -EINVAL;
181f204e0b8SIan Munsie 		goto out;
182f204e0b8SIan Munsie 	}
183b1db5513SChristophe Lombard 
184f204e0b8SIan Munsie 	if ((rc = afu_register_irqs(ctx, work.num_interrupts)))
185f204e0b8SIan Munsie 		goto out;
186f204e0b8SIan Munsie 
187f204e0b8SIan Munsie 	if (work.flags & CXL_START_WORK_AMR)
188f204e0b8SIan Munsie 		amr = work.amr & mfspr(SPRN_UAMOR);
189f204e0b8SIan Munsie 
190b1db5513SChristophe Lombard 	if (work.flags & CXL_START_WORK_TID)
191b1db5513SChristophe Lombard 		ctx->assign_tidr = true;
192b1db5513SChristophe Lombard 
193d9232a3dSIan Munsie 	ctx->mmio_err_ff = !!(work.flags & CXL_START_WORK_ERR_FF);
194d9232a3dSIan Munsie 
195f204e0b8SIan Munsie 	/*
196a05b82d5SVaibhav Jain 	 * Increment the mapped context count for adapter. This also checks
197a05b82d5SVaibhav Jain 	 * if adapter_context_lock is taken.
198a05b82d5SVaibhav Jain 	 */
199a05b82d5SVaibhav Jain 	rc = cxl_adapter_context_get(ctx->afu->adapter);
200a05b82d5SVaibhav Jain 	if (rc) {
201a05b82d5SVaibhav Jain 		afu_release_irqs(ctx, ctx);
202a05b82d5SVaibhav Jain 		goto out;
203a05b82d5SVaibhav Jain 	}
204a05b82d5SVaibhav Jain 
205a05b82d5SVaibhav Jain 	/*
206f204e0b8SIan Munsie 	 * We grab the PID here and not in the file open to allow for the case
207f204e0b8SIan Munsie 	 * where a process (master, some daemon, etc) has opened the chardev on
208f204e0b8SIan Munsie 	 * behalf of another process, so the AFU's mm gets bound to the process
209f204e0b8SIan Munsie 	 * that performs this ioctl and not the process that opened the file.
2107b8ad495SVaibhav Jain 	 * Also we grab the PID of the group leader so that if the task that
2117b8ad495SVaibhav Jain 	 * has performed the attach operation exits the mm context of the
2127b8ad495SVaibhav Jain 	 * process is still accessible.
213f204e0b8SIan Munsie 	 */
2147b8ad495SVaibhav Jain 	ctx->pid = get_task_pid(current, PIDTYPE_PID);
215f204e0b8SIan Munsie 
2166dd2d234SChristophe Lombard 	/* acquire a reference to the task's mm */
2176dd2d234SChristophe Lombard 	ctx->mm = get_task_mm(current);
2186dd2d234SChristophe Lombard 
2196dd2d234SChristophe Lombard 	/* ensure this mm_struct can't be freed */
2206dd2d234SChristophe Lombard 	cxl_context_mm_count_get(ctx);
2216dd2d234SChristophe Lombard 
22203b8abedSFrederic Barrat 	if (ctx->mm) {
22303b8abedSFrederic Barrat 		/* decrement the use count from above */
2246dd2d234SChristophe Lombard 		mmput(ctx->mm);
22503b8abedSFrederic Barrat 		/* make TLBIs for this context global */
22603b8abedSFrederic Barrat 		mm_context_add_copro(ctx->mm);
22703b8abedSFrederic Barrat 	}
22870b565bbSVaibhav Jain 
229197267d0SFrederic Barrat 	/*
230197267d0SFrederic Barrat 	 * Increment driver use count. Enables global TLBIs for hash
231197267d0SFrederic Barrat 	 * and callbacks to handle the segment table
232197267d0SFrederic Barrat 	 */
233197267d0SFrederic Barrat 	cxl_ctx_get();
234197267d0SFrederic Barrat 
23503b8abedSFrederic Barrat 	/*
23603b8abedSFrederic Barrat 	 * A barrier is needed to make sure all TLBIs are global
23703b8abedSFrederic Barrat 	 * before we attach and the context starts being used by the
23803b8abedSFrederic Barrat 	 * adapter.
23903b8abedSFrederic Barrat 	 *
24003b8abedSFrederic Barrat 	 * Needed after mm_context_add_copro() for radix and
24103b8abedSFrederic Barrat 	 * cxl_ctx_get() for hash/p8.
24203b8abedSFrederic Barrat 	 *
24303b8abedSFrederic Barrat 	 * The barrier should really be mb(), since it involves a
24403b8abedSFrederic Barrat 	 * device. However, it's only useful when we have local
24503b8abedSFrederic Barrat 	 * vs. global TLBIs, i.e SMP=y. So keep smp_mb().
24603b8abedSFrederic Barrat 	 */
24703b8abedSFrederic Barrat 	smp_mb();
24803b8abedSFrederic Barrat 
2499bcf28cdSIan Munsie 	trace_cxl_attach(ctx, work.work_element_descriptor, work.num_interrupts, amr);
2509bcf28cdSIan Munsie 
2515be587b1SFrederic Barrat 	if ((rc = cxl_ops->attach_process(ctx, false, work.work_element_descriptor,
252456295e2SIan Munsie 							amr))) {
2536428832aSMichael Neuling 		afu_release_irqs(ctx, ctx);
25470b565bbSVaibhav Jain 		cxl_adapter_context_put(ctx->afu->adapter);
255a05b82d5SVaibhav Jain 		put_pid(ctx->pid);
2566dd2d234SChristophe Lombard 		ctx->pid = NULL;
257197267d0SFrederic Barrat 		cxl_ctx_put();
2586dd2d234SChristophe Lombard 		cxl_context_mm_count_put(ctx);
25903b8abedSFrederic Barrat 		if (ctx->mm)
26003b8abedSFrederic Barrat 			mm_context_remove_copro(ctx->mm);
261f204e0b8SIan Munsie 		goto out;
262456295e2SIan Munsie 	}
263f204e0b8SIan Munsie 
264f204e0b8SIan Munsie 	rc = 0;
265b1db5513SChristophe Lombard 	if (work.flags & CXL_START_WORK_TID) {
266b1db5513SChristophe Lombard 		work.tid = ctx->tidr;
267b1db5513SChristophe Lombard 		if (copy_to_user(uwork, &work, sizeof(work)))
268b1db5513SChristophe Lombard 			rc = -EFAULT;
269b1db5513SChristophe Lombard 	}
270b1db5513SChristophe Lombard 
271b1db5513SChristophe Lombard 	ctx->status = STARTED;
272b1db5513SChristophe Lombard 
273f204e0b8SIan Munsie out:
274f204e0b8SIan Munsie 	mutex_unlock(&ctx->status_mutex);
275f204e0b8SIan Munsie 	return rc;
276f204e0b8SIan Munsie }
2775be587b1SFrederic Barrat 
afu_ioctl_process_element(struct cxl_context * ctx,int __user * upe)278f204e0b8SIan Munsie static long afu_ioctl_process_element(struct cxl_context *ctx,
279f204e0b8SIan Munsie 				      int __user *upe)
280f204e0b8SIan Munsie {
281f204e0b8SIan Munsie 	pr_devel("%s: pe: %i\n", __func__, ctx->pe);
282f204e0b8SIan Munsie 
28314baf4d9SChristophe Lombard 	if (copy_to_user(upe, &ctx->external_pe, sizeof(__u32)))
284f204e0b8SIan Munsie 		return -EFAULT;
285f204e0b8SIan Munsie 
286f204e0b8SIan Munsie 	return 0;
287f204e0b8SIan Munsie }
288f204e0b8SIan Munsie 
afu_ioctl_get_afu_id(struct cxl_context * ctx,struct cxl_afu_id __user * upafuid)28927d4dc71SVaibhav Jain static long afu_ioctl_get_afu_id(struct cxl_context *ctx,
29027d4dc71SVaibhav Jain 				 struct cxl_afu_id __user *upafuid)
29127d4dc71SVaibhav Jain {
29227d4dc71SVaibhav Jain 	struct cxl_afu_id afuid = { 0 };
29327d4dc71SVaibhav Jain 
29427d4dc71SVaibhav Jain 	afuid.card_id = ctx->afu->adapter->adapter_num;
29527d4dc71SVaibhav Jain 	afuid.afu_offset = ctx->afu->slice;
29627d4dc71SVaibhav Jain 	afuid.afu_mode = ctx->afu->current_mode;
29727d4dc71SVaibhav Jain 
29827d4dc71SVaibhav Jain 	/* set the flag bit in case the afu is a slave */
29927d4dc71SVaibhav Jain 	if (ctx->afu->current_mode == CXL_MODE_DIRECTED && !ctx->master)
30027d4dc71SVaibhav Jain 		afuid.flags |= CXL_AFUID_FLAG_SLAVE;
30127d4dc71SVaibhav Jain 
30227d4dc71SVaibhav Jain 	if (copy_to_user(upafuid, &afuid, sizeof(afuid)))
30327d4dc71SVaibhav Jain 		return -EFAULT;
30427d4dc71SVaibhav Jain 
30527d4dc71SVaibhav Jain 	return 0;
30627d4dc71SVaibhav Jain }
30727d4dc71SVaibhav Jain 
afu_ioctl(struct file * file,unsigned int cmd,unsigned long arg)3080520336aSMichael Neuling long afu_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
309f204e0b8SIan Munsie {
310f204e0b8SIan Munsie 	struct cxl_context *ctx = file->private_data;
311f204e0b8SIan Munsie 
312f204e0b8SIan Munsie 	if (ctx->status == CLOSED)
313f204e0b8SIan Munsie 		return -EIO;
314f204e0b8SIan Munsie 
3150d400f77SChristophe Lombard 	if (!cxl_ops->link_ok(ctx->afu->adapter, ctx->afu))
3160b3f9c75SDaniel Axtens 		return -EIO;
3170b3f9c75SDaniel Axtens 
318f204e0b8SIan Munsie 	pr_devel("afu_ioctl\n");
319f204e0b8SIan Munsie 	switch (cmd) {
320f204e0b8SIan Munsie 	case CXL_IOCTL_START_WORK:
321f204e0b8SIan Munsie 		return afu_ioctl_start_work(ctx, (struct cxl_ioctl_start_work __user *)arg);
322f204e0b8SIan Munsie 	case CXL_IOCTL_GET_PROCESS_ELEMENT:
323f204e0b8SIan Munsie 		return afu_ioctl_process_element(ctx, (__u32 __user *)arg);
32427d4dc71SVaibhav Jain 	case CXL_IOCTL_GET_AFU_ID:
32527d4dc71SVaibhav Jain 		return afu_ioctl_get_afu_id(ctx, (struct cxl_afu_id __user *)
32627d4dc71SVaibhav Jain 					    arg);
327f204e0b8SIan Munsie 	}
328f204e0b8SIan Munsie 	return -EINVAL;
329f204e0b8SIan Munsie }
330f204e0b8SIan Munsie 
afu_compat_ioctl(struct file * file,unsigned int cmd,unsigned long arg)3313d6b040eSDaniel Axtens static long afu_compat_ioctl(struct file *file, unsigned int cmd,
332f204e0b8SIan Munsie 			     unsigned long arg)
333f204e0b8SIan Munsie {
334f204e0b8SIan Munsie 	return afu_ioctl(file, cmd, arg);
335f204e0b8SIan Munsie }
336f204e0b8SIan Munsie 
afu_mmap(struct file * file,struct vm_area_struct * vm)3370520336aSMichael Neuling int afu_mmap(struct file *file, struct vm_area_struct *vm)
338f204e0b8SIan Munsie {
339f204e0b8SIan Munsie 	struct cxl_context *ctx = file->private_data;
340f204e0b8SIan Munsie 
341f204e0b8SIan Munsie 	/* AFU must be started before we can MMIO */
342f204e0b8SIan Munsie 	if (ctx->status != STARTED)
343f204e0b8SIan Munsie 		return -EIO;
344f204e0b8SIan Munsie 
3450d400f77SChristophe Lombard 	if (!cxl_ops->link_ok(ctx->afu->adapter, ctx->afu))
3460b3f9c75SDaniel Axtens 		return -EIO;
3470b3f9c75SDaniel Axtens 
348f204e0b8SIan Munsie 	return cxl_context_iomap(ctx, vm);
349f204e0b8SIan Munsie }
350f204e0b8SIan Munsie 
ctx_event_pending(struct cxl_context * ctx)351b810253bSPhilippe Bergheaud static inline bool ctx_event_pending(struct cxl_context *ctx)
352b810253bSPhilippe Bergheaud {
353b810253bSPhilippe Bergheaud 	if (ctx->pending_irq || ctx->pending_fault || ctx->pending_afu_err)
354b810253bSPhilippe Bergheaud 		return true;
355b810253bSPhilippe Bergheaud 
356b810253bSPhilippe Bergheaud 	if (ctx->afu_driver_ops && atomic_read(&ctx->afu_driver_events))
357b810253bSPhilippe Bergheaud 		return true;
358b810253bSPhilippe Bergheaud 
359b810253bSPhilippe Bergheaud 	return false;
360b810253bSPhilippe Bergheaud }
361b810253bSPhilippe Bergheaud 
afu_poll(struct file * file,struct poll_table_struct * poll)362afc9a42bSAl Viro __poll_t afu_poll(struct file *file, struct poll_table_struct *poll)
363f204e0b8SIan Munsie {
364f204e0b8SIan Munsie 	struct cxl_context *ctx = file->private_data;
365afc9a42bSAl Viro 	__poll_t mask = 0;
366f204e0b8SIan Munsie 	unsigned long flags;
367f204e0b8SIan Munsie 
368f204e0b8SIan Munsie 
369f204e0b8SIan Munsie 	poll_wait(file, &ctx->wq, poll);
370f204e0b8SIan Munsie 
371f204e0b8SIan Munsie 	pr_devel("afu_poll wait done pe: %i\n", ctx->pe);
372f204e0b8SIan Munsie 
373f204e0b8SIan Munsie 	spin_lock_irqsave(&ctx->lock, flags);
374b810253bSPhilippe Bergheaud 	if (ctx_event_pending(ctx))
375a9a08845SLinus Torvalds 		mask |= EPOLLIN | EPOLLRDNORM;
376f204e0b8SIan Munsie 	else if (ctx->status == CLOSED)
377f204e0b8SIan Munsie 		/* Only error on closed when there are no futher events pending
378f204e0b8SIan Munsie 		 */
379a9a08845SLinus Torvalds 		mask |= EPOLLERR;
380f204e0b8SIan Munsie 	spin_unlock_irqrestore(&ctx->lock, flags);
381f204e0b8SIan Munsie 
382f204e0b8SIan Munsie 	pr_devel("afu_poll pe: %i returning %#x\n", ctx->pe, mask);
383f204e0b8SIan Munsie 
384f204e0b8SIan Munsie 	return mask;
385f204e0b8SIan Munsie }
386f204e0b8SIan Munsie 
afu_driver_event_copy(struct cxl_context * ctx,char __user * buf,struct cxl_event * event,struct cxl_event_afu_driver_reserved * pl)387b810253bSPhilippe Bergheaud static ssize_t afu_driver_event_copy(struct cxl_context *ctx,
388b810253bSPhilippe Bergheaud 				     char __user *buf,
389b810253bSPhilippe Bergheaud 				     struct cxl_event *event,
390b810253bSPhilippe Bergheaud 				     struct cxl_event_afu_driver_reserved *pl)
391f204e0b8SIan Munsie {
392b810253bSPhilippe Bergheaud 	/* Check event */
393b810253bSPhilippe Bergheaud 	if (!pl) {
394b810253bSPhilippe Bergheaud 		ctx->afu_driver_ops->event_delivered(ctx, pl, -EINVAL);
395b810253bSPhilippe Bergheaud 		return -EFAULT;
396b810253bSPhilippe Bergheaud 	}
397b810253bSPhilippe Bergheaud 
398b810253bSPhilippe Bergheaud 	/* Check event size */
399b810253bSPhilippe Bergheaud 	event->header.size += pl->data_size;
400b810253bSPhilippe Bergheaud 	if (event->header.size > CXL_READ_MIN_SIZE) {
401b810253bSPhilippe Bergheaud 		ctx->afu_driver_ops->event_delivered(ctx, pl, -EINVAL);
402b810253bSPhilippe Bergheaud 		return -EFAULT;
403b810253bSPhilippe Bergheaud 	}
404b810253bSPhilippe Bergheaud 
405b810253bSPhilippe Bergheaud 	/* Copy event header */
406b810253bSPhilippe Bergheaud 	if (copy_to_user(buf, event, sizeof(struct cxl_event_header))) {
407b810253bSPhilippe Bergheaud 		ctx->afu_driver_ops->event_delivered(ctx, pl, -EFAULT);
408b810253bSPhilippe Bergheaud 		return -EFAULT;
409b810253bSPhilippe Bergheaud 	}
410b810253bSPhilippe Bergheaud 
411b810253bSPhilippe Bergheaud 	/* Copy event data */
412b810253bSPhilippe Bergheaud 	buf += sizeof(struct cxl_event_header);
413b810253bSPhilippe Bergheaud 	if (copy_to_user(buf, &pl->data, pl->data_size)) {
414b810253bSPhilippe Bergheaud 		ctx->afu_driver_ops->event_delivered(ctx, pl, -EFAULT);
415b810253bSPhilippe Bergheaud 		return -EFAULT;
416b810253bSPhilippe Bergheaud 	}
417b810253bSPhilippe Bergheaud 
418b810253bSPhilippe Bergheaud 	ctx->afu_driver_ops->event_delivered(ctx, pl, 0); /* Success */
419b810253bSPhilippe Bergheaud 	return event->header.size;
420f204e0b8SIan Munsie }
421f204e0b8SIan Munsie 
afu_read(struct file * file,char __user * buf,size_t count,loff_t * off)4220520336aSMichael Neuling ssize_t afu_read(struct file *file, char __user *buf, size_t count,
423f204e0b8SIan Munsie 			loff_t *off)
424f204e0b8SIan Munsie {
425f204e0b8SIan Munsie 	struct cxl_context *ctx = file->private_data;
426b810253bSPhilippe Bergheaud 	struct cxl_event_afu_driver_reserved *pl = NULL;
427f204e0b8SIan Munsie 	struct cxl_event event;
428f204e0b8SIan Munsie 	unsigned long flags;
429d53ba6b3SIan Munsie 	int rc;
430f204e0b8SIan Munsie 	DEFINE_WAIT(wait);
431f204e0b8SIan Munsie 
4320d400f77SChristophe Lombard 	if (!cxl_ops->link_ok(ctx->afu->adapter, ctx->afu))
4330b3f9c75SDaniel Axtens 		return -EIO;
4340b3f9c75SDaniel Axtens 
435f204e0b8SIan Munsie 	if (count < CXL_READ_MIN_SIZE)
436f204e0b8SIan Munsie 		return -EINVAL;
437f204e0b8SIan Munsie 
438f204e0b8SIan Munsie 	spin_lock_irqsave(&ctx->lock, flags);
439f204e0b8SIan Munsie 
440f204e0b8SIan Munsie 	for (;;) {
441f204e0b8SIan Munsie 		prepare_to_wait(&ctx->wq, &wait, TASK_INTERRUPTIBLE);
442b810253bSPhilippe Bergheaud 		if (ctx_event_pending(ctx) || (ctx->status == CLOSED))
443f204e0b8SIan Munsie 			break;
444f204e0b8SIan Munsie 
4450d400f77SChristophe Lombard 		if (!cxl_ops->link_ok(ctx->afu->adapter, ctx->afu)) {
4460b3f9c75SDaniel Axtens 			rc = -EIO;
4470b3f9c75SDaniel Axtens 			goto out;
4480b3f9c75SDaniel Axtens 		}
4490b3f9c75SDaniel Axtens 
450d53ba6b3SIan Munsie 		if (file->f_flags & O_NONBLOCK) {
451d53ba6b3SIan Munsie 			rc = -EAGAIN;
452d53ba6b3SIan Munsie 			goto out;
453d53ba6b3SIan Munsie 		}
454d53ba6b3SIan Munsie 
455d53ba6b3SIan Munsie 		if (signal_pending(current)) {
456d53ba6b3SIan Munsie 			rc = -ERESTARTSYS;
457d53ba6b3SIan Munsie 			goto out;
458d53ba6b3SIan Munsie 		}
459d53ba6b3SIan Munsie 
460f204e0b8SIan Munsie 		spin_unlock_irqrestore(&ctx->lock, flags);
461f204e0b8SIan Munsie 		pr_devel("afu_read going to sleep...\n");
462f204e0b8SIan Munsie 		schedule();
463f204e0b8SIan Munsie 		pr_devel("afu_read woken up\n");
464f204e0b8SIan Munsie 		spin_lock_irqsave(&ctx->lock, flags);
465f204e0b8SIan Munsie 	}
466f204e0b8SIan Munsie 
467f204e0b8SIan Munsie 	finish_wait(&ctx->wq, &wait);
468f204e0b8SIan Munsie 
469f204e0b8SIan Munsie 	memset(&event, 0, sizeof(event));
470f204e0b8SIan Munsie 	event.header.process_element = ctx->pe;
471f204e0b8SIan Munsie 	event.header.size = sizeof(struct cxl_event_header);
472b810253bSPhilippe Bergheaud 	if (ctx->afu_driver_ops && atomic_read(&ctx->afu_driver_events)) {
473b810253bSPhilippe Bergheaud 		pr_devel("afu_read delivering AFU driver specific event\n");
474b810253bSPhilippe Bergheaud 		pl = ctx->afu_driver_ops->fetch_event(ctx);
475b810253bSPhilippe Bergheaud 		atomic_dec(&ctx->afu_driver_events);
476b810253bSPhilippe Bergheaud 		event.header.type = CXL_EVENT_AFU_DRIVER;
477b810253bSPhilippe Bergheaud 	} else if (ctx->pending_irq) {
478f204e0b8SIan Munsie 		pr_devel("afu_read delivering AFU interrupt\n");
479f204e0b8SIan Munsie 		event.header.size += sizeof(struct cxl_event_afu_interrupt);
480f204e0b8SIan Munsie 		event.header.type = CXL_EVENT_AFU_INTERRUPT;
481f204e0b8SIan Munsie 		event.irq.irq = find_first_bit(ctx->irq_bitmap, ctx->irq_count) + 1;
482f204e0b8SIan Munsie 		clear_bit(event.irq.irq - 1, ctx->irq_bitmap);
483f204e0b8SIan Munsie 		if (bitmap_empty(ctx->irq_bitmap, ctx->irq_count))
484f204e0b8SIan Munsie 			ctx->pending_irq = false;
485f204e0b8SIan Munsie 	} else if (ctx->pending_fault) {
486f204e0b8SIan Munsie 		pr_devel("afu_read delivering data storage fault\n");
487f204e0b8SIan Munsie 		event.header.size += sizeof(struct cxl_event_data_storage);
488f204e0b8SIan Munsie 		event.header.type = CXL_EVENT_DATA_STORAGE;
489f204e0b8SIan Munsie 		event.fault.addr = ctx->fault_addr;
490f204e0b8SIan Munsie 		event.fault.dsisr = ctx->fault_dsisr;
491f204e0b8SIan Munsie 		ctx->pending_fault = false;
492f204e0b8SIan Munsie 	} else if (ctx->pending_afu_err) {
493f204e0b8SIan Munsie 		pr_devel("afu_read delivering afu error\n");
494f204e0b8SIan Munsie 		event.header.size += sizeof(struct cxl_event_afu_error);
495f204e0b8SIan Munsie 		event.header.type = CXL_EVENT_AFU_ERROR;
496f204e0b8SIan Munsie 		event.afu_error.error = ctx->afu_err;
497f204e0b8SIan Munsie 		ctx->pending_afu_err = false;
498f204e0b8SIan Munsie 	} else if (ctx->status == CLOSED) {
499f204e0b8SIan Munsie 		pr_devel("afu_read fatal error\n");
500f204e0b8SIan Munsie 		spin_unlock_irqrestore(&ctx->lock, flags);
501f204e0b8SIan Munsie 		return -EIO;
502f204e0b8SIan Munsie 	} else
503f204e0b8SIan Munsie 		WARN(1, "afu_read must be buggy\n");
504f204e0b8SIan Munsie 
505f204e0b8SIan Munsie 	spin_unlock_irqrestore(&ctx->lock, flags);
506f204e0b8SIan Munsie 
507b810253bSPhilippe Bergheaud 	if (event.header.type == CXL_EVENT_AFU_DRIVER)
508b810253bSPhilippe Bergheaud 		return afu_driver_event_copy(ctx, buf, &event, pl);
509b810253bSPhilippe Bergheaud 
510f204e0b8SIan Munsie 	if (copy_to_user(buf, &event, event.header.size))
511f204e0b8SIan Munsie 		return -EFAULT;
512f204e0b8SIan Munsie 	return event.header.size;
513d53ba6b3SIan Munsie 
514d53ba6b3SIan Munsie out:
515d53ba6b3SIan Munsie 	finish_wait(&ctx->wq, &wait);
516d53ba6b3SIan Munsie 	spin_unlock_irqrestore(&ctx->lock, flags);
517d53ba6b3SIan Munsie 	return rc;
518f204e0b8SIan Munsie }
519f204e0b8SIan Munsie 
5200520336aSMichael Neuling /*
5210520336aSMichael Neuling  * Note: if this is updated, we need to update api.c to patch the new ones in
5220520336aSMichael Neuling  * too
5230520336aSMichael Neuling  */
5240520336aSMichael Neuling const struct file_operations afu_fops = {
525f204e0b8SIan Munsie 	.owner		= THIS_MODULE,
526f204e0b8SIan Munsie 	.open           = afu_open,
527f204e0b8SIan Munsie 	.poll		= afu_poll,
528f204e0b8SIan Munsie 	.read		= afu_read,
529f204e0b8SIan Munsie 	.release        = afu_release,
530f204e0b8SIan Munsie 	.unlocked_ioctl = afu_ioctl,
531f204e0b8SIan Munsie 	.compat_ioctl   = afu_compat_ioctl,
532f204e0b8SIan Munsie 	.mmap           = afu_mmap,
533f204e0b8SIan Munsie };
534f204e0b8SIan Munsie 
5353d6b040eSDaniel Axtens static const struct file_operations afu_master_fops = {
536f204e0b8SIan Munsie 	.owner		= THIS_MODULE,
537f204e0b8SIan Munsie 	.open           = afu_master_open,
538f204e0b8SIan Munsie 	.poll		= afu_poll,
539f204e0b8SIan Munsie 	.read		= afu_read,
540f204e0b8SIan Munsie 	.release        = afu_release,
541f204e0b8SIan Munsie 	.unlocked_ioctl = afu_ioctl,
542f204e0b8SIan Munsie 	.compat_ioctl   = afu_compat_ioctl,
543f204e0b8SIan Munsie 	.mmap           = afu_mmap,
544f204e0b8SIan Munsie };
545f204e0b8SIan Munsie 
546f204e0b8SIan Munsie 
cxl_devnode(const struct device * dev,umode_t * mode)547fb12940fSGreg Kroah-Hartman static char *cxl_devnode(const struct device *dev, umode_t *mode)
548f204e0b8SIan Munsie {
549594ff7d0SChristophe Lombard 	if (cpu_has_feature(CPU_FTR_HVMODE) &&
550594ff7d0SChristophe Lombard 	    CXL_DEVT_IS_CARD(dev->devt)) {
551f204e0b8SIan Munsie 		/*
552f204e0b8SIan Munsie 		 * These minor numbers will eventually be used to program the
553f204e0b8SIan Munsie 		 * PSL and AFUs once we have dynamic reprogramming support
554f204e0b8SIan Munsie 		 */
555f204e0b8SIan Munsie 		return NULL;
556f204e0b8SIan Munsie 	}
557f204e0b8SIan Munsie 	return kasprintf(GFP_KERNEL, "cxl/%s", dev_name(dev));
558f204e0b8SIan Munsie }
559f204e0b8SIan Munsie 
560*d2231926SGreg Kroah-Hartman static const struct class cxl_class = {
561*d2231926SGreg Kroah-Hartman 	.name =		"cxl",
562*d2231926SGreg Kroah-Hartman 	.devnode =	cxl_devnode,
563*d2231926SGreg Kroah-Hartman };
564f204e0b8SIan Munsie 
cxl_add_chardev(struct cxl_afu * afu,dev_t devt,struct cdev * cdev,struct device ** chardev,char * postfix,char * desc,const struct file_operations * fops)565f204e0b8SIan Munsie static int cxl_add_chardev(struct cxl_afu *afu, dev_t devt, struct cdev *cdev,
566f204e0b8SIan Munsie 			   struct device **chardev, char *postfix, char *desc,
567f204e0b8SIan Munsie 			   const struct file_operations *fops)
568f204e0b8SIan Munsie {
569f204e0b8SIan Munsie 	struct device *dev;
570f204e0b8SIan Munsie 	int rc;
571f204e0b8SIan Munsie 
572f204e0b8SIan Munsie 	cdev_init(cdev, fops);
573da9db711SChristophe JAILLET 	rc = cdev_add(cdev, devt, 1);
574da9db711SChristophe JAILLET 	if (rc) {
575f204e0b8SIan Munsie 		dev_err(&afu->dev, "Unable to add %s chardev: %i\n", desc, rc);
576f204e0b8SIan Munsie 		return rc;
577f204e0b8SIan Munsie 	}
578f204e0b8SIan Munsie 
579*d2231926SGreg Kroah-Hartman 	dev = device_create(&cxl_class, &afu->dev, devt, afu,
580f204e0b8SIan Munsie 			"afu%i.%i%s", afu->adapter->adapter_num, afu->slice, postfix);
581f204e0b8SIan Munsie 	if (IS_ERR(dev)) {
582f204e0b8SIan Munsie 		rc = PTR_ERR(dev);
583da9db711SChristophe JAILLET 		dev_err(&afu->dev, "Unable to create %s chardev in sysfs: %i\n", desc, rc);
584f204e0b8SIan Munsie 		goto err;
585f204e0b8SIan Munsie 	}
586f204e0b8SIan Munsie 
587f204e0b8SIan Munsie 	*chardev = dev;
588f204e0b8SIan Munsie 
589f204e0b8SIan Munsie 	return 0;
590f204e0b8SIan Munsie err:
591f204e0b8SIan Munsie 	cdev_del(cdev);
592f204e0b8SIan Munsie 	return rc;
593f204e0b8SIan Munsie }
594f204e0b8SIan Munsie 
cxl_chardev_d_afu_add(struct cxl_afu * afu)595f204e0b8SIan Munsie int cxl_chardev_d_afu_add(struct cxl_afu *afu)
596f204e0b8SIan Munsie {
597f204e0b8SIan Munsie 	return cxl_add_chardev(afu, CXL_AFU_MKDEV_D(afu), &afu->afu_cdev_d,
598f204e0b8SIan Munsie 			       &afu->chardev_d, "d", "dedicated",
599f204e0b8SIan Munsie 			       &afu_master_fops); /* Uses master fops */
600f204e0b8SIan Munsie }
601f204e0b8SIan Munsie 
cxl_chardev_m_afu_add(struct cxl_afu * afu)602f204e0b8SIan Munsie int cxl_chardev_m_afu_add(struct cxl_afu *afu)
603f204e0b8SIan Munsie {
604f204e0b8SIan Munsie 	return cxl_add_chardev(afu, CXL_AFU_MKDEV_M(afu), &afu->afu_cdev_m,
605f204e0b8SIan Munsie 			       &afu->chardev_m, "m", "master",
606f204e0b8SIan Munsie 			       &afu_master_fops);
607f204e0b8SIan Munsie }
608f204e0b8SIan Munsie 
cxl_chardev_s_afu_add(struct cxl_afu * afu)609f204e0b8SIan Munsie int cxl_chardev_s_afu_add(struct cxl_afu *afu)
610f204e0b8SIan Munsie {
611f204e0b8SIan Munsie 	return cxl_add_chardev(afu, CXL_AFU_MKDEV_S(afu), &afu->afu_cdev_s,
612f204e0b8SIan Munsie 			       &afu->chardev_s, "s", "shared",
613f204e0b8SIan Munsie 			       &afu_fops);
614f204e0b8SIan Munsie }
615f204e0b8SIan Munsie 
cxl_chardev_afu_remove(struct cxl_afu * afu)616f204e0b8SIan Munsie void cxl_chardev_afu_remove(struct cxl_afu *afu)
617f204e0b8SIan Munsie {
618f204e0b8SIan Munsie 	if (afu->chardev_d) {
619f204e0b8SIan Munsie 		cdev_del(&afu->afu_cdev_d);
620f204e0b8SIan Munsie 		device_unregister(afu->chardev_d);
621f204e0b8SIan Munsie 		afu->chardev_d = NULL;
622f204e0b8SIan Munsie 	}
623f204e0b8SIan Munsie 	if (afu->chardev_m) {
624f204e0b8SIan Munsie 		cdev_del(&afu->afu_cdev_m);
625f204e0b8SIan Munsie 		device_unregister(afu->chardev_m);
626f204e0b8SIan Munsie 		afu->chardev_m = NULL;
627f204e0b8SIan Munsie 	}
628f204e0b8SIan Munsie 	if (afu->chardev_s) {
629f204e0b8SIan Munsie 		cdev_del(&afu->afu_cdev_s);
630f204e0b8SIan Munsie 		device_unregister(afu->chardev_s);
631f204e0b8SIan Munsie 		afu->chardev_s = NULL;
632f204e0b8SIan Munsie 	}
633f204e0b8SIan Munsie }
634f204e0b8SIan Munsie 
cxl_register_afu(struct cxl_afu * afu)635f204e0b8SIan Munsie int cxl_register_afu(struct cxl_afu *afu)
636f204e0b8SIan Munsie {
637*d2231926SGreg Kroah-Hartman 	afu->dev.class = &cxl_class;
638f204e0b8SIan Munsie 
639f204e0b8SIan Munsie 	return device_register(&afu->dev);
640f204e0b8SIan Munsie }
641f204e0b8SIan Munsie 
cxl_register_adapter(struct cxl * adapter)642f204e0b8SIan Munsie int cxl_register_adapter(struct cxl *adapter)
643f204e0b8SIan Munsie {
644*d2231926SGreg Kroah-Hartman 	adapter->dev.class = &cxl_class;
645f204e0b8SIan Munsie 
646f204e0b8SIan Munsie 	/*
647f204e0b8SIan Munsie 	 * Future: When we support dynamically reprogramming the PSL & AFU we
648f204e0b8SIan Munsie 	 * will expose the interface to do that via a chardev:
649f204e0b8SIan Munsie 	 * adapter->dev.devt = CXL_CARD_MKDEV(adapter);
650f204e0b8SIan Munsie 	 */
651f204e0b8SIan Munsie 
652f204e0b8SIan Munsie 	return device_register(&adapter->dev);
653f204e0b8SIan Munsie }
654f204e0b8SIan Munsie 
cxl_get_dev(void)655594ff7d0SChristophe Lombard dev_t cxl_get_dev(void)
656594ff7d0SChristophe Lombard {
657594ff7d0SChristophe Lombard 	return cxl_dev;
658594ff7d0SChristophe Lombard }
659594ff7d0SChristophe Lombard 
cxl_file_init(void)660f204e0b8SIan Munsie int __init cxl_file_init(void)
661f204e0b8SIan Munsie {
662f204e0b8SIan Munsie 	int rc;
663f204e0b8SIan Munsie 
664f204e0b8SIan Munsie 	/*
665f204e0b8SIan Munsie 	 * If these change we really need to update API.  Either change some
666f204e0b8SIan Munsie 	 * flags or update API version number CXL_API_VERSION.
667f204e0b8SIan Munsie 	 */
668b810253bSPhilippe Bergheaud 	BUILD_BUG_ON(CXL_API_VERSION != 3);
669f204e0b8SIan Munsie 	BUILD_BUG_ON(sizeof(struct cxl_ioctl_start_work) != 64);
670f204e0b8SIan Munsie 	BUILD_BUG_ON(sizeof(struct cxl_event_header) != 8);
671f204e0b8SIan Munsie 	BUILD_BUG_ON(sizeof(struct cxl_event_afu_interrupt) != 8);
672f204e0b8SIan Munsie 	BUILD_BUG_ON(sizeof(struct cxl_event_data_storage) != 32);
673f204e0b8SIan Munsie 	BUILD_BUG_ON(sizeof(struct cxl_event_afu_error) != 16);
674f204e0b8SIan Munsie 
675f204e0b8SIan Munsie 	if ((rc = alloc_chrdev_region(&cxl_dev, 0, CXL_NUM_MINORS, "cxl"))) {
676f204e0b8SIan Munsie 		pr_err("Unable to allocate CXL major number: %i\n", rc);
677f204e0b8SIan Munsie 		return rc;
678f204e0b8SIan Munsie 	}
679f204e0b8SIan Munsie 
680f204e0b8SIan Munsie 	pr_devel("CXL device allocated, MAJOR %i\n", MAJOR(cxl_dev));
681f204e0b8SIan Munsie 
682*d2231926SGreg Kroah-Hartman 	rc = class_register(&cxl_class);
683*d2231926SGreg Kroah-Hartman 	if (rc) {
684f204e0b8SIan Munsie 		pr_err("Unable to create CXL class\n");
685f204e0b8SIan Munsie 		goto err;
686f204e0b8SIan Munsie 	}
687f204e0b8SIan Munsie 
688f204e0b8SIan Munsie 	return 0;
689f204e0b8SIan Munsie 
690f204e0b8SIan Munsie err:
691f204e0b8SIan Munsie 	unregister_chrdev_region(cxl_dev, CXL_NUM_MINORS);
692f204e0b8SIan Munsie 	return rc;
693f204e0b8SIan Munsie }
694f204e0b8SIan Munsie 
cxl_file_exit(void)695f204e0b8SIan Munsie void cxl_file_exit(void)
696f204e0b8SIan Munsie {
697f204e0b8SIan Munsie 	unregister_chrdev_region(cxl_dev, CXL_NUM_MINORS);
698*d2231926SGreg Kroah-Hartman 	class_unregister(&cxl_class);
699f204e0b8SIan Munsie }
700