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