162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * CXL Flash Device Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Written by: Matthew R. Ochs <mrochs@linux.vnet.ibm.com>, IBM Corporation 662306a36Sopenharmony_ci * Uma Krishnan <ukrishn@linux.vnet.ibm.com>, IBM Corporation 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (C) 2018 IBM Corporation 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/file.h> 1262306a36Sopenharmony_ci#include <linux/idr.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/mount.h> 1562306a36Sopenharmony_ci#include <linux/pseudo_fs.h> 1662306a36Sopenharmony_ci#include <linux/poll.h> 1762306a36Sopenharmony_ci#include <linux/sched/signal.h> 1862306a36Sopenharmony_ci#include <linux/interrupt.h> 1962306a36Sopenharmony_ci#include <linux/irqdomain.h> 2062306a36Sopenharmony_ci#include <asm/xive.h> 2162306a36Sopenharmony_ci#include <misc/ocxl.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <uapi/misc/cxl.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "backend.h" 2662306a36Sopenharmony_ci#include "ocxl_hw.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * Pseudo-filesystem to allocate inodes. 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define OCXLFLASH_FS_MAGIC 0x1697698f 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic int ocxlflash_fs_cnt; 3562306a36Sopenharmony_cistatic struct vfsmount *ocxlflash_vfs_mount; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic int ocxlflash_fs_init_fs_context(struct fs_context *fc) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci return init_pseudo(fc, OCXLFLASH_FS_MAGIC) ? 0 : -ENOMEM; 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic struct file_system_type ocxlflash_fs_type = { 4362306a36Sopenharmony_ci .name = "ocxlflash", 4462306a36Sopenharmony_ci .owner = THIS_MODULE, 4562306a36Sopenharmony_ci .init_fs_context = ocxlflash_fs_init_fs_context, 4662306a36Sopenharmony_ci .kill_sb = kill_anon_super, 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* 5062306a36Sopenharmony_ci * ocxlflash_release_mapping() - release the memory mapping 5162306a36Sopenharmony_ci * @ctx: Context whose mapping is to be released. 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_cistatic void ocxlflash_release_mapping(struct ocxlflash_context *ctx) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci if (ctx->mapping) 5662306a36Sopenharmony_ci simple_release_fs(&ocxlflash_vfs_mount, &ocxlflash_fs_cnt); 5762306a36Sopenharmony_ci ctx->mapping = NULL; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* 6162306a36Sopenharmony_ci * ocxlflash_getfile() - allocate pseudo filesystem, inode, and the file 6262306a36Sopenharmony_ci * @dev: Generic device of the host. 6362306a36Sopenharmony_ci * @name: Name of the pseudo filesystem. 6462306a36Sopenharmony_ci * @fops: File operations. 6562306a36Sopenharmony_ci * @priv: Private data. 6662306a36Sopenharmony_ci * @flags: Flags for the file. 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * Return: pointer to the file on success, ERR_PTR on failure 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_cistatic struct file *ocxlflash_getfile(struct device *dev, const char *name, 7162306a36Sopenharmony_ci const struct file_operations *fops, 7262306a36Sopenharmony_ci void *priv, int flags) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct file *file; 7562306a36Sopenharmony_ci struct inode *inode; 7662306a36Sopenharmony_ci int rc; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (fops->owner && !try_module_get(fops->owner)) { 7962306a36Sopenharmony_ci dev_err(dev, "%s: Owner does not exist\n", __func__); 8062306a36Sopenharmony_ci rc = -ENOENT; 8162306a36Sopenharmony_ci goto err1; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci rc = simple_pin_fs(&ocxlflash_fs_type, &ocxlflash_vfs_mount, 8562306a36Sopenharmony_ci &ocxlflash_fs_cnt); 8662306a36Sopenharmony_ci if (unlikely(rc < 0)) { 8762306a36Sopenharmony_ci dev_err(dev, "%s: Cannot mount ocxlflash pseudofs rc=%d\n", 8862306a36Sopenharmony_ci __func__, rc); 8962306a36Sopenharmony_ci goto err2; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci inode = alloc_anon_inode(ocxlflash_vfs_mount->mnt_sb); 9362306a36Sopenharmony_ci if (IS_ERR(inode)) { 9462306a36Sopenharmony_ci rc = PTR_ERR(inode); 9562306a36Sopenharmony_ci dev_err(dev, "%s: alloc_anon_inode failed rc=%d\n", 9662306a36Sopenharmony_ci __func__, rc); 9762306a36Sopenharmony_ci goto err3; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci file = alloc_file_pseudo(inode, ocxlflash_vfs_mount, name, 10162306a36Sopenharmony_ci flags & (O_ACCMODE | O_NONBLOCK), fops); 10262306a36Sopenharmony_ci if (IS_ERR(file)) { 10362306a36Sopenharmony_ci rc = PTR_ERR(file); 10462306a36Sopenharmony_ci dev_err(dev, "%s: alloc_file failed rc=%d\n", 10562306a36Sopenharmony_ci __func__, rc); 10662306a36Sopenharmony_ci goto err4; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci file->private_data = priv; 11062306a36Sopenharmony_ciout: 11162306a36Sopenharmony_ci return file; 11262306a36Sopenharmony_cierr4: 11362306a36Sopenharmony_ci iput(inode); 11462306a36Sopenharmony_cierr3: 11562306a36Sopenharmony_ci simple_release_fs(&ocxlflash_vfs_mount, &ocxlflash_fs_cnt); 11662306a36Sopenharmony_cierr2: 11762306a36Sopenharmony_ci module_put(fops->owner); 11862306a36Sopenharmony_cierr1: 11962306a36Sopenharmony_ci file = ERR_PTR(rc); 12062306a36Sopenharmony_ci goto out; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/** 12462306a36Sopenharmony_ci * ocxlflash_psa_map() - map the process specific MMIO space 12562306a36Sopenharmony_ci * @ctx_cookie: Adapter context for which the mapping needs to be done. 12662306a36Sopenharmony_ci * 12762306a36Sopenharmony_ci * Return: MMIO pointer of the mapped region 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_cistatic void __iomem *ocxlflash_psa_map(void *ctx_cookie) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct ocxlflash_context *ctx = ctx_cookie; 13262306a36Sopenharmony_ci struct device *dev = ctx->hw_afu->dev; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci mutex_lock(&ctx->state_mutex); 13562306a36Sopenharmony_ci if (ctx->state != STARTED) { 13662306a36Sopenharmony_ci dev_err(dev, "%s: Context not started, state=%d\n", __func__, 13762306a36Sopenharmony_ci ctx->state); 13862306a36Sopenharmony_ci mutex_unlock(&ctx->state_mutex); 13962306a36Sopenharmony_ci return NULL; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci mutex_unlock(&ctx->state_mutex); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci return ioremap(ctx->psn_phys, ctx->psn_size); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci/** 14762306a36Sopenharmony_ci * ocxlflash_psa_unmap() - unmap the process specific MMIO space 14862306a36Sopenharmony_ci * @addr: MMIO pointer to unmap. 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_cistatic void ocxlflash_psa_unmap(void __iomem *addr) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci iounmap(addr); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci/** 15662306a36Sopenharmony_ci * ocxlflash_process_element() - get process element of the adapter context 15762306a36Sopenharmony_ci * @ctx_cookie: Adapter context associated with the process element. 15862306a36Sopenharmony_ci * 15962306a36Sopenharmony_ci * Return: process element of the adapter context 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_cistatic int ocxlflash_process_element(void *ctx_cookie) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct ocxlflash_context *ctx = ctx_cookie; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return ctx->pe; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/** 16962306a36Sopenharmony_ci * afu_map_irq() - map the interrupt of the adapter context 17062306a36Sopenharmony_ci * @flags: Flags. 17162306a36Sopenharmony_ci * @ctx: Adapter context. 17262306a36Sopenharmony_ci * @num: Per-context AFU interrupt number. 17362306a36Sopenharmony_ci * @handler: Interrupt handler to register. 17462306a36Sopenharmony_ci * @cookie: Interrupt handler private data. 17562306a36Sopenharmony_ci * @name: Name of the interrupt. 17662306a36Sopenharmony_ci * 17762306a36Sopenharmony_ci * Return: 0 on success, -errno on failure 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_cistatic int afu_map_irq(u64 flags, struct ocxlflash_context *ctx, int num, 18062306a36Sopenharmony_ci irq_handler_t handler, void *cookie, char *name) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct ocxl_hw_afu *afu = ctx->hw_afu; 18362306a36Sopenharmony_ci struct device *dev = afu->dev; 18462306a36Sopenharmony_ci struct ocxlflash_irqs *irq; 18562306a36Sopenharmony_ci struct xive_irq_data *xd; 18662306a36Sopenharmony_ci u32 virq; 18762306a36Sopenharmony_ci int rc = 0; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (num < 0 || num >= ctx->num_irqs) { 19062306a36Sopenharmony_ci dev_err(dev, "%s: Interrupt %d not allocated\n", __func__, num); 19162306a36Sopenharmony_ci rc = -ENOENT; 19262306a36Sopenharmony_ci goto out; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci irq = &ctx->irqs[num]; 19662306a36Sopenharmony_ci virq = irq_create_mapping(NULL, irq->hwirq); 19762306a36Sopenharmony_ci if (unlikely(!virq)) { 19862306a36Sopenharmony_ci dev_err(dev, "%s: irq_create_mapping failed\n", __func__); 19962306a36Sopenharmony_ci rc = -ENOMEM; 20062306a36Sopenharmony_ci goto out; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci rc = request_irq(virq, handler, 0, name, cookie); 20462306a36Sopenharmony_ci if (unlikely(rc)) { 20562306a36Sopenharmony_ci dev_err(dev, "%s: request_irq failed rc=%d\n", __func__, rc); 20662306a36Sopenharmony_ci goto err1; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci xd = irq_get_handler_data(virq); 21062306a36Sopenharmony_ci if (unlikely(!xd)) { 21162306a36Sopenharmony_ci dev_err(dev, "%s: Can't get interrupt data\n", __func__); 21262306a36Sopenharmony_ci rc = -ENXIO; 21362306a36Sopenharmony_ci goto err2; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci irq->virq = virq; 21762306a36Sopenharmony_ci irq->vtrig = xd->trig_mmio; 21862306a36Sopenharmony_ciout: 21962306a36Sopenharmony_ci return rc; 22062306a36Sopenharmony_cierr2: 22162306a36Sopenharmony_ci free_irq(virq, cookie); 22262306a36Sopenharmony_cierr1: 22362306a36Sopenharmony_ci irq_dispose_mapping(virq); 22462306a36Sopenharmony_ci goto out; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci/** 22862306a36Sopenharmony_ci * ocxlflash_map_afu_irq() - map the interrupt of the adapter context 22962306a36Sopenharmony_ci * @ctx_cookie: Adapter context. 23062306a36Sopenharmony_ci * @num: Per-context AFU interrupt number. 23162306a36Sopenharmony_ci * @handler: Interrupt handler to register. 23262306a36Sopenharmony_ci * @cookie: Interrupt handler private data. 23362306a36Sopenharmony_ci * @name: Name of the interrupt. 23462306a36Sopenharmony_ci * 23562306a36Sopenharmony_ci * Return: 0 on success, -errno on failure 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_cistatic int ocxlflash_map_afu_irq(void *ctx_cookie, int num, 23862306a36Sopenharmony_ci irq_handler_t handler, void *cookie, 23962306a36Sopenharmony_ci char *name) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci return afu_map_irq(0, ctx_cookie, num, handler, cookie, name); 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci/** 24562306a36Sopenharmony_ci * afu_unmap_irq() - unmap the interrupt 24662306a36Sopenharmony_ci * @flags: Flags. 24762306a36Sopenharmony_ci * @ctx: Adapter context. 24862306a36Sopenharmony_ci * @num: Per-context AFU interrupt number. 24962306a36Sopenharmony_ci * @cookie: Interrupt handler private data. 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_cistatic void afu_unmap_irq(u64 flags, struct ocxlflash_context *ctx, int num, 25262306a36Sopenharmony_ci void *cookie) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct ocxl_hw_afu *afu = ctx->hw_afu; 25562306a36Sopenharmony_ci struct device *dev = afu->dev; 25662306a36Sopenharmony_ci struct ocxlflash_irqs *irq; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (num < 0 || num >= ctx->num_irqs) { 25962306a36Sopenharmony_ci dev_err(dev, "%s: Interrupt %d not allocated\n", __func__, num); 26062306a36Sopenharmony_ci return; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci irq = &ctx->irqs[num]; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (irq_find_mapping(NULL, irq->hwirq)) { 26662306a36Sopenharmony_ci free_irq(irq->virq, cookie); 26762306a36Sopenharmony_ci irq_dispose_mapping(irq->virq); 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci memset(irq, 0, sizeof(*irq)); 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci/** 27462306a36Sopenharmony_ci * ocxlflash_unmap_afu_irq() - unmap the interrupt 27562306a36Sopenharmony_ci * @ctx_cookie: Adapter context. 27662306a36Sopenharmony_ci * @num: Per-context AFU interrupt number. 27762306a36Sopenharmony_ci * @cookie: Interrupt handler private data. 27862306a36Sopenharmony_ci */ 27962306a36Sopenharmony_cistatic void ocxlflash_unmap_afu_irq(void *ctx_cookie, int num, void *cookie) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci return afu_unmap_irq(0, ctx_cookie, num, cookie); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci/** 28562306a36Sopenharmony_ci * ocxlflash_get_irq_objhndl() - get the object handle for an interrupt 28662306a36Sopenharmony_ci * @ctx_cookie: Context associated with the interrupt. 28762306a36Sopenharmony_ci * @irq: Interrupt number. 28862306a36Sopenharmony_ci * 28962306a36Sopenharmony_ci * Return: effective address of the mapped region 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_cistatic u64 ocxlflash_get_irq_objhndl(void *ctx_cookie, int irq) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci struct ocxlflash_context *ctx = ctx_cookie; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (irq < 0 || irq >= ctx->num_irqs) 29662306a36Sopenharmony_ci return 0; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return (__force u64)ctx->irqs[irq].vtrig; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/** 30262306a36Sopenharmony_ci * ocxlflash_xsl_fault() - callback when translation error is triggered 30362306a36Sopenharmony_ci * @data: Private data provided at callback registration, the context. 30462306a36Sopenharmony_ci * @addr: Address that triggered the error. 30562306a36Sopenharmony_ci * @dsisr: Value of dsisr register. 30662306a36Sopenharmony_ci */ 30762306a36Sopenharmony_cistatic void ocxlflash_xsl_fault(void *data, u64 addr, u64 dsisr) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct ocxlflash_context *ctx = data; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci spin_lock(&ctx->slock); 31262306a36Sopenharmony_ci ctx->fault_addr = addr; 31362306a36Sopenharmony_ci ctx->fault_dsisr = dsisr; 31462306a36Sopenharmony_ci ctx->pending_fault = true; 31562306a36Sopenharmony_ci spin_unlock(&ctx->slock); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci wake_up_all(&ctx->wq); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci/** 32162306a36Sopenharmony_ci * start_context() - local routine to start a context 32262306a36Sopenharmony_ci * @ctx: Adapter context to be started. 32362306a36Sopenharmony_ci * 32462306a36Sopenharmony_ci * Assign the context specific MMIO space, add and enable the PE. 32562306a36Sopenharmony_ci * 32662306a36Sopenharmony_ci * Return: 0 on success, -errno on failure 32762306a36Sopenharmony_ci */ 32862306a36Sopenharmony_cistatic int start_context(struct ocxlflash_context *ctx) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci struct ocxl_hw_afu *afu = ctx->hw_afu; 33162306a36Sopenharmony_ci struct ocxl_afu_config *acfg = &afu->acfg; 33262306a36Sopenharmony_ci void *link_token = afu->link_token; 33362306a36Sopenharmony_ci struct pci_dev *pdev = afu->pdev; 33462306a36Sopenharmony_ci struct device *dev = afu->dev; 33562306a36Sopenharmony_ci bool master = ctx->master; 33662306a36Sopenharmony_ci struct mm_struct *mm; 33762306a36Sopenharmony_ci int rc = 0; 33862306a36Sopenharmony_ci u32 pid; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci mutex_lock(&ctx->state_mutex); 34162306a36Sopenharmony_ci if (ctx->state != OPENED) { 34262306a36Sopenharmony_ci dev_err(dev, "%s: Context state invalid, state=%d\n", 34362306a36Sopenharmony_ci __func__, ctx->state); 34462306a36Sopenharmony_ci rc = -EINVAL; 34562306a36Sopenharmony_ci goto out; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (master) { 34962306a36Sopenharmony_ci ctx->psn_size = acfg->global_mmio_size; 35062306a36Sopenharmony_ci ctx->psn_phys = afu->gmmio_phys; 35162306a36Sopenharmony_ci } else { 35262306a36Sopenharmony_ci ctx->psn_size = acfg->pp_mmio_stride; 35362306a36Sopenharmony_ci ctx->psn_phys = afu->ppmmio_phys + (ctx->pe * ctx->psn_size); 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* pid and mm not set for master contexts */ 35762306a36Sopenharmony_ci if (master) { 35862306a36Sopenharmony_ci pid = 0; 35962306a36Sopenharmony_ci mm = NULL; 36062306a36Sopenharmony_ci } else { 36162306a36Sopenharmony_ci pid = current->mm->context.id; 36262306a36Sopenharmony_ci mm = current->mm; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci rc = ocxl_link_add_pe(link_token, ctx->pe, pid, 0, 0, 36662306a36Sopenharmony_ci pci_dev_id(pdev), mm, ocxlflash_xsl_fault, 36762306a36Sopenharmony_ci ctx); 36862306a36Sopenharmony_ci if (unlikely(rc)) { 36962306a36Sopenharmony_ci dev_err(dev, "%s: ocxl_link_add_pe failed rc=%d\n", 37062306a36Sopenharmony_ci __func__, rc); 37162306a36Sopenharmony_ci goto out; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci ctx->state = STARTED; 37562306a36Sopenharmony_ciout: 37662306a36Sopenharmony_ci mutex_unlock(&ctx->state_mutex); 37762306a36Sopenharmony_ci return rc; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci/** 38162306a36Sopenharmony_ci * ocxlflash_start_context() - start a kernel context 38262306a36Sopenharmony_ci * @ctx_cookie: Adapter context to be started. 38362306a36Sopenharmony_ci * 38462306a36Sopenharmony_ci * Return: 0 on success, -errno on failure 38562306a36Sopenharmony_ci */ 38662306a36Sopenharmony_cistatic int ocxlflash_start_context(void *ctx_cookie) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct ocxlflash_context *ctx = ctx_cookie; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci return start_context(ctx); 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci/** 39462306a36Sopenharmony_ci * ocxlflash_stop_context() - stop a context 39562306a36Sopenharmony_ci * @ctx_cookie: Adapter context to be stopped. 39662306a36Sopenharmony_ci * 39762306a36Sopenharmony_ci * Return: 0 on success, -errno on failure 39862306a36Sopenharmony_ci */ 39962306a36Sopenharmony_cistatic int ocxlflash_stop_context(void *ctx_cookie) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci struct ocxlflash_context *ctx = ctx_cookie; 40262306a36Sopenharmony_ci struct ocxl_hw_afu *afu = ctx->hw_afu; 40362306a36Sopenharmony_ci struct ocxl_afu_config *acfg = &afu->acfg; 40462306a36Sopenharmony_ci struct pci_dev *pdev = afu->pdev; 40562306a36Sopenharmony_ci struct device *dev = afu->dev; 40662306a36Sopenharmony_ci enum ocxlflash_ctx_state state; 40762306a36Sopenharmony_ci int rc = 0; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci mutex_lock(&ctx->state_mutex); 41062306a36Sopenharmony_ci state = ctx->state; 41162306a36Sopenharmony_ci ctx->state = CLOSED; 41262306a36Sopenharmony_ci mutex_unlock(&ctx->state_mutex); 41362306a36Sopenharmony_ci if (state != STARTED) 41462306a36Sopenharmony_ci goto out; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci rc = ocxl_config_terminate_pasid(pdev, acfg->dvsec_afu_control_pos, 41762306a36Sopenharmony_ci ctx->pe); 41862306a36Sopenharmony_ci if (unlikely(rc)) { 41962306a36Sopenharmony_ci dev_err(dev, "%s: ocxl_config_terminate_pasid failed rc=%d\n", 42062306a36Sopenharmony_ci __func__, rc); 42162306a36Sopenharmony_ci /* If EBUSY, PE could be referenced in future by the AFU */ 42262306a36Sopenharmony_ci if (rc == -EBUSY) 42362306a36Sopenharmony_ci goto out; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci rc = ocxl_link_remove_pe(afu->link_token, ctx->pe); 42762306a36Sopenharmony_ci if (unlikely(rc)) { 42862306a36Sopenharmony_ci dev_err(dev, "%s: ocxl_link_remove_pe failed rc=%d\n", 42962306a36Sopenharmony_ci __func__, rc); 43062306a36Sopenharmony_ci goto out; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ciout: 43362306a36Sopenharmony_ci return rc; 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci/** 43762306a36Sopenharmony_ci * ocxlflash_afu_reset() - reset the AFU 43862306a36Sopenharmony_ci * @ctx_cookie: Adapter context. 43962306a36Sopenharmony_ci */ 44062306a36Sopenharmony_cistatic int ocxlflash_afu_reset(void *ctx_cookie) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci struct ocxlflash_context *ctx = ctx_cookie; 44362306a36Sopenharmony_ci struct device *dev = ctx->hw_afu->dev; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* Pending implementation from OCXL transport services */ 44662306a36Sopenharmony_ci dev_err_once(dev, "%s: afu_reset() fop not supported\n", __func__); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* Silently return success until it is implemented */ 44962306a36Sopenharmony_ci return 0; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci/** 45362306a36Sopenharmony_ci * ocxlflash_set_master() - sets the context as master 45462306a36Sopenharmony_ci * @ctx_cookie: Adapter context to set as master. 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_cistatic void ocxlflash_set_master(void *ctx_cookie) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct ocxlflash_context *ctx = ctx_cookie; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci ctx->master = true; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci/** 46462306a36Sopenharmony_ci * ocxlflash_get_context() - obtains the context associated with the host 46562306a36Sopenharmony_ci * @pdev: PCI device associated with the host. 46662306a36Sopenharmony_ci * @afu_cookie: Hardware AFU associated with the host. 46762306a36Sopenharmony_ci * 46862306a36Sopenharmony_ci * Return: returns the pointer to host adapter context 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_cistatic void *ocxlflash_get_context(struct pci_dev *pdev, void *afu_cookie) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci struct ocxl_hw_afu *afu = afu_cookie; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci return afu->ocxl_ctx; 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci/** 47862306a36Sopenharmony_ci * ocxlflash_dev_context_init() - allocate and initialize an adapter context 47962306a36Sopenharmony_ci * @pdev: PCI device associated with the host. 48062306a36Sopenharmony_ci * @afu_cookie: Hardware AFU associated with the host. 48162306a36Sopenharmony_ci * 48262306a36Sopenharmony_ci * Return: returns the adapter context on success, ERR_PTR on failure 48362306a36Sopenharmony_ci */ 48462306a36Sopenharmony_cistatic void *ocxlflash_dev_context_init(struct pci_dev *pdev, void *afu_cookie) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct ocxl_hw_afu *afu = afu_cookie; 48762306a36Sopenharmony_ci struct device *dev = afu->dev; 48862306a36Sopenharmony_ci struct ocxlflash_context *ctx; 48962306a36Sopenharmony_ci int rc; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 49262306a36Sopenharmony_ci if (unlikely(!ctx)) { 49362306a36Sopenharmony_ci dev_err(dev, "%s: Context allocation failed\n", __func__); 49462306a36Sopenharmony_ci rc = -ENOMEM; 49562306a36Sopenharmony_ci goto err1; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci idr_preload(GFP_KERNEL); 49962306a36Sopenharmony_ci rc = idr_alloc(&afu->idr, ctx, 0, afu->max_pasid, GFP_NOWAIT); 50062306a36Sopenharmony_ci idr_preload_end(); 50162306a36Sopenharmony_ci if (unlikely(rc < 0)) { 50262306a36Sopenharmony_ci dev_err(dev, "%s: idr_alloc failed rc=%d\n", __func__, rc); 50362306a36Sopenharmony_ci goto err2; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci spin_lock_init(&ctx->slock); 50762306a36Sopenharmony_ci init_waitqueue_head(&ctx->wq); 50862306a36Sopenharmony_ci mutex_init(&ctx->state_mutex); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci ctx->state = OPENED; 51162306a36Sopenharmony_ci ctx->pe = rc; 51262306a36Sopenharmony_ci ctx->master = false; 51362306a36Sopenharmony_ci ctx->mapping = NULL; 51462306a36Sopenharmony_ci ctx->hw_afu = afu; 51562306a36Sopenharmony_ci ctx->irq_bitmap = 0; 51662306a36Sopenharmony_ci ctx->pending_irq = false; 51762306a36Sopenharmony_ci ctx->pending_fault = false; 51862306a36Sopenharmony_ciout: 51962306a36Sopenharmony_ci return ctx; 52062306a36Sopenharmony_cierr2: 52162306a36Sopenharmony_ci kfree(ctx); 52262306a36Sopenharmony_cierr1: 52362306a36Sopenharmony_ci ctx = ERR_PTR(rc); 52462306a36Sopenharmony_ci goto out; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci/** 52862306a36Sopenharmony_ci * ocxlflash_release_context() - releases an adapter context 52962306a36Sopenharmony_ci * @ctx_cookie: Adapter context to be released. 53062306a36Sopenharmony_ci * 53162306a36Sopenharmony_ci * Return: 0 on success, -errno on failure 53262306a36Sopenharmony_ci */ 53362306a36Sopenharmony_cistatic int ocxlflash_release_context(void *ctx_cookie) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct ocxlflash_context *ctx = ctx_cookie; 53662306a36Sopenharmony_ci struct device *dev; 53762306a36Sopenharmony_ci int rc = 0; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (!ctx) 54062306a36Sopenharmony_ci goto out; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci dev = ctx->hw_afu->dev; 54362306a36Sopenharmony_ci mutex_lock(&ctx->state_mutex); 54462306a36Sopenharmony_ci if (ctx->state >= STARTED) { 54562306a36Sopenharmony_ci dev_err(dev, "%s: Context in use, state=%d\n", __func__, 54662306a36Sopenharmony_ci ctx->state); 54762306a36Sopenharmony_ci mutex_unlock(&ctx->state_mutex); 54862306a36Sopenharmony_ci rc = -EBUSY; 54962306a36Sopenharmony_ci goto out; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci mutex_unlock(&ctx->state_mutex); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci idr_remove(&ctx->hw_afu->idr, ctx->pe); 55462306a36Sopenharmony_ci ocxlflash_release_mapping(ctx); 55562306a36Sopenharmony_ci kfree(ctx); 55662306a36Sopenharmony_ciout: 55762306a36Sopenharmony_ci return rc; 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci/** 56162306a36Sopenharmony_ci * ocxlflash_perst_reloads_same_image() - sets the image reload policy 56262306a36Sopenharmony_ci * @afu_cookie: Hardware AFU associated with the host. 56362306a36Sopenharmony_ci * @image: Whether to load the same image on PERST. 56462306a36Sopenharmony_ci */ 56562306a36Sopenharmony_cistatic void ocxlflash_perst_reloads_same_image(void *afu_cookie, bool image) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci struct ocxl_hw_afu *afu = afu_cookie; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci afu->perst_same_image = image; 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci/** 57362306a36Sopenharmony_ci * ocxlflash_read_adapter_vpd() - reads the adapter VPD 57462306a36Sopenharmony_ci * @pdev: PCI device associated with the host. 57562306a36Sopenharmony_ci * @buf: Buffer to get the VPD data. 57662306a36Sopenharmony_ci * @count: Size of buffer (maximum bytes that can be read). 57762306a36Sopenharmony_ci * 57862306a36Sopenharmony_ci * Return: size of VPD on success, -errno on failure 57962306a36Sopenharmony_ci */ 58062306a36Sopenharmony_cistatic ssize_t ocxlflash_read_adapter_vpd(struct pci_dev *pdev, void *buf, 58162306a36Sopenharmony_ci size_t count) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci return pci_read_vpd(pdev, 0, count, buf); 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci/** 58762306a36Sopenharmony_ci * free_afu_irqs() - internal service to free interrupts 58862306a36Sopenharmony_ci * @ctx: Adapter context. 58962306a36Sopenharmony_ci */ 59062306a36Sopenharmony_cistatic void free_afu_irqs(struct ocxlflash_context *ctx) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci struct ocxl_hw_afu *afu = ctx->hw_afu; 59362306a36Sopenharmony_ci struct device *dev = afu->dev; 59462306a36Sopenharmony_ci int i; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (!ctx->irqs) { 59762306a36Sopenharmony_ci dev_err(dev, "%s: Interrupts not allocated\n", __func__); 59862306a36Sopenharmony_ci return; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci for (i = ctx->num_irqs; i >= 0; i--) 60262306a36Sopenharmony_ci ocxl_link_free_irq(afu->link_token, ctx->irqs[i].hwirq); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci kfree(ctx->irqs); 60562306a36Sopenharmony_ci ctx->irqs = NULL; 60662306a36Sopenharmony_ci} 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci/** 60962306a36Sopenharmony_ci * alloc_afu_irqs() - internal service to allocate interrupts 61062306a36Sopenharmony_ci * @ctx: Context associated with the request. 61162306a36Sopenharmony_ci * @num: Number of interrupts requested. 61262306a36Sopenharmony_ci * 61362306a36Sopenharmony_ci * Return: 0 on success, -errno on failure 61462306a36Sopenharmony_ci */ 61562306a36Sopenharmony_cistatic int alloc_afu_irqs(struct ocxlflash_context *ctx, int num) 61662306a36Sopenharmony_ci{ 61762306a36Sopenharmony_ci struct ocxl_hw_afu *afu = ctx->hw_afu; 61862306a36Sopenharmony_ci struct device *dev = afu->dev; 61962306a36Sopenharmony_ci struct ocxlflash_irqs *irqs; 62062306a36Sopenharmony_ci int rc = 0; 62162306a36Sopenharmony_ci int hwirq; 62262306a36Sopenharmony_ci int i; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci if (ctx->irqs) { 62562306a36Sopenharmony_ci dev_err(dev, "%s: Interrupts already allocated\n", __func__); 62662306a36Sopenharmony_ci rc = -EEXIST; 62762306a36Sopenharmony_ci goto out; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (num > OCXL_MAX_IRQS) { 63162306a36Sopenharmony_ci dev_err(dev, "%s: Too many interrupts num=%d\n", __func__, num); 63262306a36Sopenharmony_ci rc = -EINVAL; 63362306a36Sopenharmony_ci goto out; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci irqs = kcalloc(num, sizeof(*irqs), GFP_KERNEL); 63762306a36Sopenharmony_ci if (unlikely(!irqs)) { 63862306a36Sopenharmony_ci dev_err(dev, "%s: Context irqs allocation failed\n", __func__); 63962306a36Sopenharmony_ci rc = -ENOMEM; 64062306a36Sopenharmony_ci goto out; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci for (i = 0; i < num; i++) { 64462306a36Sopenharmony_ci rc = ocxl_link_irq_alloc(afu->link_token, &hwirq); 64562306a36Sopenharmony_ci if (unlikely(rc)) { 64662306a36Sopenharmony_ci dev_err(dev, "%s: ocxl_link_irq_alloc failed rc=%d\n", 64762306a36Sopenharmony_ci __func__, rc); 64862306a36Sopenharmony_ci goto err; 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci irqs[i].hwirq = hwirq; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci ctx->irqs = irqs; 65562306a36Sopenharmony_ci ctx->num_irqs = num; 65662306a36Sopenharmony_ciout: 65762306a36Sopenharmony_ci return rc; 65862306a36Sopenharmony_cierr: 65962306a36Sopenharmony_ci for (i = i-1; i >= 0; i--) 66062306a36Sopenharmony_ci ocxl_link_free_irq(afu->link_token, irqs[i].hwirq); 66162306a36Sopenharmony_ci kfree(irqs); 66262306a36Sopenharmony_ci goto out; 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci/** 66662306a36Sopenharmony_ci * ocxlflash_allocate_afu_irqs() - allocates the requested number of interrupts 66762306a36Sopenharmony_ci * @ctx_cookie: Context associated with the request. 66862306a36Sopenharmony_ci * @num: Number of interrupts requested. 66962306a36Sopenharmony_ci * 67062306a36Sopenharmony_ci * Return: 0 on success, -errno on failure 67162306a36Sopenharmony_ci */ 67262306a36Sopenharmony_cistatic int ocxlflash_allocate_afu_irqs(void *ctx_cookie, int num) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci return alloc_afu_irqs(ctx_cookie, num); 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci/** 67862306a36Sopenharmony_ci * ocxlflash_free_afu_irqs() - frees the interrupts of an adapter context 67962306a36Sopenharmony_ci * @ctx_cookie: Adapter context. 68062306a36Sopenharmony_ci */ 68162306a36Sopenharmony_cistatic void ocxlflash_free_afu_irqs(void *ctx_cookie) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci free_afu_irqs(ctx_cookie); 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci/** 68762306a36Sopenharmony_ci * ocxlflash_unconfig_afu() - unconfigure the AFU 68862306a36Sopenharmony_ci * @afu: AFU associated with the host. 68962306a36Sopenharmony_ci */ 69062306a36Sopenharmony_cistatic void ocxlflash_unconfig_afu(struct ocxl_hw_afu *afu) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci if (afu->gmmio_virt) { 69362306a36Sopenharmony_ci iounmap(afu->gmmio_virt); 69462306a36Sopenharmony_ci afu->gmmio_virt = NULL; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci/** 69962306a36Sopenharmony_ci * ocxlflash_destroy_afu() - destroy the AFU structure 70062306a36Sopenharmony_ci * @afu_cookie: AFU to be freed. 70162306a36Sopenharmony_ci */ 70262306a36Sopenharmony_cistatic void ocxlflash_destroy_afu(void *afu_cookie) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci struct ocxl_hw_afu *afu = afu_cookie; 70562306a36Sopenharmony_ci int pos; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci if (!afu) 70862306a36Sopenharmony_ci return; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci ocxlflash_release_context(afu->ocxl_ctx); 71162306a36Sopenharmony_ci idr_destroy(&afu->idr); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci /* Disable the AFU */ 71462306a36Sopenharmony_ci pos = afu->acfg.dvsec_afu_control_pos; 71562306a36Sopenharmony_ci ocxl_config_set_afu_state(afu->pdev, pos, 0); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci ocxlflash_unconfig_afu(afu); 71862306a36Sopenharmony_ci kfree(afu); 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci/** 72262306a36Sopenharmony_ci * ocxlflash_config_fn() - configure the host function 72362306a36Sopenharmony_ci * @pdev: PCI device associated with the host. 72462306a36Sopenharmony_ci * @afu: AFU associated with the host. 72562306a36Sopenharmony_ci * 72662306a36Sopenharmony_ci * Return: 0 on success, -errno on failure 72762306a36Sopenharmony_ci */ 72862306a36Sopenharmony_cistatic int ocxlflash_config_fn(struct pci_dev *pdev, struct ocxl_hw_afu *afu) 72962306a36Sopenharmony_ci{ 73062306a36Sopenharmony_ci struct ocxl_fn_config *fcfg = &afu->fcfg; 73162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 73262306a36Sopenharmony_ci u16 base, enabled, supported; 73362306a36Sopenharmony_ci int rc = 0; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci /* Read DVSEC config of the function */ 73662306a36Sopenharmony_ci rc = ocxl_config_read_function(pdev, fcfg); 73762306a36Sopenharmony_ci if (unlikely(rc)) { 73862306a36Sopenharmony_ci dev_err(dev, "%s: ocxl_config_read_function failed rc=%d\n", 73962306a36Sopenharmony_ci __func__, rc); 74062306a36Sopenharmony_ci goto out; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci /* Check if function has AFUs defined, only 1 per function supported */ 74462306a36Sopenharmony_ci if (fcfg->max_afu_index >= 0) { 74562306a36Sopenharmony_ci afu->is_present = true; 74662306a36Sopenharmony_ci if (fcfg->max_afu_index != 0) 74762306a36Sopenharmony_ci dev_warn(dev, "%s: Unexpected AFU index value %d\n", 74862306a36Sopenharmony_ci __func__, fcfg->max_afu_index); 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci rc = ocxl_config_get_actag_info(pdev, &base, &enabled, &supported); 75262306a36Sopenharmony_ci if (unlikely(rc)) { 75362306a36Sopenharmony_ci dev_err(dev, "%s: ocxl_config_get_actag_info failed rc=%d\n", 75462306a36Sopenharmony_ci __func__, rc); 75562306a36Sopenharmony_ci goto out; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci afu->fn_actag_base = base; 75962306a36Sopenharmony_ci afu->fn_actag_enabled = enabled; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci ocxl_config_set_actag(pdev, fcfg->dvsec_function_pos, base, enabled); 76262306a36Sopenharmony_ci dev_dbg(dev, "%s: Function acTag range base=%u enabled=%u\n", 76362306a36Sopenharmony_ci __func__, base, enabled); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci rc = ocxl_link_setup(pdev, 0, &afu->link_token); 76662306a36Sopenharmony_ci if (unlikely(rc)) { 76762306a36Sopenharmony_ci dev_err(dev, "%s: ocxl_link_setup failed rc=%d\n", 76862306a36Sopenharmony_ci __func__, rc); 76962306a36Sopenharmony_ci goto out; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci rc = ocxl_config_set_TL(pdev, fcfg->dvsec_tl_pos); 77362306a36Sopenharmony_ci if (unlikely(rc)) { 77462306a36Sopenharmony_ci dev_err(dev, "%s: ocxl_config_set_TL failed rc=%d\n", 77562306a36Sopenharmony_ci __func__, rc); 77662306a36Sopenharmony_ci goto err; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ciout: 77962306a36Sopenharmony_ci return rc; 78062306a36Sopenharmony_cierr: 78162306a36Sopenharmony_ci ocxl_link_release(pdev, afu->link_token); 78262306a36Sopenharmony_ci goto out; 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci/** 78662306a36Sopenharmony_ci * ocxlflash_unconfig_fn() - unconfigure the host function 78762306a36Sopenharmony_ci * @pdev: PCI device associated with the host. 78862306a36Sopenharmony_ci * @afu: AFU associated with the host. 78962306a36Sopenharmony_ci */ 79062306a36Sopenharmony_cistatic void ocxlflash_unconfig_fn(struct pci_dev *pdev, struct ocxl_hw_afu *afu) 79162306a36Sopenharmony_ci{ 79262306a36Sopenharmony_ci ocxl_link_release(pdev, afu->link_token); 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci/** 79662306a36Sopenharmony_ci * ocxlflash_map_mmio() - map the AFU MMIO space 79762306a36Sopenharmony_ci * @afu: AFU associated with the host. 79862306a36Sopenharmony_ci * 79962306a36Sopenharmony_ci * Return: 0 on success, -errno on failure 80062306a36Sopenharmony_ci */ 80162306a36Sopenharmony_cistatic int ocxlflash_map_mmio(struct ocxl_hw_afu *afu) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci struct ocxl_afu_config *acfg = &afu->acfg; 80462306a36Sopenharmony_ci struct pci_dev *pdev = afu->pdev; 80562306a36Sopenharmony_ci struct device *dev = afu->dev; 80662306a36Sopenharmony_ci phys_addr_t gmmio, ppmmio; 80762306a36Sopenharmony_ci int rc = 0; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci rc = pci_request_region(pdev, acfg->global_mmio_bar, "ocxlflash"); 81062306a36Sopenharmony_ci if (unlikely(rc)) { 81162306a36Sopenharmony_ci dev_err(dev, "%s: pci_request_region for global failed rc=%d\n", 81262306a36Sopenharmony_ci __func__, rc); 81362306a36Sopenharmony_ci goto out; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci gmmio = pci_resource_start(pdev, acfg->global_mmio_bar); 81662306a36Sopenharmony_ci gmmio += acfg->global_mmio_offset; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci rc = pci_request_region(pdev, acfg->pp_mmio_bar, "ocxlflash"); 81962306a36Sopenharmony_ci if (unlikely(rc)) { 82062306a36Sopenharmony_ci dev_err(dev, "%s: pci_request_region for pp bar failed rc=%d\n", 82162306a36Sopenharmony_ci __func__, rc); 82262306a36Sopenharmony_ci goto err1; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci ppmmio = pci_resource_start(pdev, acfg->pp_mmio_bar); 82562306a36Sopenharmony_ci ppmmio += acfg->pp_mmio_offset; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci afu->gmmio_virt = ioremap(gmmio, acfg->global_mmio_size); 82862306a36Sopenharmony_ci if (unlikely(!afu->gmmio_virt)) { 82962306a36Sopenharmony_ci dev_err(dev, "%s: MMIO mapping failed\n", __func__); 83062306a36Sopenharmony_ci rc = -ENOMEM; 83162306a36Sopenharmony_ci goto err2; 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci afu->gmmio_phys = gmmio; 83562306a36Sopenharmony_ci afu->ppmmio_phys = ppmmio; 83662306a36Sopenharmony_ciout: 83762306a36Sopenharmony_ci return rc; 83862306a36Sopenharmony_cierr2: 83962306a36Sopenharmony_ci pci_release_region(pdev, acfg->pp_mmio_bar); 84062306a36Sopenharmony_cierr1: 84162306a36Sopenharmony_ci pci_release_region(pdev, acfg->global_mmio_bar); 84262306a36Sopenharmony_ci goto out; 84362306a36Sopenharmony_ci} 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci/** 84662306a36Sopenharmony_ci * ocxlflash_config_afu() - configure the host AFU 84762306a36Sopenharmony_ci * @pdev: PCI device associated with the host. 84862306a36Sopenharmony_ci * @afu: AFU associated with the host. 84962306a36Sopenharmony_ci * 85062306a36Sopenharmony_ci * Must be called _after_ host function configuration. 85162306a36Sopenharmony_ci * 85262306a36Sopenharmony_ci * Return: 0 on success, -errno on failure 85362306a36Sopenharmony_ci */ 85462306a36Sopenharmony_cistatic int ocxlflash_config_afu(struct pci_dev *pdev, struct ocxl_hw_afu *afu) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci struct ocxl_afu_config *acfg = &afu->acfg; 85762306a36Sopenharmony_ci struct ocxl_fn_config *fcfg = &afu->fcfg; 85862306a36Sopenharmony_ci struct device *dev = &pdev->dev; 85962306a36Sopenharmony_ci int count; 86062306a36Sopenharmony_ci int base; 86162306a36Sopenharmony_ci int pos; 86262306a36Sopenharmony_ci int rc = 0; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci /* This HW AFU function does not have any AFUs defined */ 86562306a36Sopenharmony_ci if (!afu->is_present) 86662306a36Sopenharmony_ci goto out; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci /* Read AFU config at index 0 */ 86962306a36Sopenharmony_ci rc = ocxl_config_read_afu(pdev, fcfg, acfg, 0); 87062306a36Sopenharmony_ci if (unlikely(rc)) { 87162306a36Sopenharmony_ci dev_err(dev, "%s: ocxl_config_read_afu failed rc=%d\n", 87262306a36Sopenharmony_ci __func__, rc); 87362306a36Sopenharmony_ci goto out; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci /* Only one AFU per function is supported, so actag_base is same */ 87762306a36Sopenharmony_ci base = afu->fn_actag_base; 87862306a36Sopenharmony_ci count = min_t(int, acfg->actag_supported, afu->fn_actag_enabled); 87962306a36Sopenharmony_ci pos = acfg->dvsec_afu_control_pos; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci ocxl_config_set_afu_actag(pdev, pos, base, count); 88262306a36Sopenharmony_ci dev_dbg(dev, "%s: acTag base=%d enabled=%d\n", __func__, base, count); 88362306a36Sopenharmony_ci afu->afu_actag_base = base; 88462306a36Sopenharmony_ci afu->afu_actag_enabled = count; 88562306a36Sopenharmony_ci afu->max_pasid = 1 << acfg->pasid_supported_log; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci ocxl_config_set_afu_pasid(pdev, pos, 0, acfg->pasid_supported_log); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci rc = ocxlflash_map_mmio(afu); 89062306a36Sopenharmony_ci if (unlikely(rc)) { 89162306a36Sopenharmony_ci dev_err(dev, "%s: ocxlflash_map_mmio failed rc=%d\n", 89262306a36Sopenharmony_ci __func__, rc); 89362306a36Sopenharmony_ci goto out; 89462306a36Sopenharmony_ci } 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci /* Enable the AFU */ 89762306a36Sopenharmony_ci ocxl_config_set_afu_state(pdev, acfg->dvsec_afu_control_pos, 1); 89862306a36Sopenharmony_ciout: 89962306a36Sopenharmony_ci return rc; 90062306a36Sopenharmony_ci} 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci/** 90362306a36Sopenharmony_ci * ocxlflash_create_afu() - create the AFU for OCXL 90462306a36Sopenharmony_ci * @pdev: PCI device associated with the host. 90562306a36Sopenharmony_ci * 90662306a36Sopenharmony_ci * Return: AFU on success, NULL on failure 90762306a36Sopenharmony_ci */ 90862306a36Sopenharmony_cistatic void *ocxlflash_create_afu(struct pci_dev *pdev) 90962306a36Sopenharmony_ci{ 91062306a36Sopenharmony_ci struct device *dev = &pdev->dev; 91162306a36Sopenharmony_ci struct ocxlflash_context *ctx; 91262306a36Sopenharmony_ci struct ocxl_hw_afu *afu; 91362306a36Sopenharmony_ci int rc; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci afu = kzalloc(sizeof(*afu), GFP_KERNEL); 91662306a36Sopenharmony_ci if (unlikely(!afu)) { 91762306a36Sopenharmony_ci dev_err(dev, "%s: HW AFU allocation failed\n", __func__); 91862306a36Sopenharmony_ci goto out; 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci afu->pdev = pdev; 92262306a36Sopenharmony_ci afu->dev = dev; 92362306a36Sopenharmony_ci idr_init(&afu->idr); 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci rc = ocxlflash_config_fn(pdev, afu); 92662306a36Sopenharmony_ci if (unlikely(rc)) { 92762306a36Sopenharmony_ci dev_err(dev, "%s: Function configuration failed rc=%d\n", 92862306a36Sopenharmony_ci __func__, rc); 92962306a36Sopenharmony_ci goto err1; 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci rc = ocxlflash_config_afu(pdev, afu); 93362306a36Sopenharmony_ci if (unlikely(rc)) { 93462306a36Sopenharmony_ci dev_err(dev, "%s: AFU configuration failed rc=%d\n", 93562306a36Sopenharmony_ci __func__, rc); 93662306a36Sopenharmony_ci goto err2; 93762306a36Sopenharmony_ci } 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci ctx = ocxlflash_dev_context_init(pdev, afu); 94062306a36Sopenharmony_ci if (IS_ERR(ctx)) { 94162306a36Sopenharmony_ci rc = PTR_ERR(ctx); 94262306a36Sopenharmony_ci dev_err(dev, "%s: ocxlflash_dev_context_init failed rc=%d\n", 94362306a36Sopenharmony_ci __func__, rc); 94462306a36Sopenharmony_ci goto err3; 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci afu->ocxl_ctx = ctx; 94862306a36Sopenharmony_ciout: 94962306a36Sopenharmony_ci return afu; 95062306a36Sopenharmony_cierr3: 95162306a36Sopenharmony_ci ocxlflash_unconfig_afu(afu); 95262306a36Sopenharmony_cierr2: 95362306a36Sopenharmony_ci ocxlflash_unconfig_fn(pdev, afu); 95462306a36Sopenharmony_cierr1: 95562306a36Sopenharmony_ci idr_destroy(&afu->idr); 95662306a36Sopenharmony_ci kfree(afu); 95762306a36Sopenharmony_ci afu = NULL; 95862306a36Sopenharmony_ci goto out; 95962306a36Sopenharmony_ci} 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci/** 96262306a36Sopenharmony_ci * ctx_event_pending() - check for any event pending on the context 96362306a36Sopenharmony_ci * @ctx: Context to be checked. 96462306a36Sopenharmony_ci * 96562306a36Sopenharmony_ci * Return: true if there is an event pending, false if none pending 96662306a36Sopenharmony_ci */ 96762306a36Sopenharmony_cistatic inline bool ctx_event_pending(struct ocxlflash_context *ctx) 96862306a36Sopenharmony_ci{ 96962306a36Sopenharmony_ci if (ctx->pending_irq || ctx->pending_fault) 97062306a36Sopenharmony_ci return true; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci return false; 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci/** 97662306a36Sopenharmony_ci * afu_poll() - poll the AFU for events on the context 97762306a36Sopenharmony_ci * @file: File associated with the adapter context. 97862306a36Sopenharmony_ci * @poll: Poll structure from the user. 97962306a36Sopenharmony_ci * 98062306a36Sopenharmony_ci * Return: poll mask 98162306a36Sopenharmony_ci */ 98262306a36Sopenharmony_cistatic unsigned int afu_poll(struct file *file, struct poll_table_struct *poll) 98362306a36Sopenharmony_ci{ 98462306a36Sopenharmony_ci struct ocxlflash_context *ctx = file->private_data; 98562306a36Sopenharmony_ci struct device *dev = ctx->hw_afu->dev; 98662306a36Sopenharmony_ci ulong lock_flags; 98762306a36Sopenharmony_ci int mask = 0; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci poll_wait(file, &ctx->wq, poll); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci spin_lock_irqsave(&ctx->slock, lock_flags); 99262306a36Sopenharmony_ci if (ctx_event_pending(ctx)) 99362306a36Sopenharmony_ci mask |= POLLIN | POLLRDNORM; 99462306a36Sopenharmony_ci else if (ctx->state == CLOSED) 99562306a36Sopenharmony_ci mask |= POLLERR; 99662306a36Sopenharmony_ci spin_unlock_irqrestore(&ctx->slock, lock_flags); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci dev_dbg(dev, "%s: Poll wait completed for pe %i mask %i\n", 99962306a36Sopenharmony_ci __func__, ctx->pe, mask); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci return mask; 100262306a36Sopenharmony_ci} 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci/** 100562306a36Sopenharmony_ci * afu_read() - perform a read on the context for any event 100662306a36Sopenharmony_ci * @file: File associated with the adapter context. 100762306a36Sopenharmony_ci * @buf: Buffer to receive the data. 100862306a36Sopenharmony_ci * @count: Size of buffer (maximum bytes that can be read). 100962306a36Sopenharmony_ci * @off: Offset. 101062306a36Sopenharmony_ci * 101162306a36Sopenharmony_ci * Return: size of the data read on success, -errno on failure 101262306a36Sopenharmony_ci */ 101362306a36Sopenharmony_cistatic ssize_t afu_read(struct file *file, char __user *buf, size_t count, 101462306a36Sopenharmony_ci loff_t *off) 101562306a36Sopenharmony_ci{ 101662306a36Sopenharmony_ci struct ocxlflash_context *ctx = file->private_data; 101762306a36Sopenharmony_ci struct device *dev = ctx->hw_afu->dev; 101862306a36Sopenharmony_ci struct cxl_event event; 101962306a36Sopenharmony_ci ulong lock_flags; 102062306a36Sopenharmony_ci ssize_t esize; 102162306a36Sopenharmony_ci ssize_t rc; 102262306a36Sopenharmony_ci int bit; 102362306a36Sopenharmony_ci DEFINE_WAIT(event_wait); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci if (*off != 0) { 102662306a36Sopenharmony_ci dev_err(dev, "%s: Non-zero offset not supported, off=%lld\n", 102762306a36Sopenharmony_ci __func__, *off); 102862306a36Sopenharmony_ci rc = -EINVAL; 102962306a36Sopenharmony_ci goto out; 103062306a36Sopenharmony_ci } 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci spin_lock_irqsave(&ctx->slock, lock_flags); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci for (;;) { 103562306a36Sopenharmony_ci prepare_to_wait(&ctx->wq, &event_wait, TASK_INTERRUPTIBLE); 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci if (ctx_event_pending(ctx) || (ctx->state == CLOSED)) 103862306a36Sopenharmony_ci break; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci if (file->f_flags & O_NONBLOCK) { 104162306a36Sopenharmony_ci dev_err(dev, "%s: File cannot be blocked on I/O\n", 104262306a36Sopenharmony_ci __func__); 104362306a36Sopenharmony_ci rc = -EAGAIN; 104462306a36Sopenharmony_ci goto err; 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci if (signal_pending(current)) { 104862306a36Sopenharmony_ci dev_err(dev, "%s: Signal pending on the process\n", 104962306a36Sopenharmony_ci __func__); 105062306a36Sopenharmony_ci rc = -ERESTARTSYS; 105162306a36Sopenharmony_ci goto err; 105262306a36Sopenharmony_ci } 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci spin_unlock_irqrestore(&ctx->slock, lock_flags); 105562306a36Sopenharmony_ci schedule(); 105662306a36Sopenharmony_ci spin_lock_irqsave(&ctx->slock, lock_flags); 105762306a36Sopenharmony_ci } 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci finish_wait(&ctx->wq, &event_wait); 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci memset(&event, 0, sizeof(event)); 106262306a36Sopenharmony_ci event.header.process_element = ctx->pe; 106362306a36Sopenharmony_ci event.header.size = sizeof(struct cxl_event_header); 106462306a36Sopenharmony_ci if (ctx->pending_irq) { 106562306a36Sopenharmony_ci esize = sizeof(struct cxl_event_afu_interrupt); 106662306a36Sopenharmony_ci event.header.size += esize; 106762306a36Sopenharmony_ci event.header.type = CXL_EVENT_AFU_INTERRUPT; 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci bit = find_first_bit(&ctx->irq_bitmap, ctx->num_irqs); 107062306a36Sopenharmony_ci clear_bit(bit, &ctx->irq_bitmap); 107162306a36Sopenharmony_ci event.irq.irq = bit + 1; 107262306a36Sopenharmony_ci if (bitmap_empty(&ctx->irq_bitmap, ctx->num_irqs)) 107362306a36Sopenharmony_ci ctx->pending_irq = false; 107462306a36Sopenharmony_ci } else if (ctx->pending_fault) { 107562306a36Sopenharmony_ci event.header.size += sizeof(struct cxl_event_data_storage); 107662306a36Sopenharmony_ci event.header.type = CXL_EVENT_DATA_STORAGE; 107762306a36Sopenharmony_ci event.fault.addr = ctx->fault_addr; 107862306a36Sopenharmony_ci event.fault.dsisr = ctx->fault_dsisr; 107962306a36Sopenharmony_ci ctx->pending_fault = false; 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci spin_unlock_irqrestore(&ctx->slock, lock_flags); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci if (copy_to_user(buf, &event, event.header.size)) { 108562306a36Sopenharmony_ci dev_err(dev, "%s: copy_to_user failed\n", __func__); 108662306a36Sopenharmony_ci rc = -EFAULT; 108762306a36Sopenharmony_ci goto out; 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci rc = event.header.size; 109162306a36Sopenharmony_ciout: 109262306a36Sopenharmony_ci return rc; 109362306a36Sopenharmony_cierr: 109462306a36Sopenharmony_ci finish_wait(&ctx->wq, &event_wait); 109562306a36Sopenharmony_ci spin_unlock_irqrestore(&ctx->slock, lock_flags); 109662306a36Sopenharmony_ci goto out; 109762306a36Sopenharmony_ci} 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci/** 110062306a36Sopenharmony_ci * afu_release() - release and free the context 110162306a36Sopenharmony_ci * @inode: File inode pointer. 110262306a36Sopenharmony_ci * @file: File associated with the context. 110362306a36Sopenharmony_ci * 110462306a36Sopenharmony_ci * Return: 0 on success, -errno on failure 110562306a36Sopenharmony_ci */ 110662306a36Sopenharmony_cistatic int afu_release(struct inode *inode, struct file *file) 110762306a36Sopenharmony_ci{ 110862306a36Sopenharmony_ci struct ocxlflash_context *ctx = file->private_data; 110962306a36Sopenharmony_ci int i; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci /* Unmap and free the interrupts associated with the context */ 111262306a36Sopenharmony_ci for (i = ctx->num_irqs; i >= 0; i--) 111362306a36Sopenharmony_ci afu_unmap_irq(0, ctx, i, ctx); 111462306a36Sopenharmony_ci free_afu_irqs(ctx); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci return ocxlflash_release_context(ctx); 111762306a36Sopenharmony_ci} 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci/** 112062306a36Sopenharmony_ci * ocxlflash_mmap_fault() - mmap fault handler 112162306a36Sopenharmony_ci * @vmf: VM fault associated with current fault. 112262306a36Sopenharmony_ci * 112362306a36Sopenharmony_ci * Return: 0 on success, -errno on failure 112462306a36Sopenharmony_ci */ 112562306a36Sopenharmony_cistatic vm_fault_t ocxlflash_mmap_fault(struct vm_fault *vmf) 112662306a36Sopenharmony_ci{ 112762306a36Sopenharmony_ci struct vm_area_struct *vma = vmf->vma; 112862306a36Sopenharmony_ci struct ocxlflash_context *ctx = vma->vm_file->private_data; 112962306a36Sopenharmony_ci struct device *dev = ctx->hw_afu->dev; 113062306a36Sopenharmony_ci u64 mmio_area, offset; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci offset = vmf->pgoff << PAGE_SHIFT; 113362306a36Sopenharmony_ci if (offset >= ctx->psn_size) 113462306a36Sopenharmony_ci return VM_FAULT_SIGBUS; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci mutex_lock(&ctx->state_mutex); 113762306a36Sopenharmony_ci if (ctx->state != STARTED) { 113862306a36Sopenharmony_ci dev_err(dev, "%s: Context not started, state=%d\n", 113962306a36Sopenharmony_ci __func__, ctx->state); 114062306a36Sopenharmony_ci mutex_unlock(&ctx->state_mutex); 114162306a36Sopenharmony_ci return VM_FAULT_SIGBUS; 114262306a36Sopenharmony_ci } 114362306a36Sopenharmony_ci mutex_unlock(&ctx->state_mutex); 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci mmio_area = ctx->psn_phys; 114662306a36Sopenharmony_ci mmio_area += offset; 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci return vmf_insert_pfn(vma, vmf->address, mmio_area >> PAGE_SHIFT); 114962306a36Sopenharmony_ci} 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_cistatic const struct vm_operations_struct ocxlflash_vmops = { 115262306a36Sopenharmony_ci .fault = ocxlflash_mmap_fault, 115362306a36Sopenharmony_ci}; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci/** 115662306a36Sopenharmony_ci * afu_mmap() - map the fault handler operations 115762306a36Sopenharmony_ci * @file: File associated with the context. 115862306a36Sopenharmony_ci * @vma: VM area associated with mapping. 115962306a36Sopenharmony_ci * 116062306a36Sopenharmony_ci * Return: 0 on success, -errno on failure 116162306a36Sopenharmony_ci */ 116262306a36Sopenharmony_cistatic int afu_mmap(struct file *file, struct vm_area_struct *vma) 116362306a36Sopenharmony_ci{ 116462306a36Sopenharmony_ci struct ocxlflash_context *ctx = file->private_data; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci if ((vma_pages(vma) + vma->vm_pgoff) > 116762306a36Sopenharmony_ci (ctx->psn_size >> PAGE_SHIFT)) 116862306a36Sopenharmony_ci return -EINVAL; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci vm_flags_set(vma, VM_IO | VM_PFNMAP); 117162306a36Sopenharmony_ci vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 117262306a36Sopenharmony_ci vma->vm_ops = &ocxlflash_vmops; 117362306a36Sopenharmony_ci return 0; 117462306a36Sopenharmony_ci} 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_cistatic const struct file_operations ocxl_afu_fops = { 117762306a36Sopenharmony_ci .owner = THIS_MODULE, 117862306a36Sopenharmony_ci .poll = afu_poll, 117962306a36Sopenharmony_ci .read = afu_read, 118062306a36Sopenharmony_ci .release = afu_release, 118162306a36Sopenharmony_ci .mmap = afu_mmap, 118262306a36Sopenharmony_ci}; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci#define PATCH_FOPS(NAME) \ 118562306a36Sopenharmony_ci do { if (!fops->NAME) fops->NAME = ocxl_afu_fops.NAME; } while (0) 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci/** 118862306a36Sopenharmony_ci * ocxlflash_get_fd() - get file descriptor for an adapter context 118962306a36Sopenharmony_ci * @ctx_cookie: Adapter context. 119062306a36Sopenharmony_ci * @fops: File operations to be associated. 119162306a36Sopenharmony_ci * @fd: File descriptor to be returned back. 119262306a36Sopenharmony_ci * 119362306a36Sopenharmony_ci * Return: pointer to the file on success, ERR_PTR on failure 119462306a36Sopenharmony_ci */ 119562306a36Sopenharmony_cistatic struct file *ocxlflash_get_fd(void *ctx_cookie, 119662306a36Sopenharmony_ci struct file_operations *fops, int *fd) 119762306a36Sopenharmony_ci{ 119862306a36Sopenharmony_ci struct ocxlflash_context *ctx = ctx_cookie; 119962306a36Sopenharmony_ci struct device *dev = ctx->hw_afu->dev; 120062306a36Sopenharmony_ci struct file *file; 120162306a36Sopenharmony_ci int flags, fdtmp; 120262306a36Sopenharmony_ci int rc = 0; 120362306a36Sopenharmony_ci char *name = NULL; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci /* Only allow one fd per context */ 120662306a36Sopenharmony_ci if (ctx->mapping) { 120762306a36Sopenharmony_ci dev_err(dev, "%s: Context is already mapped to an fd\n", 120862306a36Sopenharmony_ci __func__); 120962306a36Sopenharmony_ci rc = -EEXIST; 121062306a36Sopenharmony_ci goto err1; 121162306a36Sopenharmony_ci } 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci flags = O_RDWR | O_CLOEXEC; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci /* This code is similar to anon_inode_getfd() */ 121662306a36Sopenharmony_ci rc = get_unused_fd_flags(flags); 121762306a36Sopenharmony_ci if (unlikely(rc < 0)) { 121862306a36Sopenharmony_ci dev_err(dev, "%s: get_unused_fd_flags failed rc=%d\n", 121962306a36Sopenharmony_ci __func__, rc); 122062306a36Sopenharmony_ci goto err1; 122162306a36Sopenharmony_ci } 122262306a36Sopenharmony_ci fdtmp = rc; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci /* Patch the file ops that are not defined */ 122562306a36Sopenharmony_ci if (fops) { 122662306a36Sopenharmony_ci PATCH_FOPS(poll); 122762306a36Sopenharmony_ci PATCH_FOPS(read); 122862306a36Sopenharmony_ci PATCH_FOPS(release); 122962306a36Sopenharmony_ci PATCH_FOPS(mmap); 123062306a36Sopenharmony_ci } else /* Use default ops */ 123162306a36Sopenharmony_ci fops = (struct file_operations *)&ocxl_afu_fops; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci name = kasprintf(GFP_KERNEL, "ocxlflash:%d", ctx->pe); 123462306a36Sopenharmony_ci file = ocxlflash_getfile(dev, name, fops, ctx, flags); 123562306a36Sopenharmony_ci kfree(name); 123662306a36Sopenharmony_ci if (IS_ERR(file)) { 123762306a36Sopenharmony_ci rc = PTR_ERR(file); 123862306a36Sopenharmony_ci dev_err(dev, "%s: ocxlflash_getfile failed rc=%d\n", 123962306a36Sopenharmony_ci __func__, rc); 124062306a36Sopenharmony_ci goto err2; 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci ctx->mapping = file->f_mapping; 124462306a36Sopenharmony_ci *fd = fdtmp; 124562306a36Sopenharmony_ciout: 124662306a36Sopenharmony_ci return file; 124762306a36Sopenharmony_cierr2: 124862306a36Sopenharmony_ci put_unused_fd(fdtmp); 124962306a36Sopenharmony_cierr1: 125062306a36Sopenharmony_ci file = ERR_PTR(rc); 125162306a36Sopenharmony_ci goto out; 125262306a36Sopenharmony_ci} 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci/** 125562306a36Sopenharmony_ci * ocxlflash_fops_get_context() - get the context associated with the file 125662306a36Sopenharmony_ci * @file: File associated with the adapter context. 125762306a36Sopenharmony_ci * 125862306a36Sopenharmony_ci * Return: pointer to the context 125962306a36Sopenharmony_ci */ 126062306a36Sopenharmony_cistatic void *ocxlflash_fops_get_context(struct file *file) 126162306a36Sopenharmony_ci{ 126262306a36Sopenharmony_ci return file->private_data; 126362306a36Sopenharmony_ci} 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci/** 126662306a36Sopenharmony_ci * ocxlflash_afu_irq() - interrupt handler for user contexts 126762306a36Sopenharmony_ci * @irq: Interrupt number. 126862306a36Sopenharmony_ci * @data: Private data provided at interrupt registration, the context. 126962306a36Sopenharmony_ci * 127062306a36Sopenharmony_ci * Return: Always return IRQ_HANDLED. 127162306a36Sopenharmony_ci */ 127262306a36Sopenharmony_cistatic irqreturn_t ocxlflash_afu_irq(int irq, void *data) 127362306a36Sopenharmony_ci{ 127462306a36Sopenharmony_ci struct ocxlflash_context *ctx = data; 127562306a36Sopenharmony_ci struct device *dev = ctx->hw_afu->dev; 127662306a36Sopenharmony_ci int i; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci dev_dbg(dev, "%s: Interrupt raised for pe %i virq %i\n", 127962306a36Sopenharmony_ci __func__, ctx->pe, irq); 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci for (i = 0; i < ctx->num_irqs; i++) { 128262306a36Sopenharmony_ci if (ctx->irqs[i].virq == irq) 128362306a36Sopenharmony_ci break; 128462306a36Sopenharmony_ci } 128562306a36Sopenharmony_ci if (unlikely(i >= ctx->num_irqs)) { 128662306a36Sopenharmony_ci dev_err(dev, "%s: Received AFU IRQ out of range\n", __func__); 128762306a36Sopenharmony_ci goto out; 128862306a36Sopenharmony_ci } 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci spin_lock(&ctx->slock); 129162306a36Sopenharmony_ci set_bit(i - 1, &ctx->irq_bitmap); 129262306a36Sopenharmony_ci ctx->pending_irq = true; 129362306a36Sopenharmony_ci spin_unlock(&ctx->slock); 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci wake_up_all(&ctx->wq); 129662306a36Sopenharmony_ciout: 129762306a36Sopenharmony_ci return IRQ_HANDLED; 129862306a36Sopenharmony_ci} 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci/** 130162306a36Sopenharmony_ci * ocxlflash_start_work() - start a user context 130262306a36Sopenharmony_ci * @ctx_cookie: Context to be started. 130362306a36Sopenharmony_ci * @num_irqs: Number of interrupts requested. 130462306a36Sopenharmony_ci * 130562306a36Sopenharmony_ci * Return: 0 on success, -errno on failure 130662306a36Sopenharmony_ci */ 130762306a36Sopenharmony_cistatic int ocxlflash_start_work(void *ctx_cookie, u64 num_irqs) 130862306a36Sopenharmony_ci{ 130962306a36Sopenharmony_ci struct ocxlflash_context *ctx = ctx_cookie; 131062306a36Sopenharmony_ci struct ocxl_hw_afu *afu = ctx->hw_afu; 131162306a36Sopenharmony_ci struct device *dev = afu->dev; 131262306a36Sopenharmony_ci char *name; 131362306a36Sopenharmony_ci int rc = 0; 131462306a36Sopenharmony_ci int i; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci rc = alloc_afu_irqs(ctx, num_irqs); 131762306a36Sopenharmony_ci if (unlikely(rc < 0)) { 131862306a36Sopenharmony_ci dev_err(dev, "%s: alloc_afu_irqs failed rc=%d\n", __func__, rc); 131962306a36Sopenharmony_ci goto out; 132062306a36Sopenharmony_ci } 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci for (i = 0; i < num_irqs; i++) { 132362306a36Sopenharmony_ci name = kasprintf(GFP_KERNEL, "ocxlflash-%s-pe%i-%i", 132462306a36Sopenharmony_ci dev_name(dev), ctx->pe, i); 132562306a36Sopenharmony_ci rc = afu_map_irq(0, ctx, i, ocxlflash_afu_irq, ctx, name); 132662306a36Sopenharmony_ci kfree(name); 132762306a36Sopenharmony_ci if (unlikely(rc < 0)) { 132862306a36Sopenharmony_ci dev_err(dev, "%s: afu_map_irq failed rc=%d\n", 132962306a36Sopenharmony_ci __func__, rc); 133062306a36Sopenharmony_ci goto err; 133162306a36Sopenharmony_ci } 133262306a36Sopenharmony_ci } 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci rc = start_context(ctx); 133562306a36Sopenharmony_ci if (unlikely(rc)) { 133662306a36Sopenharmony_ci dev_err(dev, "%s: start_context failed rc=%d\n", __func__, rc); 133762306a36Sopenharmony_ci goto err; 133862306a36Sopenharmony_ci } 133962306a36Sopenharmony_ciout: 134062306a36Sopenharmony_ci return rc; 134162306a36Sopenharmony_cierr: 134262306a36Sopenharmony_ci for (i = i-1; i >= 0; i--) 134362306a36Sopenharmony_ci afu_unmap_irq(0, ctx, i, ctx); 134462306a36Sopenharmony_ci free_afu_irqs(ctx); 134562306a36Sopenharmony_ci goto out; 134662306a36Sopenharmony_ci}; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci/** 134962306a36Sopenharmony_ci * ocxlflash_fd_mmap() - mmap handler for adapter file descriptor 135062306a36Sopenharmony_ci * @file: File installed with adapter file descriptor. 135162306a36Sopenharmony_ci * @vma: VM area associated with mapping. 135262306a36Sopenharmony_ci * 135362306a36Sopenharmony_ci * Return: 0 on success, -errno on failure 135462306a36Sopenharmony_ci */ 135562306a36Sopenharmony_cistatic int ocxlflash_fd_mmap(struct file *file, struct vm_area_struct *vma) 135662306a36Sopenharmony_ci{ 135762306a36Sopenharmony_ci return afu_mmap(file, vma); 135862306a36Sopenharmony_ci} 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci/** 136162306a36Sopenharmony_ci * ocxlflash_fd_release() - release the context associated with the file 136262306a36Sopenharmony_ci * @inode: File inode pointer. 136362306a36Sopenharmony_ci * @file: File associated with the adapter context. 136462306a36Sopenharmony_ci * 136562306a36Sopenharmony_ci * Return: 0 on success, -errno on failure 136662306a36Sopenharmony_ci */ 136762306a36Sopenharmony_cistatic int ocxlflash_fd_release(struct inode *inode, struct file *file) 136862306a36Sopenharmony_ci{ 136962306a36Sopenharmony_ci return afu_release(inode, file); 137062306a36Sopenharmony_ci} 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci/* Backend ops to ocxlflash services */ 137362306a36Sopenharmony_ciconst struct cxlflash_backend_ops cxlflash_ocxl_ops = { 137462306a36Sopenharmony_ci .module = THIS_MODULE, 137562306a36Sopenharmony_ci .psa_map = ocxlflash_psa_map, 137662306a36Sopenharmony_ci .psa_unmap = ocxlflash_psa_unmap, 137762306a36Sopenharmony_ci .process_element = ocxlflash_process_element, 137862306a36Sopenharmony_ci .map_afu_irq = ocxlflash_map_afu_irq, 137962306a36Sopenharmony_ci .unmap_afu_irq = ocxlflash_unmap_afu_irq, 138062306a36Sopenharmony_ci .get_irq_objhndl = ocxlflash_get_irq_objhndl, 138162306a36Sopenharmony_ci .start_context = ocxlflash_start_context, 138262306a36Sopenharmony_ci .stop_context = ocxlflash_stop_context, 138362306a36Sopenharmony_ci .afu_reset = ocxlflash_afu_reset, 138462306a36Sopenharmony_ci .set_master = ocxlflash_set_master, 138562306a36Sopenharmony_ci .get_context = ocxlflash_get_context, 138662306a36Sopenharmony_ci .dev_context_init = ocxlflash_dev_context_init, 138762306a36Sopenharmony_ci .release_context = ocxlflash_release_context, 138862306a36Sopenharmony_ci .perst_reloads_same_image = ocxlflash_perst_reloads_same_image, 138962306a36Sopenharmony_ci .read_adapter_vpd = ocxlflash_read_adapter_vpd, 139062306a36Sopenharmony_ci .allocate_afu_irqs = ocxlflash_allocate_afu_irqs, 139162306a36Sopenharmony_ci .free_afu_irqs = ocxlflash_free_afu_irqs, 139262306a36Sopenharmony_ci .create_afu = ocxlflash_create_afu, 139362306a36Sopenharmony_ci .destroy_afu = ocxlflash_destroy_afu, 139462306a36Sopenharmony_ci .get_fd = ocxlflash_get_fd, 139562306a36Sopenharmony_ci .fops_get_context = ocxlflash_fops_get_context, 139662306a36Sopenharmony_ci .start_work = ocxlflash_start_work, 139762306a36Sopenharmony_ci .fd_mmap = ocxlflash_fd_mmap, 139862306a36Sopenharmony_ci .fd_release = ocxlflash_fd_release, 139962306a36Sopenharmony_ci}; 1400