]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/misc/cxl/api.c
f02a85974e49c10c24c696001f95b93d68bc90e6
[karo-tx-linux.git] / drivers / misc / cxl / api.c
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 #include <linux/fs.h>
16 #include <asm/pnv-pci.h>
17
18 #include "cxl.h"
19
20 struct cxl_context *cxl_dev_context_init(struct pci_dev *dev)
21 {
22         struct address_space *mapping;
23         struct cxl_afu *afu;
24         struct cxl_context  *ctx;
25         int rc;
26
27         afu = cxl_pci_to_afu(dev);
28         if (IS_ERR(afu))
29                 return ERR_CAST(afu);
30
31         ctx = cxl_context_alloc();
32         if (IS_ERR(ctx)) {
33                 rc = PTR_ERR(ctx);
34                 goto err_dev;
35         }
36
37         ctx->kernelapi = true;
38
39         /*
40          * Make our own address space since we won't have one from the
41          * filesystem like the user api has, and even if we do associate a file
42          * with this context we don't want to use the global anonymous inode's
43          * address space as that can invalidate unrelated users:
44          */
45         mapping = kmalloc(sizeof(struct address_space), GFP_KERNEL);
46         if (!mapping) {
47                 rc = -ENOMEM;
48                 goto err_ctx;
49         }
50         address_space_init_once(mapping);
51
52         /* Make it a slave context.  We can promote it later? */
53         rc = cxl_context_init(ctx, afu, false, mapping);
54         if (rc)
55                 goto err_mapping;
56
57         return ctx;
58
59 err_mapping:
60         kfree(mapping);
61 err_ctx:
62         kfree(ctx);
63 err_dev:
64         return ERR_PTR(rc);
65 }
66 EXPORT_SYMBOL_GPL(cxl_dev_context_init);
67
68 struct cxl_context *cxl_get_context(struct pci_dev *dev)
69 {
70         return dev->dev.archdata.cxl_ctx;
71 }
72 EXPORT_SYMBOL_GPL(cxl_get_context);
73
74 int cxl_release_context(struct cxl_context *ctx)
75 {
76         if (ctx->status >= STARTED)
77                 return -EBUSY;
78
79         cxl_context_free(ctx);
80
81         return 0;
82 }
83 EXPORT_SYMBOL_GPL(cxl_release_context);
84
85 static irq_hw_number_t cxl_find_afu_irq(struct cxl_context *ctx, int num)
86 {
87         __u16 range;
88         int r;
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_next_msi_hwirq(struct pci_dev *pdev, struct cxl_context **ctx, int *afu_irq)
101 {
102         if (*ctx == NULL || *afu_irq == 0) {
103                 *afu_irq = 1;
104                 *ctx = cxl_get_context(pdev);
105         } else {
106                 (*afu_irq)++;
107                 if (*afu_irq > cxl_get_max_irqs_per_process(pdev)) {
108                         *ctx = list_next_entry(*ctx, extra_irq_contexts);
109                         *afu_irq = 1;
110                 }
111         }
112         return cxl_find_afu_irq(*ctx, *afu_irq);
113 }
114 /* Exported via cxl_base */
115
116 int cxl_set_priv(struct cxl_context *ctx, void *priv)
117 {
118         if (!ctx)
119                 return -EINVAL;
120
121         ctx->priv = priv;
122
123         return 0;
124 }
125 EXPORT_SYMBOL_GPL(cxl_set_priv);
126
127 void *cxl_get_priv(struct cxl_context *ctx)
128 {
129         if (!ctx)
130                 return ERR_PTR(-EINVAL);
131
132         return ctx->priv;
133 }
134 EXPORT_SYMBOL_GPL(cxl_get_priv);
135
136 int cxl_allocate_afu_irqs(struct cxl_context *ctx, int num)
137 {
138         int res;
139         irq_hw_number_t hwirq;
140
141         if (num == 0)
142                 num = ctx->afu->pp_irqs;
143         res = afu_allocate_irqs(ctx, num);
144         if (res)
145                 return res;
146
147         if (!cpu_has_feature(CPU_FTR_HVMODE)) {
148                 /* In a guest, the PSL interrupt is not multiplexed. It was
149                  * allocated above, and we need to set its handler
150                  */
151                 hwirq = cxl_find_afu_irq(ctx, 0);
152                 if (hwirq)
153                         cxl_map_irq(ctx->afu->adapter, hwirq, cxl_ops->psl_interrupt, ctx, "psl");
154         }
155
156         if (ctx->status == STARTED) {
157                 if (cxl_ops->update_ivtes)
158                         cxl_ops->update_ivtes(ctx);
159                 else WARN(1, "BUG: cxl_allocate_afu_irqs must be called prior to starting the context on this platform\n");
160         }
161
162         return res;
163 }
164 EXPORT_SYMBOL_GPL(cxl_allocate_afu_irqs);
165
166 void cxl_free_afu_irqs(struct cxl_context *ctx)
167 {
168         irq_hw_number_t hwirq;
169         unsigned int virq;
170
171         if (!cpu_has_feature(CPU_FTR_HVMODE)) {
172                 hwirq = cxl_find_afu_irq(ctx, 0);
173                 if (hwirq) {
174                         virq = irq_find_mapping(NULL, hwirq);
175                         if (virq)
176                                 cxl_unmap_irq(virq, ctx);
177                 }
178         }
179         afu_irq_name_free(ctx);
180         cxl_ops->release_irq_ranges(&ctx->irqs, ctx->afu->adapter);
181 }
182 EXPORT_SYMBOL_GPL(cxl_free_afu_irqs);
183
184 int cxl_map_afu_irq(struct cxl_context *ctx, int num,
185                     irq_handler_t handler, void *cookie, char *name)
186 {
187         irq_hw_number_t hwirq;
188
189         /*
190          * Find interrupt we are to register.
191          */
192         hwirq = cxl_find_afu_irq(ctx, num);
193         if (!hwirq)
194                 return -ENOENT;
195
196         return cxl_map_irq(ctx->afu->adapter, hwirq, handler, cookie, name);
197 }
198 EXPORT_SYMBOL_GPL(cxl_map_afu_irq);
199
200 void cxl_unmap_afu_irq(struct cxl_context *ctx, int num, void *cookie)
201 {
202         irq_hw_number_t hwirq;
203         unsigned int virq;
204
205         hwirq = cxl_find_afu_irq(ctx, num);
206         if (!hwirq)
207                 return;
208
209         virq = irq_find_mapping(NULL, hwirq);
210         if (virq)
211                 cxl_unmap_irq(virq, cookie);
212 }
213 EXPORT_SYMBOL_GPL(cxl_unmap_afu_irq);
214
215 /*
216  * Start a context
217  * Code here similar to afu_ioctl_start_work().
218  */
219 int cxl_start_context(struct cxl_context *ctx, u64 wed,
220                       struct task_struct *task)
221 {
222         int rc = 0;
223         bool kernel = true;
224
225         pr_devel("%s: pe: %i\n", __func__, ctx->pe);
226
227         mutex_lock(&ctx->status_mutex);
228         if (ctx->status == STARTED)
229                 goto out; /* already started */
230
231         if (task) {
232                 ctx->pid = get_task_pid(task, PIDTYPE_PID);
233                 ctx->glpid = get_task_pid(task->group_leader, PIDTYPE_PID);
234                 kernel = false;
235                 ctx->real_mode = false;
236         }
237
238         cxl_ctx_get();
239
240         if ((rc = cxl_ops->attach_process(ctx, kernel, wed, 0))) {
241                 put_pid(ctx->pid);
242                 cxl_ctx_put();
243                 goto out;
244         }
245
246         ctx->status = STARTED;
247 out:
248         mutex_unlock(&ctx->status_mutex);
249         return rc;
250 }
251 EXPORT_SYMBOL_GPL(cxl_start_context);
252
253 int cxl_process_element(struct cxl_context *ctx)
254 {
255         return ctx->external_pe;
256 }
257 EXPORT_SYMBOL_GPL(cxl_process_element);
258
259 /* Stop a context.  Returns 0 on success, otherwise -Errno */
260 int cxl_stop_context(struct cxl_context *ctx)
261 {
262         return __detach_context(ctx);
263 }
264 EXPORT_SYMBOL_GPL(cxl_stop_context);
265
266 void cxl_set_master(struct cxl_context *ctx)
267 {
268         ctx->master = true;
269 }
270 EXPORT_SYMBOL_GPL(cxl_set_master);
271
272 int cxl_set_translation_mode(struct cxl_context *ctx, bool real_mode)
273 {
274         if (ctx->status == STARTED) {
275                 /*
276                  * We could potentially update the PE and issue an update LLCMD
277                  * to support this, but it doesn't seem to have a good use case
278                  * since it's trivial to just create a second kernel context
279                  * with different translation modes, so until someone convinces
280                  * me otherwise:
281                  */
282                 return -EBUSY;
283         }
284
285         ctx->real_mode = real_mode;
286         return 0;
287 }
288 EXPORT_SYMBOL_GPL(cxl_set_translation_mode);
289
290 /* wrappers around afu_* file ops which are EXPORTED */
291 int cxl_fd_open(struct inode *inode, struct file *file)
292 {
293         return afu_open(inode, file);
294 }
295 EXPORT_SYMBOL_GPL(cxl_fd_open);
296 int cxl_fd_release(struct inode *inode, struct file *file)
297 {
298         return afu_release(inode, file);
299 }
300 EXPORT_SYMBOL_GPL(cxl_fd_release);
301 long cxl_fd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
302 {
303         return afu_ioctl(file, cmd, arg);
304 }
305 EXPORT_SYMBOL_GPL(cxl_fd_ioctl);
306 int cxl_fd_mmap(struct file *file, struct vm_area_struct *vm)
307 {
308         return afu_mmap(file, vm);
309 }
310 EXPORT_SYMBOL_GPL(cxl_fd_mmap);
311 unsigned int cxl_fd_poll(struct file *file, struct poll_table_struct *poll)
312 {
313         return afu_poll(file, poll);
314 }
315 EXPORT_SYMBOL_GPL(cxl_fd_poll);
316 ssize_t cxl_fd_read(struct file *file, char __user *buf, size_t count,
317                         loff_t *off)
318 {
319         return afu_read(file, buf, count, off);
320 }
321 EXPORT_SYMBOL_GPL(cxl_fd_read);
322
323 #define PATCH_FOPS(NAME) if (!fops->NAME) fops->NAME = afu_fops.NAME
324
325 /* Get a struct file and fd for a context and attach the ops */
326 struct file *cxl_get_fd(struct cxl_context *ctx, struct file_operations *fops,
327                         int *fd)
328 {
329         struct file *file;
330         int rc, flags, fdtmp;
331
332         flags = O_RDWR | O_CLOEXEC;
333
334         /* This code is similar to anon_inode_getfd() */
335         rc = get_unused_fd_flags(flags);
336         if (rc < 0)
337                 return ERR_PTR(rc);
338         fdtmp = rc;
339
340         /*
341          * Patch the file ops.  Needs to be careful that this is rentrant safe.
342          */
343         if (fops) {
344                 PATCH_FOPS(open);
345                 PATCH_FOPS(poll);
346                 PATCH_FOPS(read);
347                 PATCH_FOPS(release);
348                 PATCH_FOPS(unlocked_ioctl);
349                 PATCH_FOPS(compat_ioctl);
350                 PATCH_FOPS(mmap);
351         } else /* use default ops */
352                 fops = (struct file_operations *)&afu_fops;
353
354         file = anon_inode_getfile("cxl", fops, ctx, flags);
355         if (IS_ERR(file))
356                 goto err_fd;
357
358         file->f_mapping = ctx->mapping;
359
360         *fd = fdtmp;
361         return file;
362
363 err_fd:
364         put_unused_fd(fdtmp);
365         return NULL;
366 }
367 EXPORT_SYMBOL_GPL(cxl_get_fd);
368
369 struct cxl_context *cxl_fops_get_context(struct file *file)
370 {
371         return file->private_data;
372 }
373 EXPORT_SYMBOL_GPL(cxl_fops_get_context);
374
375 void cxl_set_driver_ops(struct cxl_context *ctx,
376                         struct cxl_afu_driver_ops *ops)
377 {
378         WARN_ON(!ops->fetch_event || !ops->event_delivered);
379         atomic_set(&ctx->afu_driver_events, 0);
380         ctx->afu_driver_ops = ops;
381 }
382 EXPORT_SYMBOL_GPL(cxl_set_driver_ops);
383
384 void cxl_context_events_pending(struct cxl_context *ctx,
385                                 unsigned int new_events)
386 {
387         atomic_add(new_events, &ctx->afu_driver_events);
388         wake_up_all(&ctx->wq);
389 }
390 EXPORT_SYMBOL_GPL(cxl_context_events_pending);
391
392 int cxl_start_work(struct cxl_context *ctx,
393                    struct cxl_ioctl_start_work *work)
394 {
395         int rc;
396
397         /* code taken from afu_ioctl_start_work */
398         if (!(work->flags & CXL_START_WORK_NUM_IRQS))
399                 work->num_interrupts = ctx->afu->pp_irqs;
400         else if ((work->num_interrupts < ctx->afu->pp_irqs) ||
401                  (work->num_interrupts > ctx->afu->irqs_max)) {
402                 return -EINVAL;
403         }
404
405         rc = afu_register_irqs(ctx, work->num_interrupts);
406         if (rc)
407                 return rc;
408
409         rc = cxl_start_context(ctx, work->work_element_descriptor, current);
410         if (rc < 0) {
411                 afu_release_irqs(ctx, ctx);
412                 return rc;
413         }
414
415         return 0;
416 }
417 EXPORT_SYMBOL_GPL(cxl_start_work);
418
419 void __iomem *cxl_psa_map(struct cxl_context *ctx)
420 {
421         if (ctx->status != STARTED)
422                 return NULL;
423
424         pr_devel("%s: psn_phys%llx size:%llx\n",
425                 __func__, ctx->psn_phys, ctx->psn_size);
426         return ioremap(ctx->psn_phys, ctx->psn_size);
427 }
428 EXPORT_SYMBOL_GPL(cxl_psa_map);
429
430 void cxl_psa_unmap(void __iomem *addr)
431 {
432         iounmap(addr);
433 }
434 EXPORT_SYMBOL_GPL(cxl_psa_unmap);
435
436 int cxl_afu_reset(struct cxl_context *ctx)
437 {
438         struct cxl_afu *afu = ctx->afu;
439         int rc;
440
441         rc = cxl_ops->afu_reset(afu);
442         if (rc)
443                 return rc;
444
445         return cxl_ops->afu_check_and_enable(afu);
446 }
447 EXPORT_SYMBOL_GPL(cxl_afu_reset);
448
449 void cxl_perst_reloads_same_image(struct cxl_afu *afu,
450                                   bool perst_reloads_same_image)
451 {
452         afu->adapter->perst_same_image = perst_reloads_same_image;
453 }
454 EXPORT_SYMBOL_GPL(cxl_perst_reloads_same_image);
455
456 ssize_t cxl_read_adapter_vpd(struct pci_dev *dev, void *buf, size_t count)
457 {
458         struct cxl_afu *afu = cxl_pci_to_afu(dev);
459         if (IS_ERR(afu))
460                 return -ENODEV;
461
462         return cxl_ops->read_adapter_vpd(afu->adapter, buf, count);
463 }
464 EXPORT_SYMBOL_GPL(cxl_read_adapter_vpd);
465
466 int cxl_set_max_irqs_per_process(struct pci_dev *dev, int irqs)
467 {
468         struct cxl_afu *afu = cxl_pci_to_afu(dev);
469         if (IS_ERR(afu))
470                 return -ENODEV;
471
472         if (irqs > afu->adapter->user_irqs)
473                 return -EINVAL;
474
475         /* Limit user_irqs to prevent the user increasing this via sysfs */
476         afu->adapter->user_irqs = irqs;
477         afu->irqs_max = irqs;
478
479         return 0;
480 }
481 EXPORT_SYMBOL_GPL(cxl_set_max_irqs_per_process);
482
483 int cxl_get_max_irqs_per_process(struct pci_dev *dev)
484 {
485         struct cxl_afu *afu = cxl_pci_to_afu(dev);
486         if (IS_ERR(afu))
487                 return -ENODEV;
488
489         return afu->irqs_max;
490 }
491 EXPORT_SYMBOL_GPL(cxl_get_max_irqs_per_process);