xref: /linux/drivers/misc/cxl/api.c (revision 13e68d8bd05c998cae452a4f3400af1e8edd852e)
16f7f0b3dSMichael Neuling /*
26f7f0b3dSMichael Neuling  * Copyright 2014 IBM Corp.
36f7f0b3dSMichael Neuling  *
46f7f0b3dSMichael Neuling  * This program is free software; you can redistribute it and/or
56f7f0b3dSMichael Neuling  * modify it under the terms of the GNU General Public License
66f7f0b3dSMichael Neuling  * as published by the Free Software Foundation; either version
76f7f0b3dSMichael Neuling  * 2 of the License, or (at your option) any later version.
86f7f0b3dSMichael Neuling  */
96f7f0b3dSMichael Neuling 
106f7f0b3dSMichael Neuling #include <linux/pci.h>
116f7f0b3dSMichael Neuling #include <linux/slab.h>
126f7f0b3dSMichael Neuling #include <linux/anon_inodes.h>
136f7f0b3dSMichael Neuling #include <linux/file.h>
146f7f0b3dSMichael Neuling #include <misc/cxl.h>
156f7f0b3dSMichael Neuling 
166f7f0b3dSMichael Neuling #include "cxl.h"
176f7f0b3dSMichael Neuling 
186f7f0b3dSMichael Neuling struct cxl_context *cxl_dev_context_init(struct pci_dev *dev)
196f7f0b3dSMichael Neuling {
206f7f0b3dSMichael Neuling 	struct cxl_afu *afu;
216f7f0b3dSMichael Neuling 	struct cxl_context  *ctx;
226f7f0b3dSMichael Neuling 	int rc;
236f7f0b3dSMichael Neuling 
246f7f0b3dSMichael Neuling 	afu = cxl_pci_to_afu(dev);
256f7f0b3dSMichael Neuling 
263f8dc44dSMichael Neuling 	get_device(&afu->dev);
276f7f0b3dSMichael Neuling 	ctx = cxl_context_alloc();
286f7f0b3dSMichael Neuling 	if (IS_ERR(ctx))
296f7f0b3dSMichael Neuling 		return ctx;
306f7f0b3dSMichael Neuling 
316f7f0b3dSMichael Neuling 	/* Make it a slave context.  We can promote it later? */
326f7f0b3dSMichael Neuling 	rc = cxl_context_init(ctx, afu, false, NULL);
336f7f0b3dSMichael Neuling 	if (rc) {
346f7f0b3dSMichael Neuling 		kfree(ctx);
353f8dc44dSMichael Neuling 		put_device(&afu->dev);
366f7f0b3dSMichael Neuling 		return ERR_PTR(-ENOMEM);
376f7f0b3dSMichael Neuling 	}
386f7f0b3dSMichael Neuling 	cxl_assign_psn_space(ctx);
396f7f0b3dSMichael Neuling 
406f7f0b3dSMichael Neuling 	return ctx;
416f7f0b3dSMichael Neuling }
426f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_dev_context_init);
436f7f0b3dSMichael Neuling 
446f7f0b3dSMichael Neuling struct cxl_context *cxl_get_context(struct pci_dev *dev)
456f7f0b3dSMichael Neuling {
466f7f0b3dSMichael Neuling 	return dev->dev.archdata.cxl_ctx;
476f7f0b3dSMichael Neuling }
486f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_get_context);
496f7f0b3dSMichael Neuling 
506f7f0b3dSMichael Neuling struct device *cxl_get_phys_dev(struct pci_dev *dev)
516f7f0b3dSMichael Neuling {
526f7f0b3dSMichael Neuling 	struct cxl_afu *afu;
536f7f0b3dSMichael Neuling 
546f7f0b3dSMichael Neuling 	afu = cxl_pci_to_afu(dev);
556f7f0b3dSMichael Neuling 
566f7f0b3dSMichael Neuling 	return afu->adapter->dev.parent;
576f7f0b3dSMichael Neuling }
586f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_get_phys_dev);
596f7f0b3dSMichael Neuling 
606f7f0b3dSMichael Neuling int cxl_release_context(struct cxl_context *ctx)
616f7f0b3dSMichael Neuling {
626f7f0b3dSMichael Neuling 	if (ctx->status != CLOSED)
636f7f0b3dSMichael Neuling 		return -EBUSY;
646f7f0b3dSMichael Neuling 
653f8dc44dSMichael Neuling 	put_device(&ctx->afu->dev);
663f8dc44dSMichael Neuling 
676f7f0b3dSMichael Neuling 	cxl_context_free(ctx);
686f7f0b3dSMichael Neuling 
696f7f0b3dSMichael Neuling 	return 0;
706f7f0b3dSMichael Neuling }
716f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_release_context);
726f7f0b3dSMichael Neuling 
736f7f0b3dSMichael Neuling int cxl_allocate_afu_irqs(struct cxl_context *ctx, int num)
746f7f0b3dSMichael Neuling {
756f7f0b3dSMichael Neuling 	if (num == 0)
766f7f0b3dSMichael Neuling 		num = ctx->afu->pp_irqs;
776f7f0b3dSMichael Neuling 	return afu_allocate_irqs(ctx, num);
786f7f0b3dSMichael Neuling }
796f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_allocate_afu_irqs);
806f7f0b3dSMichael Neuling 
816f7f0b3dSMichael Neuling void cxl_free_afu_irqs(struct cxl_context *ctx)
826f7f0b3dSMichael Neuling {
836f7f0b3dSMichael Neuling 	cxl_release_irq_ranges(&ctx->irqs, ctx->afu->adapter);
846f7f0b3dSMichael Neuling }
856f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_free_afu_irqs);
866f7f0b3dSMichael Neuling 
876f7f0b3dSMichael Neuling static irq_hw_number_t cxl_find_afu_irq(struct cxl_context *ctx, int num)
886f7f0b3dSMichael Neuling {
896f7f0b3dSMichael Neuling 	__u16 range;
906f7f0b3dSMichael Neuling 	int r;
916f7f0b3dSMichael Neuling 
926f7f0b3dSMichael Neuling 	WARN_ON(num == 0);
936f7f0b3dSMichael Neuling 
946f7f0b3dSMichael Neuling 	for (r = 0; r < CXL_IRQ_RANGES; r++) {
956f7f0b3dSMichael Neuling 		range = ctx->irqs.range[r];
966f7f0b3dSMichael Neuling 		if (num < range) {
976f7f0b3dSMichael Neuling 			return ctx->irqs.offset[r] + num;
986f7f0b3dSMichael Neuling 		}
996f7f0b3dSMichael Neuling 		num -= range;
1006f7f0b3dSMichael Neuling 	}
1016f7f0b3dSMichael Neuling 	return 0;
1026f7f0b3dSMichael Neuling }
1036f7f0b3dSMichael Neuling 
1046f7f0b3dSMichael Neuling int cxl_map_afu_irq(struct cxl_context *ctx, int num,
1056f7f0b3dSMichael Neuling 		    irq_handler_t handler, void *cookie, char *name)
1066f7f0b3dSMichael Neuling {
1076f7f0b3dSMichael Neuling 	irq_hw_number_t hwirq;
1086f7f0b3dSMichael Neuling 
1096f7f0b3dSMichael Neuling 	/*
1106f7f0b3dSMichael Neuling 	 * Find interrupt we are to register.
1116f7f0b3dSMichael Neuling 	 */
1126f7f0b3dSMichael Neuling 	hwirq = cxl_find_afu_irq(ctx, num);
1136f7f0b3dSMichael Neuling 	if (!hwirq)
1146f7f0b3dSMichael Neuling 		return -ENOENT;
1156f7f0b3dSMichael Neuling 
1166f7f0b3dSMichael Neuling 	return cxl_map_irq(ctx->afu->adapter, hwirq, handler, cookie, name);
1176f7f0b3dSMichael Neuling }
1186f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_map_afu_irq);
1196f7f0b3dSMichael Neuling 
1206f7f0b3dSMichael Neuling void cxl_unmap_afu_irq(struct cxl_context *ctx, int num, void *cookie)
1216f7f0b3dSMichael Neuling {
1226f7f0b3dSMichael Neuling 	irq_hw_number_t hwirq;
1236f7f0b3dSMichael Neuling 	unsigned int virq;
1246f7f0b3dSMichael Neuling 
1256f7f0b3dSMichael Neuling 	hwirq = cxl_find_afu_irq(ctx, num);
1266f7f0b3dSMichael Neuling 	if (!hwirq)
1276f7f0b3dSMichael Neuling 		return;
1286f7f0b3dSMichael Neuling 
1296f7f0b3dSMichael Neuling 	virq = irq_find_mapping(NULL, hwirq);
1306f7f0b3dSMichael Neuling 	if (virq)
1316f7f0b3dSMichael Neuling 		cxl_unmap_irq(virq, cookie);
1326f7f0b3dSMichael Neuling }
1336f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_unmap_afu_irq);
1346f7f0b3dSMichael Neuling 
1356f7f0b3dSMichael Neuling /*
1366f7f0b3dSMichael Neuling  * Start a context
1376f7f0b3dSMichael Neuling  * Code here similar to afu_ioctl_start_work().
1386f7f0b3dSMichael Neuling  */
1396f7f0b3dSMichael Neuling int cxl_start_context(struct cxl_context *ctx, u64 wed,
1406f7f0b3dSMichael Neuling 		      struct task_struct *task)
1416f7f0b3dSMichael Neuling {
1426f7f0b3dSMichael Neuling 	int rc = 0;
1436f7f0b3dSMichael Neuling 	bool kernel = true;
1446f7f0b3dSMichael Neuling 
1456f7f0b3dSMichael Neuling 	pr_devel("%s: pe: %i\n", __func__, ctx->pe);
1466f7f0b3dSMichael Neuling 
1476f7f0b3dSMichael Neuling 	mutex_lock(&ctx->status_mutex);
1486f7f0b3dSMichael Neuling 	if (ctx->status == STARTED)
1496f7f0b3dSMichael Neuling 		goto out; /* already started */
1506f7f0b3dSMichael Neuling 
1516f7f0b3dSMichael Neuling 	if (task) {
1526f7f0b3dSMichael Neuling 		ctx->pid = get_task_pid(task, PIDTYPE_PID);
1536f7f0b3dSMichael Neuling 		get_pid(ctx->pid);
1546f7f0b3dSMichael Neuling 		kernel = false;
1556f7f0b3dSMichael Neuling 	}
1566f7f0b3dSMichael Neuling 
1576f7f0b3dSMichael Neuling 	cxl_ctx_get();
1586f7f0b3dSMichael Neuling 
1596f7f0b3dSMichael Neuling 	if ((rc = cxl_attach_process(ctx, kernel, wed , 0))) {
1606f7f0b3dSMichael Neuling 		put_pid(ctx->pid);
1616f7f0b3dSMichael Neuling 		cxl_ctx_put();
1626f7f0b3dSMichael Neuling 		goto out;
1636f7f0b3dSMichael Neuling 	}
1646f7f0b3dSMichael Neuling 
1656f7f0b3dSMichael Neuling 	ctx->status = STARTED;
1666f7f0b3dSMichael Neuling out:
1676f7f0b3dSMichael Neuling 	mutex_unlock(&ctx->status_mutex);
1686f7f0b3dSMichael Neuling 	return rc;
1696f7f0b3dSMichael Neuling }
1706f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_start_context);
1716f7f0b3dSMichael Neuling 
1726f7f0b3dSMichael Neuling int cxl_process_element(struct cxl_context *ctx)
1736f7f0b3dSMichael Neuling {
1746f7f0b3dSMichael Neuling 	return ctx->pe;
1756f7f0b3dSMichael Neuling }
1766f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_process_element);
1776f7f0b3dSMichael Neuling 
1786f7f0b3dSMichael Neuling /* Stop a context.  Returns 0 on success, otherwise -Errno */
1796f7f0b3dSMichael Neuling int cxl_stop_context(struct cxl_context *ctx)
1806f7f0b3dSMichael Neuling {
1813f8dc44dSMichael Neuling 	return __detach_context(ctx);
1826f7f0b3dSMichael Neuling }
1836f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_stop_context);
1846f7f0b3dSMichael Neuling 
1856f7f0b3dSMichael Neuling void cxl_set_master(struct cxl_context *ctx)
1866f7f0b3dSMichael Neuling {
1876f7f0b3dSMichael Neuling 	ctx->master = true;
1886f7f0b3dSMichael Neuling 	cxl_assign_psn_space(ctx);
1896f7f0b3dSMichael Neuling }
1906f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_set_master);
1916f7f0b3dSMichael Neuling 
1926f7f0b3dSMichael Neuling /* wrappers around afu_* file ops which are EXPORTED */
1936f7f0b3dSMichael Neuling int cxl_fd_open(struct inode *inode, struct file *file)
1946f7f0b3dSMichael Neuling {
1956f7f0b3dSMichael Neuling 	return afu_open(inode, file);
1966f7f0b3dSMichael Neuling }
1976f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_fd_open);
1986f7f0b3dSMichael Neuling int cxl_fd_release(struct inode *inode, struct file *file)
1996f7f0b3dSMichael Neuling {
2006f7f0b3dSMichael Neuling 	return afu_release(inode, file);
2016f7f0b3dSMichael Neuling }
2026f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_fd_release);
2036f7f0b3dSMichael Neuling long cxl_fd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
2046f7f0b3dSMichael Neuling {
2056f7f0b3dSMichael Neuling 	return afu_ioctl(file, cmd, arg);
2066f7f0b3dSMichael Neuling }
2076f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_fd_ioctl);
2086f7f0b3dSMichael Neuling int cxl_fd_mmap(struct file *file, struct vm_area_struct *vm)
2096f7f0b3dSMichael Neuling {
2106f7f0b3dSMichael Neuling 	return afu_mmap(file, vm);
2116f7f0b3dSMichael Neuling }
2126f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_fd_mmap);
2136f7f0b3dSMichael Neuling unsigned int cxl_fd_poll(struct file *file, struct poll_table_struct *poll)
2146f7f0b3dSMichael Neuling {
2156f7f0b3dSMichael Neuling 	return afu_poll(file, poll);
2166f7f0b3dSMichael Neuling }
2176f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_fd_poll);
2186f7f0b3dSMichael Neuling ssize_t cxl_fd_read(struct file *file, char __user *buf, size_t count,
2196f7f0b3dSMichael Neuling 			loff_t *off)
2206f7f0b3dSMichael Neuling {
2216f7f0b3dSMichael Neuling 	return afu_read(file, buf, count, off);
2226f7f0b3dSMichael Neuling }
2236f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_fd_read);
2246f7f0b3dSMichael Neuling 
2256f7f0b3dSMichael Neuling #define PATCH_FOPS(NAME) if (!fops->NAME) fops->NAME = afu_fops.NAME
2266f7f0b3dSMichael Neuling 
2276f7f0b3dSMichael Neuling /* Get a struct file and fd for a context and attach the ops */
2286f7f0b3dSMichael Neuling struct file *cxl_get_fd(struct cxl_context *ctx, struct file_operations *fops,
2296f7f0b3dSMichael Neuling 			int *fd)
2306f7f0b3dSMichael Neuling {
2316f7f0b3dSMichael Neuling 	struct file *file;
2326f7f0b3dSMichael Neuling 	int rc, flags, fdtmp;
2336f7f0b3dSMichael Neuling 
2346f7f0b3dSMichael Neuling 	flags = O_RDWR | O_CLOEXEC;
2356f7f0b3dSMichael Neuling 
2366f7f0b3dSMichael Neuling 	/* This code is similar to anon_inode_getfd() */
2376f7f0b3dSMichael Neuling 	rc = get_unused_fd_flags(flags);
2386f7f0b3dSMichael Neuling 	if (rc < 0)
2396f7f0b3dSMichael Neuling 		return ERR_PTR(rc);
2406f7f0b3dSMichael Neuling 	fdtmp = rc;
2416f7f0b3dSMichael Neuling 
2426f7f0b3dSMichael Neuling 	/*
2436f7f0b3dSMichael Neuling 	 * Patch the file ops.  Needs to be careful that this is rentrant safe.
2446f7f0b3dSMichael Neuling 	 */
2456f7f0b3dSMichael Neuling 	if (fops) {
2466f7f0b3dSMichael Neuling 		PATCH_FOPS(open);
2476f7f0b3dSMichael Neuling 		PATCH_FOPS(poll);
2486f7f0b3dSMichael Neuling 		PATCH_FOPS(read);
2496f7f0b3dSMichael Neuling 		PATCH_FOPS(release);
2506f7f0b3dSMichael Neuling 		PATCH_FOPS(unlocked_ioctl);
2516f7f0b3dSMichael Neuling 		PATCH_FOPS(compat_ioctl);
2526f7f0b3dSMichael Neuling 		PATCH_FOPS(mmap);
2536f7f0b3dSMichael Neuling 	} else /* use default ops */
2546f7f0b3dSMichael Neuling 		fops = (struct file_operations *)&afu_fops;
2556f7f0b3dSMichael Neuling 
2566f7f0b3dSMichael Neuling 	file = anon_inode_getfile("cxl", fops, ctx, flags);
2576f7f0b3dSMichael Neuling 	if (IS_ERR(file))
2586f7f0b3dSMichael Neuling 		put_unused_fd(fdtmp);
2596f7f0b3dSMichael Neuling 	*fd = fdtmp;
2606f7f0b3dSMichael Neuling 	return file;
2616f7f0b3dSMichael Neuling }
2626f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_get_fd);
2636f7f0b3dSMichael Neuling 
2646f7f0b3dSMichael Neuling struct cxl_context *cxl_fops_get_context(struct file *file)
2656f7f0b3dSMichael Neuling {
2666f7f0b3dSMichael Neuling 	return file->private_data;
2676f7f0b3dSMichael Neuling }
2686f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_fops_get_context);
2696f7f0b3dSMichael Neuling 
2706f7f0b3dSMichael Neuling int cxl_start_work(struct cxl_context *ctx,
2716f7f0b3dSMichael Neuling 		   struct cxl_ioctl_start_work *work)
2726f7f0b3dSMichael Neuling {
2736f7f0b3dSMichael Neuling 	int rc;
2746f7f0b3dSMichael Neuling 
2756f7f0b3dSMichael Neuling 	/* code taken from afu_ioctl_start_work */
2766f7f0b3dSMichael Neuling 	if (!(work->flags & CXL_START_WORK_NUM_IRQS))
2776f7f0b3dSMichael Neuling 		work->num_interrupts = ctx->afu->pp_irqs;
2786f7f0b3dSMichael Neuling 	else if ((work->num_interrupts < ctx->afu->pp_irqs) ||
2796f7f0b3dSMichael Neuling 		 (work->num_interrupts > ctx->afu->irqs_max)) {
2806f7f0b3dSMichael Neuling 		return -EINVAL;
2816f7f0b3dSMichael Neuling 	}
2826f7f0b3dSMichael Neuling 
2836f7f0b3dSMichael Neuling 	rc = afu_register_irqs(ctx, work->num_interrupts);
2846f7f0b3dSMichael Neuling 	if (rc)
2856f7f0b3dSMichael Neuling 		return rc;
2866f7f0b3dSMichael Neuling 
2876f7f0b3dSMichael Neuling 	rc = cxl_start_context(ctx, work->work_element_descriptor, current);
2886f7f0b3dSMichael Neuling 	if (rc < 0) {
2896f7f0b3dSMichael Neuling 		afu_release_irqs(ctx, ctx);
2906f7f0b3dSMichael Neuling 		return rc;
2916f7f0b3dSMichael Neuling 	}
2926f7f0b3dSMichael Neuling 
2936f7f0b3dSMichael Neuling 	return 0;
2946f7f0b3dSMichael Neuling }
2956f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_start_work);
2966f7f0b3dSMichael Neuling 
2976f7f0b3dSMichael Neuling void __iomem *cxl_psa_map(struct cxl_context *ctx)
2986f7f0b3dSMichael Neuling {
2996f7f0b3dSMichael Neuling 	struct cxl_afu *afu = ctx->afu;
3006f7f0b3dSMichael Neuling 	int rc;
3016f7f0b3dSMichael Neuling 
3026f7f0b3dSMichael Neuling 	rc = cxl_afu_check_and_enable(afu);
3036f7f0b3dSMichael Neuling 	if (rc)
3046f7f0b3dSMichael Neuling 		return NULL;
3056f7f0b3dSMichael Neuling 
3066f7f0b3dSMichael Neuling 	pr_devel("%s: psn_phys%llx size:%llx\n",
3076f7f0b3dSMichael Neuling 		 __func__, afu->psn_phys, afu->adapter->ps_size);
3086f7f0b3dSMichael Neuling 	return ioremap(ctx->psn_phys, ctx->psn_size);
3096f7f0b3dSMichael Neuling }
3106f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_psa_map);
3116f7f0b3dSMichael Neuling 
3126f7f0b3dSMichael Neuling void cxl_psa_unmap(void __iomem *addr)
3136f7f0b3dSMichael Neuling {
3146f7f0b3dSMichael Neuling 	iounmap(addr);
3156f7f0b3dSMichael Neuling }
3166f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_psa_unmap);
3176f7f0b3dSMichael Neuling 
3186f7f0b3dSMichael Neuling int cxl_afu_reset(struct cxl_context *ctx)
3196f7f0b3dSMichael Neuling {
3206f7f0b3dSMichael Neuling 	struct cxl_afu *afu = ctx->afu;
3216f7f0b3dSMichael Neuling 	int rc;
3226f7f0b3dSMichael Neuling 
3236f7f0b3dSMichael Neuling 	rc = __cxl_afu_reset(afu);
3246f7f0b3dSMichael Neuling 	if (rc)
3256f7f0b3dSMichael Neuling 		return rc;
3266f7f0b3dSMichael Neuling 
3276f7f0b3dSMichael Neuling 	return cxl_afu_check_and_enable(afu);
3286f7f0b3dSMichael Neuling }
3296f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_afu_reset);
330*13e68d8bSDaniel Axtens 
331*13e68d8bSDaniel Axtens void cxl_perst_reloads_same_image(struct cxl_afu *afu,
332*13e68d8bSDaniel Axtens 				  bool perst_reloads_same_image)
333*13e68d8bSDaniel Axtens {
334*13e68d8bSDaniel Axtens 	afu->adapter->perst_same_image = perst_reloads_same_image;
335*13e68d8bSDaniel Axtens }
336*13e68d8bSDaniel Axtens EXPORT_SYMBOL_GPL(cxl_perst_reloads_same_image);
337