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