xref: /linux/drivers/misc/cxl/api.c (revision 005438a8eef063495ac059d128eea71b58de50e5)
1 /*
2  * Copyright 2014 IBM Corp.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version
7  * 2 of the License, or (at your option) any later version.
8  */
9 
10 #include <linux/pci.h>
11 #include <linux/slab.h>
12 #include <linux/anon_inodes.h>
13 #include <linux/file.h>
14 #include <misc/cxl.h>
15 
16 #include "cxl.h"
17 
18 struct cxl_context *cxl_dev_context_init(struct pci_dev *dev)
19 {
20 	struct cxl_afu *afu;
21 	struct cxl_context  *ctx;
22 	int rc;
23 
24 	afu = cxl_pci_to_afu(dev);
25 
26 	ctx = cxl_context_alloc();
27 	if (IS_ERR(ctx))
28 		return ctx;
29 
30 	/* Make it a slave context.  We can promote it later? */
31 	rc = cxl_context_init(ctx, afu, false, NULL);
32 	if (rc) {
33 		kfree(ctx);
34 		return ERR_PTR(-ENOMEM);
35 	}
36 	cxl_assign_psn_space(ctx);
37 
38 	return ctx;
39 }
40 EXPORT_SYMBOL_GPL(cxl_dev_context_init);
41 
42 struct cxl_context *cxl_get_context(struct pci_dev *dev)
43 {
44 	return dev->dev.archdata.cxl_ctx;
45 }
46 EXPORT_SYMBOL_GPL(cxl_get_context);
47 
48 struct device *cxl_get_phys_dev(struct pci_dev *dev)
49 {
50 	struct cxl_afu *afu;
51 
52 	afu = cxl_pci_to_afu(dev);
53 
54 	return afu->adapter->dev.parent;
55 }
56 EXPORT_SYMBOL_GPL(cxl_get_phys_dev);
57 
58 int cxl_release_context(struct cxl_context *ctx)
59 {
60 	if (ctx->status != CLOSED)
61 		return -EBUSY;
62 
63 	cxl_context_free(ctx);
64 
65 	return 0;
66 }
67 EXPORT_SYMBOL_GPL(cxl_release_context);
68 
69 int cxl_allocate_afu_irqs(struct cxl_context *ctx, int num)
70 {
71 	if (num == 0)
72 		num = ctx->afu->pp_irqs;
73 	return afu_allocate_irqs(ctx, num);
74 }
75 EXPORT_SYMBOL_GPL(cxl_allocate_afu_irqs);
76 
77 void cxl_free_afu_irqs(struct cxl_context *ctx)
78 {
79 	cxl_release_irq_ranges(&ctx->irqs, ctx->afu->adapter);
80 }
81 EXPORT_SYMBOL_GPL(cxl_free_afu_irqs);
82 
83 static irq_hw_number_t cxl_find_afu_irq(struct cxl_context *ctx, int num)
84 {
85 	__u16 range;
86 	int r;
87 
88 	WARN_ON(num == 0);
89 
90 	for (r = 0; r < CXL_IRQ_RANGES; r++) {
91 		range = ctx->irqs.range[r];
92 		if (num < range) {
93 			return ctx->irqs.offset[r] + num;
94 		}
95 		num -= range;
96 	}
97 	return 0;
98 }
99 
100 int cxl_map_afu_irq(struct cxl_context *ctx, int num,
101 		    irq_handler_t handler, void *cookie, char *name)
102 {
103 	irq_hw_number_t hwirq;
104 
105 	/*
106 	 * Find interrupt we are to register.
107 	 */
108 	hwirq = cxl_find_afu_irq(ctx, num);
109 	if (!hwirq)
110 		return -ENOENT;
111 
112 	return cxl_map_irq(ctx->afu->adapter, hwirq, handler, cookie, name);
113 }
114 EXPORT_SYMBOL_GPL(cxl_map_afu_irq);
115 
116 void cxl_unmap_afu_irq(struct cxl_context *ctx, int num, void *cookie)
117 {
118 	irq_hw_number_t hwirq;
119 	unsigned int virq;
120 
121 	hwirq = cxl_find_afu_irq(ctx, num);
122 	if (!hwirq)
123 		return;
124 
125 	virq = irq_find_mapping(NULL, hwirq);
126 	if (virq)
127 		cxl_unmap_irq(virq, cookie);
128 }
129 EXPORT_SYMBOL_GPL(cxl_unmap_afu_irq);
130 
131 /*
132  * Start a context
133  * Code here similar to afu_ioctl_start_work().
134  */
135 int cxl_start_context(struct cxl_context *ctx, u64 wed,
136 		      struct task_struct *task)
137 {
138 	int rc = 0;
139 	bool kernel = true;
140 
141 	pr_devel("%s: pe: %i\n", __func__, ctx->pe);
142 
143 	mutex_lock(&ctx->status_mutex);
144 	if (ctx->status == STARTED)
145 		goto out; /* already started */
146 
147 	if (task) {
148 		ctx->pid = get_task_pid(task, PIDTYPE_PID);
149 		get_pid(ctx->pid);
150 		kernel = false;
151 	}
152 
153 	cxl_ctx_get();
154 
155 	if ((rc = cxl_attach_process(ctx, kernel, wed , 0))) {
156 		put_pid(ctx->pid);
157 		cxl_ctx_put();
158 		goto out;
159 	}
160 
161 	ctx->status = STARTED;
162 	get_device(&ctx->afu->dev);
163 out:
164 	mutex_unlock(&ctx->status_mutex);
165 	return rc;
166 }
167 EXPORT_SYMBOL_GPL(cxl_start_context);
168 
169 int cxl_process_element(struct cxl_context *ctx)
170 {
171 	return ctx->pe;
172 }
173 EXPORT_SYMBOL_GPL(cxl_process_element);
174 
175 /* Stop a context.  Returns 0 on success, otherwise -Errno */
176 int cxl_stop_context(struct cxl_context *ctx)
177 {
178 	int rc;
179 
180 	rc = __detach_context(ctx);
181 	if (!rc)
182 		put_device(&ctx->afu->dev);
183 	return rc;
184 }
185 EXPORT_SYMBOL_GPL(cxl_stop_context);
186 
187 void cxl_set_master(struct cxl_context *ctx)
188 {
189 	ctx->master = true;
190 	cxl_assign_psn_space(ctx);
191 }
192 EXPORT_SYMBOL_GPL(cxl_set_master);
193 
194 /* wrappers around afu_* file ops which are EXPORTED */
195 int cxl_fd_open(struct inode *inode, struct file *file)
196 {
197 	return afu_open(inode, file);
198 }
199 EXPORT_SYMBOL_GPL(cxl_fd_open);
200 int cxl_fd_release(struct inode *inode, struct file *file)
201 {
202 	return afu_release(inode, file);
203 }
204 EXPORT_SYMBOL_GPL(cxl_fd_release);
205 long cxl_fd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
206 {
207 	return afu_ioctl(file, cmd, arg);
208 }
209 EXPORT_SYMBOL_GPL(cxl_fd_ioctl);
210 int cxl_fd_mmap(struct file *file, struct vm_area_struct *vm)
211 {
212 	return afu_mmap(file, vm);
213 }
214 EXPORT_SYMBOL_GPL(cxl_fd_mmap);
215 unsigned int cxl_fd_poll(struct file *file, struct poll_table_struct *poll)
216 {
217 	return afu_poll(file, poll);
218 }
219 EXPORT_SYMBOL_GPL(cxl_fd_poll);
220 ssize_t cxl_fd_read(struct file *file, char __user *buf, size_t count,
221 			loff_t *off)
222 {
223 	return afu_read(file, buf, count, off);
224 }
225 EXPORT_SYMBOL_GPL(cxl_fd_read);
226 
227 #define PATCH_FOPS(NAME) if (!fops->NAME) fops->NAME = afu_fops.NAME
228 
229 /* Get a struct file and fd for a context and attach the ops */
230 struct file *cxl_get_fd(struct cxl_context *ctx, struct file_operations *fops,
231 			int *fd)
232 {
233 	struct file *file;
234 	int rc, flags, fdtmp;
235 
236 	flags = O_RDWR | O_CLOEXEC;
237 
238 	/* This code is similar to anon_inode_getfd() */
239 	rc = get_unused_fd_flags(flags);
240 	if (rc < 0)
241 		return ERR_PTR(rc);
242 	fdtmp = rc;
243 
244 	/*
245 	 * Patch the file ops.  Needs to be careful that this is rentrant safe.
246 	 */
247 	if (fops) {
248 		PATCH_FOPS(open);
249 		PATCH_FOPS(poll);
250 		PATCH_FOPS(read);
251 		PATCH_FOPS(release);
252 		PATCH_FOPS(unlocked_ioctl);
253 		PATCH_FOPS(compat_ioctl);
254 		PATCH_FOPS(mmap);
255 	} else /* use default ops */
256 		fops = (struct file_operations *)&afu_fops;
257 
258 	file = anon_inode_getfile("cxl", fops, ctx, flags);
259 	if (IS_ERR(file))
260 		put_unused_fd(fdtmp);
261 	*fd = fdtmp;
262 	return file;
263 }
264 EXPORT_SYMBOL_GPL(cxl_get_fd);
265 
266 struct cxl_context *cxl_fops_get_context(struct file *file)
267 {
268 	return file->private_data;
269 }
270 EXPORT_SYMBOL_GPL(cxl_fops_get_context);
271 
272 int cxl_start_work(struct cxl_context *ctx,
273 		   struct cxl_ioctl_start_work *work)
274 {
275 	int rc;
276 
277 	/* code taken from afu_ioctl_start_work */
278 	if (!(work->flags & CXL_START_WORK_NUM_IRQS))
279 		work->num_interrupts = ctx->afu->pp_irqs;
280 	else if ((work->num_interrupts < ctx->afu->pp_irqs) ||
281 		 (work->num_interrupts > ctx->afu->irqs_max)) {
282 		return -EINVAL;
283 	}
284 
285 	rc = afu_register_irqs(ctx, work->num_interrupts);
286 	if (rc)
287 		return rc;
288 
289 	rc = cxl_start_context(ctx, work->work_element_descriptor, current);
290 	if (rc < 0) {
291 		afu_release_irqs(ctx, ctx);
292 		return rc;
293 	}
294 
295 	return 0;
296 }
297 EXPORT_SYMBOL_GPL(cxl_start_work);
298 
299 void __iomem *cxl_psa_map(struct cxl_context *ctx)
300 {
301 	struct cxl_afu *afu = ctx->afu;
302 	int rc;
303 
304 	rc = cxl_afu_check_and_enable(afu);
305 	if (rc)
306 		return NULL;
307 
308 	pr_devel("%s: psn_phys%llx size:%llx\n",
309 		 __func__, afu->psn_phys, afu->adapter->ps_size);
310 	return ioremap(ctx->psn_phys, ctx->psn_size);
311 }
312 EXPORT_SYMBOL_GPL(cxl_psa_map);
313 
314 void cxl_psa_unmap(void __iomem *addr)
315 {
316 	iounmap(addr);
317 }
318 EXPORT_SYMBOL_GPL(cxl_psa_unmap);
319 
320 int cxl_afu_reset(struct cxl_context *ctx)
321 {
322 	struct cxl_afu *afu = ctx->afu;
323 	int rc;
324 
325 	rc = __cxl_afu_reset(afu);
326 	if (rc)
327 		return rc;
328 
329 	return cxl_afu_check_and_enable(afu);
330 }
331 EXPORT_SYMBOL_GPL(cxl_afu_reset);
332