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> 1555e07668SIan Munsie #include <linux/fs.h> 166f7f0b3dSMichael Neuling 176f7f0b3dSMichael Neuling #include "cxl.h" 186f7f0b3dSMichael Neuling 196f7f0b3dSMichael Neuling struct cxl_context *cxl_dev_context_init(struct pci_dev *dev) 206f7f0b3dSMichael Neuling { 2155e07668SIan Munsie struct address_space *mapping; 226f7f0b3dSMichael Neuling struct cxl_afu *afu; 236f7f0b3dSMichael Neuling struct cxl_context *ctx; 246f7f0b3dSMichael Neuling int rc; 256f7f0b3dSMichael Neuling 266f7f0b3dSMichael Neuling afu = cxl_pci_to_afu(dev); 276f7f0b3dSMichael Neuling 286f7f0b3dSMichael Neuling ctx = cxl_context_alloc(); 29af2a50bbSIan Munsie if (IS_ERR(ctx)) { 30af2a50bbSIan Munsie rc = PTR_ERR(ctx); 31af2a50bbSIan Munsie goto err_dev; 32af2a50bbSIan Munsie } 336f7f0b3dSMichael Neuling 3455e07668SIan Munsie ctx->kernelapi = true; 3555e07668SIan Munsie 3655e07668SIan Munsie /* 3755e07668SIan Munsie * Make our own address space since we won't have one from the 3855e07668SIan Munsie * filesystem like the user api has, and even if we do associate a file 3955e07668SIan Munsie * with this context we don't want to use the global anonymous inode's 4055e07668SIan Munsie * address space as that can invalidate unrelated users: 4155e07668SIan Munsie */ 4255e07668SIan Munsie mapping = kmalloc(sizeof(struct address_space), GFP_KERNEL); 4355e07668SIan Munsie if (!mapping) { 4455e07668SIan Munsie rc = -ENOMEM; 45af2a50bbSIan Munsie goto err_ctx; 4655e07668SIan Munsie } 4755e07668SIan Munsie address_space_init_once(mapping); 4855e07668SIan Munsie 4955e07668SIan Munsie /* Make it a slave context. We can promote it later? */ 5055e07668SIan Munsie rc = cxl_context_init(ctx, afu, false, mapping); 5155e07668SIan Munsie if (rc) 5255e07668SIan Munsie goto err_mapping; 5355e07668SIan Munsie 546f7f0b3dSMichael Neuling return ctx; 55af2a50bbSIan Munsie 5655e07668SIan Munsie err_mapping: 5755e07668SIan Munsie kfree(mapping); 58af2a50bbSIan Munsie err_ctx: 59af2a50bbSIan Munsie kfree(ctx); 60af2a50bbSIan Munsie err_dev: 61af2a50bbSIan Munsie return ERR_PTR(rc); 626f7f0b3dSMichael Neuling } 636f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_dev_context_init); 646f7f0b3dSMichael Neuling 656f7f0b3dSMichael Neuling struct cxl_context *cxl_get_context(struct pci_dev *dev) 666f7f0b3dSMichael Neuling { 676f7f0b3dSMichael Neuling return dev->dev.archdata.cxl_ctx; 686f7f0b3dSMichael Neuling } 696f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_get_context); 706f7f0b3dSMichael Neuling 716f7f0b3dSMichael Neuling struct device *cxl_get_phys_dev(struct pci_dev *dev) 726f7f0b3dSMichael Neuling { 736f7f0b3dSMichael Neuling struct cxl_afu *afu; 746f7f0b3dSMichael Neuling 756f7f0b3dSMichael Neuling afu = cxl_pci_to_afu(dev); 766f7f0b3dSMichael Neuling 776f7f0b3dSMichael Neuling return afu->adapter->dev.parent; 786f7f0b3dSMichael Neuling } 796f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_get_phys_dev); 806f7f0b3dSMichael Neuling 816f7f0b3dSMichael Neuling int cxl_release_context(struct cxl_context *ctx) 826f7f0b3dSMichael Neuling { 837c26b9cfSAndrew Donnellan if (ctx->status >= STARTED) 846f7f0b3dSMichael Neuling return -EBUSY; 856f7f0b3dSMichael Neuling 866f7f0b3dSMichael Neuling cxl_context_free(ctx); 876f7f0b3dSMichael Neuling 886f7f0b3dSMichael Neuling return 0; 896f7f0b3dSMichael Neuling } 906f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_release_context); 916f7f0b3dSMichael Neuling 926f7f0b3dSMichael Neuling int cxl_allocate_afu_irqs(struct cxl_context *ctx, int num) 936f7f0b3dSMichael Neuling { 946f7f0b3dSMichael Neuling if (num == 0) 956f7f0b3dSMichael Neuling num = ctx->afu->pp_irqs; 966f7f0b3dSMichael Neuling return afu_allocate_irqs(ctx, num); 976f7f0b3dSMichael Neuling } 986f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_allocate_afu_irqs); 996f7f0b3dSMichael Neuling 1006f7f0b3dSMichael Neuling void cxl_free_afu_irqs(struct cxl_context *ctx) 1016f7f0b3dSMichael Neuling { 1028dde152eSAndrew Donnellan afu_irq_name_free(ctx); 1035be587b1SFrederic Barrat cxl_ops->release_irq_ranges(&ctx->irqs, ctx->afu->adapter); 1046f7f0b3dSMichael Neuling } 1056f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_free_afu_irqs); 1066f7f0b3dSMichael Neuling 1076f7f0b3dSMichael Neuling static irq_hw_number_t cxl_find_afu_irq(struct cxl_context *ctx, int num) 1086f7f0b3dSMichael Neuling { 1096f7f0b3dSMichael Neuling __u16 range; 1106f7f0b3dSMichael Neuling int r; 1116f7f0b3dSMichael Neuling 1126f7f0b3dSMichael Neuling WARN_ON(num == 0); 1136f7f0b3dSMichael Neuling 1146f7f0b3dSMichael Neuling for (r = 0; r < CXL_IRQ_RANGES; r++) { 1156f7f0b3dSMichael Neuling range = ctx->irqs.range[r]; 1166f7f0b3dSMichael Neuling if (num < range) { 1176f7f0b3dSMichael Neuling return ctx->irqs.offset[r] + num; 1186f7f0b3dSMichael Neuling } 1196f7f0b3dSMichael Neuling num -= range; 1206f7f0b3dSMichael Neuling } 1216f7f0b3dSMichael Neuling return 0; 1226f7f0b3dSMichael Neuling } 1236f7f0b3dSMichael Neuling 1246f7f0b3dSMichael Neuling int cxl_map_afu_irq(struct cxl_context *ctx, int num, 1256f7f0b3dSMichael Neuling irq_handler_t handler, void *cookie, char *name) 1266f7f0b3dSMichael Neuling { 1276f7f0b3dSMichael Neuling irq_hw_number_t hwirq; 1286f7f0b3dSMichael Neuling 1296f7f0b3dSMichael Neuling /* 1306f7f0b3dSMichael Neuling * Find interrupt we are to register. 1316f7f0b3dSMichael Neuling */ 1326f7f0b3dSMichael Neuling hwirq = cxl_find_afu_irq(ctx, num); 1336f7f0b3dSMichael Neuling if (!hwirq) 1346f7f0b3dSMichael Neuling return -ENOENT; 1356f7f0b3dSMichael Neuling 1366f7f0b3dSMichael Neuling return cxl_map_irq(ctx->afu->adapter, hwirq, handler, cookie, name); 1376f7f0b3dSMichael Neuling } 1386f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_map_afu_irq); 1396f7f0b3dSMichael Neuling 1406f7f0b3dSMichael Neuling void cxl_unmap_afu_irq(struct cxl_context *ctx, int num, void *cookie) 1416f7f0b3dSMichael Neuling { 1426f7f0b3dSMichael Neuling irq_hw_number_t hwirq; 1436f7f0b3dSMichael Neuling unsigned int virq; 1446f7f0b3dSMichael Neuling 1456f7f0b3dSMichael Neuling hwirq = cxl_find_afu_irq(ctx, num); 1466f7f0b3dSMichael Neuling if (!hwirq) 1476f7f0b3dSMichael Neuling return; 1486f7f0b3dSMichael Neuling 1496f7f0b3dSMichael Neuling virq = irq_find_mapping(NULL, hwirq); 1506f7f0b3dSMichael Neuling if (virq) 1516f7f0b3dSMichael Neuling cxl_unmap_irq(virq, cookie); 1526f7f0b3dSMichael Neuling } 1536f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_unmap_afu_irq); 1546f7f0b3dSMichael Neuling 1556f7f0b3dSMichael Neuling /* 1566f7f0b3dSMichael Neuling * Start a context 1576f7f0b3dSMichael Neuling * Code here similar to afu_ioctl_start_work(). 1586f7f0b3dSMichael Neuling */ 1596f7f0b3dSMichael Neuling int cxl_start_context(struct cxl_context *ctx, u64 wed, 1606f7f0b3dSMichael Neuling struct task_struct *task) 1616f7f0b3dSMichael Neuling { 1626f7f0b3dSMichael Neuling int rc = 0; 1636f7f0b3dSMichael Neuling bool kernel = true; 1646f7f0b3dSMichael Neuling 1656f7f0b3dSMichael Neuling pr_devel("%s: pe: %i\n", __func__, ctx->pe); 1666f7f0b3dSMichael Neuling 1676f7f0b3dSMichael Neuling mutex_lock(&ctx->status_mutex); 1686f7f0b3dSMichael Neuling if (ctx->status == STARTED) 1696f7f0b3dSMichael Neuling goto out; /* already started */ 1706f7f0b3dSMichael Neuling 1716f7f0b3dSMichael Neuling if (task) { 1726f7f0b3dSMichael Neuling ctx->pid = get_task_pid(task, PIDTYPE_PID); 1737b8ad495SVaibhav Jain ctx->glpid = get_task_pid(task->group_leader, PIDTYPE_PID); 1746f7f0b3dSMichael Neuling kernel = false; 1756f7f0b3dSMichael Neuling } 1766f7f0b3dSMichael Neuling 1776f7f0b3dSMichael Neuling cxl_ctx_get(); 1786f7f0b3dSMichael Neuling 1795be587b1SFrederic Barrat if ((rc = cxl_ops->attach_process(ctx, kernel, wed, 0))) { 1806f7f0b3dSMichael Neuling put_pid(ctx->pid); 1816f7f0b3dSMichael Neuling cxl_ctx_put(); 1826f7f0b3dSMichael Neuling goto out; 1836f7f0b3dSMichael Neuling } 1846f7f0b3dSMichael Neuling 1856f7f0b3dSMichael Neuling ctx->status = STARTED; 1866f7f0b3dSMichael Neuling out: 1876f7f0b3dSMichael Neuling mutex_unlock(&ctx->status_mutex); 1886f7f0b3dSMichael Neuling return rc; 1896f7f0b3dSMichael Neuling } 1906f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_start_context); 1916f7f0b3dSMichael Neuling 1926f7f0b3dSMichael Neuling int cxl_process_element(struct cxl_context *ctx) 1936f7f0b3dSMichael Neuling { 194*14baf4d9SChristophe Lombard return ctx->external_pe; 1956f7f0b3dSMichael Neuling } 1966f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_process_element); 1976f7f0b3dSMichael Neuling 1986f7f0b3dSMichael Neuling /* Stop a context. Returns 0 on success, otherwise -Errno */ 1996f7f0b3dSMichael Neuling int cxl_stop_context(struct cxl_context *ctx) 2006f7f0b3dSMichael Neuling { 2013f8dc44dSMichael Neuling return __detach_context(ctx); 2026f7f0b3dSMichael Neuling } 2036f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_stop_context); 2046f7f0b3dSMichael Neuling 2056f7f0b3dSMichael Neuling void cxl_set_master(struct cxl_context *ctx) 2066f7f0b3dSMichael Neuling { 2076f7f0b3dSMichael Neuling ctx->master = true; 2086f7f0b3dSMichael Neuling } 2096f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_set_master); 2106f7f0b3dSMichael Neuling 2116f7f0b3dSMichael Neuling /* wrappers around afu_* file ops which are EXPORTED */ 2126f7f0b3dSMichael Neuling int cxl_fd_open(struct inode *inode, struct file *file) 2136f7f0b3dSMichael Neuling { 2146f7f0b3dSMichael Neuling return afu_open(inode, file); 2156f7f0b3dSMichael Neuling } 2166f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_fd_open); 2176f7f0b3dSMichael Neuling int cxl_fd_release(struct inode *inode, struct file *file) 2186f7f0b3dSMichael Neuling { 2196f7f0b3dSMichael Neuling return afu_release(inode, file); 2206f7f0b3dSMichael Neuling } 2216f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_fd_release); 2226f7f0b3dSMichael Neuling long cxl_fd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 2236f7f0b3dSMichael Neuling { 2246f7f0b3dSMichael Neuling return afu_ioctl(file, cmd, arg); 2256f7f0b3dSMichael Neuling } 2266f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_fd_ioctl); 2276f7f0b3dSMichael Neuling int cxl_fd_mmap(struct file *file, struct vm_area_struct *vm) 2286f7f0b3dSMichael Neuling { 2296f7f0b3dSMichael Neuling return afu_mmap(file, vm); 2306f7f0b3dSMichael Neuling } 2316f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_fd_mmap); 2326f7f0b3dSMichael Neuling unsigned int cxl_fd_poll(struct file *file, struct poll_table_struct *poll) 2336f7f0b3dSMichael Neuling { 2346f7f0b3dSMichael Neuling return afu_poll(file, poll); 2356f7f0b3dSMichael Neuling } 2366f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_fd_poll); 2376f7f0b3dSMichael Neuling ssize_t cxl_fd_read(struct file *file, char __user *buf, size_t count, 2386f7f0b3dSMichael Neuling loff_t *off) 2396f7f0b3dSMichael Neuling { 2406f7f0b3dSMichael Neuling return afu_read(file, buf, count, off); 2416f7f0b3dSMichael Neuling } 2426f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_fd_read); 2436f7f0b3dSMichael Neuling 2446f7f0b3dSMichael Neuling #define PATCH_FOPS(NAME) if (!fops->NAME) fops->NAME = afu_fops.NAME 2456f7f0b3dSMichael Neuling 2466f7f0b3dSMichael Neuling /* Get a struct file and fd for a context and attach the ops */ 2476f7f0b3dSMichael Neuling struct file *cxl_get_fd(struct cxl_context *ctx, struct file_operations *fops, 2486f7f0b3dSMichael Neuling int *fd) 2496f7f0b3dSMichael Neuling { 2506f7f0b3dSMichael Neuling struct file *file; 2516f7f0b3dSMichael Neuling int rc, flags, fdtmp; 2526f7f0b3dSMichael Neuling 2536f7f0b3dSMichael Neuling flags = O_RDWR | O_CLOEXEC; 2546f7f0b3dSMichael Neuling 2556f7f0b3dSMichael Neuling /* This code is similar to anon_inode_getfd() */ 2566f7f0b3dSMichael Neuling rc = get_unused_fd_flags(flags); 2576f7f0b3dSMichael Neuling if (rc < 0) 2586f7f0b3dSMichael Neuling return ERR_PTR(rc); 2596f7f0b3dSMichael Neuling fdtmp = rc; 2606f7f0b3dSMichael Neuling 2616f7f0b3dSMichael Neuling /* 2626f7f0b3dSMichael Neuling * Patch the file ops. Needs to be careful that this is rentrant safe. 2636f7f0b3dSMichael Neuling */ 2646f7f0b3dSMichael Neuling if (fops) { 2656f7f0b3dSMichael Neuling PATCH_FOPS(open); 2666f7f0b3dSMichael Neuling PATCH_FOPS(poll); 2676f7f0b3dSMichael Neuling PATCH_FOPS(read); 2686f7f0b3dSMichael Neuling PATCH_FOPS(release); 2696f7f0b3dSMichael Neuling PATCH_FOPS(unlocked_ioctl); 2706f7f0b3dSMichael Neuling PATCH_FOPS(compat_ioctl); 2716f7f0b3dSMichael Neuling PATCH_FOPS(mmap); 2726f7f0b3dSMichael Neuling } else /* use default ops */ 2736f7f0b3dSMichael Neuling fops = (struct file_operations *)&afu_fops; 2746f7f0b3dSMichael Neuling 2756f7f0b3dSMichael Neuling file = anon_inode_getfile("cxl", fops, ctx, flags); 2766f7f0b3dSMichael Neuling if (IS_ERR(file)) 27755e07668SIan Munsie goto err_fd; 27855e07668SIan Munsie 27955e07668SIan Munsie file->f_mapping = ctx->mapping; 28055e07668SIan Munsie 2816f7f0b3dSMichael Neuling *fd = fdtmp; 2826f7f0b3dSMichael Neuling return file; 28355e07668SIan Munsie 28455e07668SIan Munsie err_fd: 28555e07668SIan Munsie put_unused_fd(fdtmp); 28655e07668SIan Munsie return NULL; 2876f7f0b3dSMichael Neuling } 2886f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_get_fd); 2896f7f0b3dSMichael Neuling 2906f7f0b3dSMichael Neuling struct cxl_context *cxl_fops_get_context(struct file *file) 2916f7f0b3dSMichael Neuling { 2926f7f0b3dSMichael Neuling return file->private_data; 2936f7f0b3dSMichael Neuling } 2946f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_fops_get_context); 2956f7f0b3dSMichael Neuling 2966f7f0b3dSMichael Neuling int cxl_start_work(struct cxl_context *ctx, 2976f7f0b3dSMichael Neuling struct cxl_ioctl_start_work *work) 2986f7f0b3dSMichael Neuling { 2996f7f0b3dSMichael Neuling int rc; 3006f7f0b3dSMichael Neuling 3016f7f0b3dSMichael Neuling /* code taken from afu_ioctl_start_work */ 3026f7f0b3dSMichael Neuling if (!(work->flags & CXL_START_WORK_NUM_IRQS)) 3036f7f0b3dSMichael Neuling work->num_interrupts = ctx->afu->pp_irqs; 3046f7f0b3dSMichael Neuling else if ((work->num_interrupts < ctx->afu->pp_irqs) || 3056f7f0b3dSMichael Neuling (work->num_interrupts > ctx->afu->irqs_max)) { 3066f7f0b3dSMichael Neuling return -EINVAL; 3076f7f0b3dSMichael Neuling } 3086f7f0b3dSMichael Neuling 3096f7f0b3dSMichael Neuling rc = afu_register_irqs(ctx, work->num_interrupts); 3106f7f0b3dSMichael Neuling if (rc) 3116f7f0b3dSMichael Neuling return rc; 3126f7f0b3dSMichael Neuling 3136f7f0b3dSMichael Neuling rc = cxl_start_context(ctx, work->work_element_descriptor, current); 3146f7f0b3dSMichael Neuling if (rc < 0) { 3156f7f0b3dSMichael Neuling afu_release_irqs(ctx, ctx); 3166f7f0b3dSMichael Neuling return rc; 3176f7f0b3dSMichael Neuling } 3186f7f0b3dSMichael Neuling 3196f7f0b3dSMichael Neuling return 0; 3206f7f0b3dSMichael Neuling } 3216f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_start_work); 3226f7f0b3dSMichael Neuling 3236f7f0b3dSMichael Neuling void __iomem *cxl_psa_map(struct cxl_context *ctx) 3246f7f0b3dSMichael Neuling { 325cca44c01SFrederic Barrat if (ctx->status != STARTED) 3266f7f0b3dSMichael Neuling return NULL; 3276f7f0b3dSMichael Neuling 3286f7f0b3dSMichael Neuling pr_devel("%s: psn_phys%llx size:%llx\n", 329cca44c01SFrederic Barrat __func__, ctx->psn_phys, ctx->psn_size); 3306f7f0b3dSMichael Neuling return ioremap(ctx->psn_phys, ctx->psn_size); 3316f7f0b3dSMichael Neuling } 3326f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_psa_map); 3336f7f0b3dSMichael Neuling 3346f7f0b3dSMichael Neuling void cxl_psa_unmap(void __iomem *addr) 3356f7f0b3dSMichael Neuling { 3366f7f0b3dSMichael Neuling iounmap(addr); 3376f7f0b3dSMichael Neuling } 3386f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_psa_unmap); 3396f7f0b3dSMichael Neuling 3406f7f0b3dSMichael Neuling int cxl_afu_reset(struct cxl_context *ctx) 3416f7f0b3dSMichael Neuling { 3426f7f0b3dSMichael Neuling struct cxl_afu *afu = ctx->afu; 3436f7f0b3dSMichael Neuling int rc; 3446f7f0b3dSMichael Neuling 3455be587b1SFrederic Barrat rc = cxl_ops->afu_reset(afu); 3466f7f0b3dSMichael Neuling if (rc) 3476f7f0b3dSMichael Neuling return rc; 3486f7f0b3dSMichael Neuling 3495be587b1SFrederic Barrat return cxl_ops->afu_check_and_enable(afu); 3506f7f0b3dSMichael Neuling } 3516f7f0b3dSMichael Neuling EXPORT_SYMBOL_GPL(cxl_afu_reset); 35213e68d8bSDaniel Axtens 35313e68d8bSDaniel Axtens void cxl_perst_reloads_same_image(struct cxl_afu *afu, 35413e68d8bSDaniel Axtens bool perst_reloads_same_image) 35513e68d8bSDaniel Axtens { 35613e68d8bSDaniel Axtens afu->adapter->perst_same_image = perst_reloads_same_image; 35713e68d8bSDaniel Axtens } 35813e68d8bSDaniel Axtens EXPORT_SYMBOL_GPL(cxl_perst_reloads_same_image); 359