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