162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2015 IBM Corp.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/spinlock.h>
762306a36Sopenharmony_ci#include <linux/uaccess.h>
862306a36Sopenharmony_ci#include <linux/delay.h>
962306a36Sopenharmony_ci#include <linux/irqdomain.h>
1062306a36Sopenharmony_ci#include <linux/platform_device.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "cxl.h"
1362306a36Sopenharmony_ci#include "hcalls.h"
1462306a36Sopenharmony_ci#include "trace.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define CXL_ERROR_DETECTED_EVENT	1
1762306a36Sopenharmony_ci#define CXL_SLOT_RESET_EVENT		2
1862306a36Sopenharmony_ci#define CXL_RESUME_EVENT		3
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic void pci_error_handlers(struct cxl_afu *afu,
2162306a36Sopenharmony_ci				int bus_error_event,
2262306a36Sopenharmony_ci				pci_channel_state_t state)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	struct pci_dev *afu_dev;
2562306a36Sopenharmony_ci	struct pci_driver *afu_drv;
2662306a36Sopenharmony_ci	const struct pci_error_handlers *err_handler;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	if (afu->phb == NULL)
2962306a36Sopenharmony_ci		return;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	list_for_each_entry(afu_dev, &afu->phb->bus->devices, bus_list) {
3262306a36Sopenharmony_ci		afu_drv = to_pci_driver(afu_dev->dev.driver);
3362306a36Sopenharmony_ci		if (!afu_drv)
3462306a36Sopenharmony_ci			continue;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci		err_handler = afu_drv->err_handler;
3762306a36Sopenharmony_ci		switch (bus_error_event) {
3862306a36Sopenharmony_ci		case CXL_ERROR_DETECTED_EVENT:
3962306a36Sopenharmony_ci			afu_dev->error_state = state;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci			if (err_handler &&
4262306a36Sopenharmony_ci			    err_handler->error_detected)
4362306a36Sopenharmony_ci				err_handler->error_detected(afu_dev, state);
4462306a36Sopenharmony_ci			break;
4562306a36Sopenharmony_ci		case CXL_SLOT_RESET_EVENT:
4662306a36Sopenharmony_ci			afu_dev->error_state = state;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci			if (err_handler &&
4962306a36Sopenharmony_ci			    err_handler->slot_reset)
5062306a36Sopenharmony_ci				err_handler->slot_reset(afu_dev);
5162306a36Sopenharmony_ci			break;
5262306a36Sopenharmony_ci		case CXL_RESUME_EVENT:
5362306a36Sopenharmony_ci			if (err_handler &&
5462306a36Sopenharmony_ci			    err_handler->resume)
5562306a36Sopenharmony_ci				err_handler->resume(afu_dev);
5662306a36Sopenharmony_ci			break;
5762306a36Sopenharmony_ci		}
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic irqreturn_t guest_handle_psl_slice_error(struct cxl_context *ctx, u64 dsisr,
6262306a36Sopenharmony_ci					u64 errstat)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	pr_devel("in %s\n", __func__);
6562306a36Sopenharmony_ci	dev_crit(&ctx->afu->dev, "PSL ERROR STATUS: 0x%.16llx\n", errstat);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	return cxl_ops->ack_irq(ctx, 0, errstat);
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic ssize_t guest_collect_vpd(struct cxl *adapter, struct cxl_afu *afu,
7162306a36Sopenharmony_ci			void *buf, size_t len)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	unsigned int entries, mod;
7462306a36Sopenharmony_ci	unsigned long **vpd_buf = NULL;
7562306a36Sopenharmony_ci	struct sg_list *le;
7662306a36Sopenharmony_ci	int rc = 0, i, tocopy;
7762306a36Sopenharmony_ci	u64 out = 0;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (buf == NULL)
8062306a36Sopenharmony_ci		return -EINVAL;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	/* number of entries in the list */
8362306a36Sopenharmony_ci	entries = len / SG_BUFFER_SIZE;
8462306a36Sopenharmony_ci	mod = len % SG_BUFFER_SIZE;
8562306a36Sopenharmony_ci	if (mod)
8662306a36Sopenharmony_ci		entries++;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (entries > SG_MAX_ENTRIES) {
8962306a36Sopenharmony_ci		entries = SG_MAX_ENTRIES;
9062306a36Sopenharmony_ci		len = SG_MAX_ENTRIES * SG_BUFFER_SIZE;
9162306a36Sopenharmony_ci		mod = 0;
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	vpd_buf = kcalloc(entries, sizeof(unsigned long *), GFP_KERNEL);
9562306a36Sopenharmony_ci	if (!vpd_buf)
9662306a36Sopenharmony_ci		return -ENOMEM;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	le = (struct sg_list *)get_zeroed_page(GFP_KERNEL);
9962306a36Sopenharmony_ci	if (!le) {
10062306a36Sopenharmony_ci		rc = -ENOMEM;
10162306a36Sopenharmony_ci		goto err1;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	for (i = 0; i < entries; i++) {
10562306a36Sopenharmony_ci		vpd_buf[i] = (unsigned long *)get_zeroed_page(GFP_KERNEL);
10662306a36Sopenharmony_ci		if (!vpd_buf[i]) {
10762306a36Sopenharmony_ci			rc = -ENOMEM;
10862306a36Sopenharmony_ci			goto err2;
10962306a36Sopenharmony_ci		}
11062306a36Sopenharmony_ci		le[i].phys_addr = cpu_to_be64(virt_to_phys(vpd_buf[i]));
11162306a36Sopenharmony_ci		le[i].len = cpu_to_be64(SG_BUFFER_SIZE);
11262306a36Sopenharmony_ci		if ((i == (entries - 1)) && mod)
11362306a36Sopenharmony_ci			le[i].len = cpu_to_be64(mod);
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (adapter)
11762306a36Sopenharmony_ci		rc = cxl_h_collect_vpd_adapter(adapter->guest->handle,
11862306a36Sopenharmony_ci					virt_to_phys(le), entries, &out);
11962306a36Sopenharmony_ci	else
12062306a36Sopenharmony_ci		rc = cxl_h_collect_vpd(afu->guest->handle, 0,
12162306a36Sopenharmony_ci				virt_to_phys(le), entries, &out);
12262306a36Sopenharmony_ci	pr_devel("length of available (entries: %i), vpd: %#llx\n",
12362306a36Sopenharmony_ci		entries, out);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (!rc) {
12662306a36Sopenharmony_ci		/*
12762306a36Sopenharmony_ci		 * hcall returns in 'out' the size of available VPDs.
12862306a36Sopenharmony_ci		 * It fills the buffer with as much data as possible.
12962306a36Sopenharmony_ci		 */
13062306a36Sopenharmony_ci		if (out < len)
13162306a36Sopenharmony_ci			len = out;
13262306a36Sopenharmony_ci		rc = len;
13362306a36Sopenharmony_ci		if (out) {
13462306a36Sopenharmony_ci			for (i = 0; i < entries; i++) {
13562306a36Sopenharmony_ci				if (len < SG_BUFFER_SIZE)
13662306a36Sopenharmony_ci					tocopy = len;
13762306a36Sopenharmony_ci				else
13862306a36Sopenharmony_ci					tocopy = SG_BUFFER_SIZE;
13962306a36Sopenharmony_ci				memcpy(buf, vpd_buf[i], tocopy);
14062306a36Sopenharmony_ci				buf += tocopy;
14162306a36Sopenharmony_ci				len -= tocopy;
14262306a36Sopenharmony_ci			}
14362306a36Sopenharmony_ci		}
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_cierr2:
14662306a36Sopenharmony_ci	for (i = 0; i < entries; i++) {
14762306a36Sopenharmony_ci		if (vpd_buf[i])
14862306a36Sopenharmony_ci			free_page((unsigned long) vpd_buf[i]);
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci	free_page((unsigned long) le);
15162306a36Sopenharmony_cierr1:
15262306a36Sopenharmony_ci	kfree(vpd_buf);
15362306a36Sopenharmony_ci	return rc;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic int guest_get_irq_info(struct cxl_context *ctx, struct cxl_irq_info *info)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	return cxl_h_collect_int_info(ctx->afu->guest->handle, ctx->process_token, info);
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic irqreturn_t guest_psl_irq(int irq, void *data)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	struct cxl_context *ctx = data;
16462306a36Sopenharmony_ci	struct cxl_irq_info irq_info;
16562306a36Sopenharmony_ci	int rc;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	pr_devel("%d: received PSL interrupt %i\n", ctx->pe, irq);
16862306a36Sopenharmony_ci	rc = guest_get_irq_info(ctx, &irq_info);
16962306a36Sopenharmony_ci	if (rc) {
17062306a36Sopenharmony_ci		WARN(1, "Unable to get IRQ info: %i\n", rc);
17162306a36Sopenharmony_ci		return IRQ_HANDLED;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	rc = cxl_irq_psl8(irq, ctx, &irq_info);
17562306a36Sopenharmony_ci	return rc;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic int afu_read_error_state(struct cxl_afu *afu, int *state_out)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	u64 state;
18162306a36Sopenharmony_ci	int rc = 0;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	if (!afu)
18462306a36Sopenharmony_ci		return -EIO;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	rc = cxl_h_read_error_state(afu->guest->handle, &state);
18762306a36Sopenharmony_ci	if (!rc) {
18862306a36Sopenharmony_ci		WARN_ON(state != H_STATE_NORMAL &&
18962306a36Sopenharmony_ci			state != H_STATE_DISABLE &&
19062306a36Sopenharmony_ci			state != H_STATE_TEMP_UNAVAILABLE &&
19162306a36Sopenharmony_ci			state != H_STATE_PERM_UNAVAILABLE);
19262306a36Sopenharmony_ci		*state_out = state & 0xffffffff;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci	return rc;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic irqreturn_t guest_slice_irq_err(int irq, void *data)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct cxl_afu *afu = data;
20062306a36Sopenharmony_ci	int rc;
20162306a36Sopenharmony_ci	u64 serr, afu_error, dsisr;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	rc = cxl_h_get_fn_error_interrupt(afu->guest->handle, &serr);
20462306a36Sopenharmony_ci	if (rc) {
20562306a36Sopenharmony_ci		dev_crit(&afu->dev, "Couldn't read PSL_SERR_An: %d\n", rc);
20662306a36Sopenharmony_ci		return IRQ_HANDLED;
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci	afu_error = cxl_p2n_read(afu, CXL_AFU_ERR_An);
20962306a36Sopenharmony_ci	dsisr = cxl_p2n_read(afu, CXL_PSL_DSISR_An);
21062306a36Sopenharmony_ci	cxl_afu_decode_psl_serr(afu, serr);
21162306a36Sopenharmony_ci	dev_crit(&afu->dev, "AFU_ERR_An: 0x%.16llx\n", afu_error);
21262306a36Sopenharmony_ci	dev_crit(&afu->dev, "PSL_DSISR_An: 0x%.16llx\n", dsisr);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	rc = cxl_h_ack_fn_error_interrupt(afu->guest->handle, serr);
21562306a36Sopenharmony_ci	if (rc)
21662306a36Sopenharmony_ci		dev_crit(&afu->dev, "Couldn't ack slice error interrupt: %d\n",
21762306a36Sopenharmony_ci			rc);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	return IRQ_HANDLED;
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic int irq_alloc_range(struct cxl *adapter, int len, int *irq)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	int i, n;
22662306a36Sopenharmony_ci	struct irq_avail *cur;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	for (i = 0; i < adapter->guest->irq_nranges; i++) {
22962306a36Sopenharmony_ci		cur = &adapter->guest->irq_avail[i];
23062306a36Sopenharmony_ci		n = bitmap_find_next_zero_area(cur->bitmap, cur->range,
23162306a36Sopenharmony_ci					0, len, 0);
23262306a36Sopenharmony_ci		if (n < cur->range) {
23362306a36Sopenharmony_ci			bitmap_set(cur->bitmap, n, len);
23462306a36Sopenharmony_ci			*irq = cur->offset + n;
23562306a36Sopenharmony_ci			pr_devel("guest: allocate IRQs %#x->%#x\n",
23662306a36Sopenharmony_ci				*irq, *irq + len - 1);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci			return 0;
23962306a36Sopenharmony_ci		}
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci	return -ENOSPC;
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic int irq_free_range(struct cxl *adapter, int irq, int len)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	int i, n;
24762306a36Sopenharmony_ci	struct irq_avail *cur;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if (len == 0)
25062306a36Sopenharmony_ci		return -ENOENT;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	for (i = 0; i < adapter->guest->irq_nranges; i++) {
25362306a36Sopenharmony_ci		cur = &adapter->guest->irq_avail[i];
25462306a36Sopenharmony_ci		if (irq >= cur->offset &&
25562306a36Sopenharmony_ci			(irq + len) <= (cur->offset + cur->range)) {
25662306a36Sopenharmony_ci			n = irq - cur->offset;
25762306a36Sopenharmony_ci			bitmap_clear(cur->bitmap, n, len);
25862306a36Sopenharmony_ci			pr_devel("guest: release IRQs %#x->%#x\n",
25962306a36Sopenharmony_ci				irq, irq + len - 1);
26062306a36Sopenharmony_ci			return 0;
26162306a36Sopenharmony_ci		}
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci	return -ENOENT;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic int guest_reset(struct cxl *adapter)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	struct cxl_afu *afu = NULL;
26962306a36Sopenharmony_ci	int i, rc;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	pr_devel("Adapter reset request\n");
27262306a36Sopenharmony_ci	spin_lock(&adapter->afu_list_lock);
27362306a36Sopenharmony_ci	for (i = 0; i < adapter->slices; i++) {
27462306a36Sopenharmony_ci		if ((afu = adapter->afu[i])) {
27562306a36Sopenharmony_ci			pci_error_handlers(afu, CXL_ERROR_DETECTED_EVENT,
27662306a36Sopenharmony_ci					pci_channel_io_frozen);
27762306a36Sopenharmony_ci			cxl_context_detach_all(afu);
27862306a36Sopenharmony_ci		}
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	rc = cxl_h_reset_adapter(adapter->guest->handle);
28262306a36Sopenharmony_ci	for (i = 0; i < adapter->slices; i++) {
28362306a36Sopenharmony_ci		if (!rc && (afu = adapter->afu[i])) {
28462306a36Sopenharmony_ci			pci_error_handlers(afu, CXL_SLOT_RESET_EVENT,
28562306a36Sopenharmony_ci					pci_channel_io_normal);
28662306a36Sopenharmony_ci			pci_error_handlers(afu, CXL_RESUME_EVENT, 0);
28762306a36Sopenharmony_ci		}
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci	spin_unlock(&adapter->afu_list_lock);
29062306a36Sopenharmony_ci	return rc;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic int guest_alloc_one_irq(struct cxl *adapter)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	int irq;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	spin_lock(&adapter->guest->irq_alloc_lock);
29862306a36Sopenharmony_ci	if (irq_alloc_range(adapter, 1, &irq))
29962306a36Sopenharmony_ci		irq = -ENOSPC;
30062306a36Sopenharmony_ci	spin_unlock(&adapter->guest->irq_alloc_lock);
30162306a36Sopenharmony_ci	return irq;
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic void guest_release_one_irq(struct cxl *adapter, int irq)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	spin_lock(&adapter->guest->irq_alloc_lock);
30762306a36Sopenharmony_ci	irq_free_range(adapter, irq, 1);
30862306a36Sopenharmony_ci	spin_unlock(&adapter->guest->irq_alloc_lock);
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic int guest_alloc_irq_ranges(struct cxl_irq_ranges *irqs,
31262306a36Sopenharmony_ci				struct cxl *adapter, unsigned int num)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	int i, try, irq;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	memset(irqs, 0, sizeof(struct cxl_irq_ranges));
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	spin_lock(&adapter->guest->irq_alloc_lock);
31962306a36Sopenharmony_ci	for (i = 0; i < CXL_IRQ_RANGES && num; i++) {
32062306a36Sopenharmony_ci		try = num;
32162306a36Sopenharmony_ci		while (try) {
32262306a36Sopenharmony_ci			if (irq_alloc_range(adapter, try, &irq) == 0)
32362306a36Sopenharmony_ci				break;
32462306a36Sopenharmony_ci			try /= 2;
32562306a36Sopenharmony_ci		}
32662306a36Sopenharmony_ci		if (!try)
32762306a36Sopenharmony_ci			goto error;
32862306a36Sopenharmony_ci		irqs->offset[i] = irq;
32962306a36Sopenharmony_ci		irqs->range[i] = try;
33062306a36Sopenharmony_ci		num -= try;
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci	if (num)
33362306a36Sopenharmony_ci		goto error;
33462306a36Sopenharmony_ci	spin_unlock(&adapter->guest->irq_alloc_lock);
33562306a36Sopenharmony_ci	return 0;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cierror:
33862306a36Sopenharmony_ci	for (i = 0; i < CXL_IRQ_RANGES; i++)
33962306a36Sopenharmony_ci		irq_free_range(adapter, irqs->offset[i], irqs->range[i]);
34062306a36Sopenharmony_ci	spin_unlock(&adapter->guest->irq_alloc_lock);
34162306a36Sopenharmony_ci	return -ENOSPC;
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistatic void guest_release_irq_ranges(struct cxl_irq_ranges *irqs,
34562306a36Sopenharmony_ci				struct cxl *adapter)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	int i;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	spin_lock(&adapter->guest->irq_alloc_lock);
35062306a36Sopenharmony_ci	for (i = 0; i < CXL_IRQ_RANGES; i++)
35162306a36Sopenharmony_ci		irq_free_range(adapter, irqs->offset[i], irqs->range[i]);
35262306a36Sopenharmony_ci	spin_unlock(&adapter->guest->irq_alloc_lock);
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic int guest_register_serr_irq(struct cxl_afu *afu)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	afu->err_irq_name = kasprintf(GFP_KERNEL, "cxl-%s-err",
35862306a36Sopenharmony_ci				      dev_name(&afu->dev));
35962306a36Sopenharmony_ci	if (!afu->err_irq_name)
36062306a36Sopenharmony_ci		return -ENOMEM;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	if (!(afu->serr_virq = cxl_map_irq(afu->adapter, afu->serr_hwirq,
36362306a36Sopenharmony_ci				 guest_slice_irq_err, afu, afu->err_irq_name))) {
36462306a36Sopenharmony_ci		kfree(afu->err_irq_name);
36562306a36Sopenharmony_ci		afu->err_irq_name = NULL;
36662306a36Sopenharmony_ci		return -ENOMEM;
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	return 0;
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic void guest_release_serr_irq(struct cxl_afu *afu)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	cxl_unmap_irq(afu->serr_virq, afu);
37562306a36Sopenharmony_ci	cxl_ops->release_one_irq(afu->adapter, afu->serr_hwirq);
37662306a36Sopenharmony_ci	kfree(afu->err_irq_name);
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic int guest_ack_irq(struct cxl_context *ctx, u64 tfc, u64 psl_reset_mask)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	return cxl_h_control_faults(ctx->afu->guest->handle, ctx->process_token,
38262306a36Sopenharmony_ci				tfc >> 32, (psl_reset_mask != 0));
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic void disable_afu_irqs(struct cxl_context *ctx)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	irq_hw_number_t hwirq;
38862306a36Sopenharmony_ci	unsigned int virq;
38962306a36Sopenharmony_ci	int r, i;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	pr_devel("Disabling AFU(%d) interrupts\n", ctx->afu->slice);
39262306a36Sopenharmony_ci	for (r = 0; r < CXL_IRQ_RANGES; r++) {
39362306a36Sopenharmony_ci		hwirq = ctx->irqs.offset[r];
39462306a36Sopenharmony_ci		for (i = 0; i < ctx->irqs.range[r]; hwirq++, i++) {
39562306a36Sopenharmony_ci			virq = irq_find_mapping(NULL, hwirq);
39662306a36Sopenharmony_ci			disable_irq(virq);
39762306a36Sopenharmony_ci		}
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic void enable_afu_irqs(struct cxl_context *ctx)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	irq_hw_number_t hwirq;
40462306a36Sopenharmony_ci	unsigned int virq;
40562306a36Sopenharmony_ci	int r, i;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	pr_devel("Enabling AFU(%d) interrupts\n", ctx->afu->slice);
40862306a36Sopenharmony_ci	for (r = 0; r < CXL_IRQ_RANGES; r++) {
40962306a36Sopenharmony_ci		hwirq = ctx->irqs.offset[r];
41062306a36Sopenharmony_ci		for (i = 0; i < ctx->irqs.range[r]; hwirq++, i++) {
41162306a36Sopenharmony_ci			virq = irq_find_mapping(NULL, hwirq);
41262306a36Sopenharmony_ci			enable_irq(virq);
41362306a36Sopenharmony_ci		}
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic int _guest_afu_cr_readXX(int sz, struct cxl_afu *afu, int cr_idx,
41862306a36Sopenharmony_ci			u64 offset, u64 *val)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	unsigned long cr;
42162306a36Sopenharmony_ci	char c;
42262306a36Sopenharmony_ci	int rc = 0;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	if (afu->crs_len < sz)
42562306a36Sopenharmony_ci		return -ENOENT;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	if (unlikely(offset >= afu->crs_len))
42862306a36Sopenharmony_ci		return -ERANGE;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	cr = get_zeroed_page(GFP_KERNEL);
43162306a36Sopenharmony_ci	if (!cr)
43262306a36Sopenharmony_ci		return -ENOMEM;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	rc = cxl_h_get_config(afu->guest->handle, cr_idx, offset,
43562306a36Sopenharmony_ci			virt_to_phys((void *)cr), sz);
43662306a36Sopenharmony_ci	if (rc)
43762306a36Sopenharmony_ci		goto err;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	switch (sz) {
44062306a36Sopenharmony_ci	case 1:
44162306a36Sopenharmony_ci		c = *((char *) cr);
44262306a36Sopenharmony_ci		*val = c;
44362306a36Sopenharmony_ci		break;
44462306a36Sopenharmony_ci	case 2:
44562306a36Sopenharmony_ci		*val = in_le16((u16 *)cr);
44662306a36Sopenharmony_ci		break;
44762306a36Sopenharmony_ci	case 4:
44862306a36Sopenharmony_ci		*val = in_le32((unsigned *)cr);
44962306a36Sopenharmony_ci		break;
45062306a36Sopenharmony_ci	case 8:
45162306a36Sopenharmony_ci		*val = in_le64((u64 *)cr);
45262306a36Sopenharmony_ci		break;
45362306a36Sopenharmony_ci	default:
45462306a36Sopenharmony_ci		WARN_ON(1);
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_cierr:
45762306a36Sopenharmony_ci	free_page(cr);
45862306a36Sopenharmony_ci	return rc;
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic int guest_afu_cr_read32(struct cxl_afu *afu, int cr_idx, u64 offset,
46262306a36Sopenharmony_ci			u32 *out)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	int rc;
46562306a36Sopenharmony_ci	u64 val;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	rc = _guest_afu_cr_readXX(4, afu, cr_idx, offset, &val);
46862306a36Sopenharmony_ci	if (!rc)
46962306a36Sopenharmony_ci		*out = (u32) val;
47062306a36Sopenharmony_ci	return rc;
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cistatic int guest_afu_cr_read16(struct cxl_afu *afu, int cr_idx, u64 offset,
47462306a36Sopenharmony_ci			u16 *out)
47562306a36Sopenharmony_ci{
47662306a36Sopenharmony_ci	int rc;
47762306a36Sopenharmony_ci	u64 val;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	rc = _guest_afu_cr_readXX(2, afu, cr_idx, offset, &val);
48062306a36Sopenharmony_ci	if (!rc)
48162306a36Sopenharmony_ci		*out = (u16) val;
48262306a36Sopenharmony_ci	return rc;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic int guest_afu_cr_read8(struct cxl_afu *afu, int cr_idx, u64 offset,
48662306a36Sopenharmony_ci			u8 *out)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	int rc;
48962306a36Sopenharmony_ci	u64 val;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	rc = _guest_afu_cr_readXX(1, afu, cr_idx, offset, &val);
49262306a36Sopenharmony_ci	if (!rc)
49362306a36Sopenharmony_ci		*out = (u8) val;
49462306a36Sopenharmony_ci	return rc;
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistatic int guest_afu_cr_read64(struct cxl_afu *afu, int cr_idx, u64 offset,
49862306a36Sopenharmony_ci			u64 *out)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	return _guest_afu_cr_readXX(8, afu, cr_idx, offset, out);
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_cistatic int guest_afu_cr_write32(struct cxl_afu *afu, int cr, u64 off, u32 in)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci	/* config record is not writable from guest */
50662306a36Sopenharmony_ci	return -EPERM;
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistatic int guest_afu_cr_write16(struct cxl_afu *afu, int cr, u64 off, u16 in)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	/* config record is not writable from guest */
51262306a36Sopenharmony_ci	return -EPERM;
51362306a36Sopenharmony_ci}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_cistatic int guest_afu_cr_write8(struct cxl_afu *afu, int cr, u64 off, u8 in)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	/* config record is not writable from guest */
51862306a36Sopenharmony_ci	return -EPERM;
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic int attach_afu_directed(struct cxl_context *ctx, u64 wed, u64 amr)
52262306a36Sopenharmony_ci{
52362306a36Sopenharmony_ci	struct cxl_process_element_hcall *elem;
52462306a36Sopenharmony_ci	struct cxl *adapter = ctx->afu->adapter;
52562306a36Sopenharmony_ci	const struct cred *cred;
52662306a36Sopenharmony_ci	u32 pid, idx;
52762306a36Sopenharmony_ci	int rc, r, i;
52862306a36Sopenharmony_ci	u64 mmio_addr, mmio_size;
52962306a36Sopenharmony_ci	__be64 flags = 0;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	/* Must be 8 byte aligned and cannot cross a 4096 byte boundary */
53262306a36Sopenharmony_ci	if (!(elem = (struct cxl_process_element_hcall *)
53362306a36Sopenharmony_ci			get_zeroed_page(GFP_KERNEL)))
53462306a36Sopenharmony_ci		return -ENOMEM;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	elem->version = cpu_to_be64(CXL_PROCESS_ELEMENT_VERSION);
53762306a36Sopenharmony_ci	if (ctx->kernel) {
53862306a36Sopenharmony_ci		pid = 0;
53962306a36Sopenharmony_ci		flags |= CXL_PE_TRANSLATION_ENABLED;
54062306a36Sopenharmony_ci		flags |= CXL_PE_PRIVILEGED_PROCESS;
54162306a36Sopenharmony_ci		if (mfmsr() & MSR_SF)
54262306a36Sopenharmony_ci			flags |= CXL_PE_64_BIT;
54362306a36Sopenharmony_ci	} else {
54462306a36Sopenharmony_ci		pid = current->pid;
54562306a36Sopenharmony_ci		flags |= CXL_PE_PROBLEM_STATE;
54662306a36Sopenharmony_ci		flags |= CXL_PE_TRANSLATION_ENABLED;
54762306a36Sopenharmony_ci		if (!test_tsk_thread_flag(current, TIF_32BIT))
54862306a36Sopenharmony_ci			flags |= CXL_PE_64_BIT;
54962306a36Sopenharmony_ci		cred = get_current_cred();
55062306a36Sopenharmony_ci		if (uid_eq(cred->euid, GLOBAL_ROOT_UID))
55162306a36Sopenharmony_ci			flags |= CXL_PE_PRIVILEGED_PROCESS;
55262306a36Sopenharmony_ci		put_cred(cred);
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci	elem->flags         = cpu_to_be64(flags);
55562306a36Sopenharmony_ci	elem->common.tid    = cpu_to_be32(0); /* Unused */
55662306a36Sopenharmony_ci	elem->common.pid    = cpu_to_be32(pid);
55762306a36Sopenharmony_ci	elem->common.csrp   = cpu_to_be64(0); /* disable */
55862306a36Sopenharmony_ci	elem->common.u.psl8.aurp0  = cpu_to_be64(0); /* disable */
55962306a36Sopenharmony_ci	elem->common.u.psl8.aurp1  = cpu_to_be64(0); /* disable */
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	cxl_prefault(ctx, wed);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	elem->common.u.psl8.sstp0  = cpu_to_be64(ctx->sstp0);
56462306a36Sopenharmony_ci	elem->common.u.psl8.sstp1  = cpu_to_be64(ctx->sstp1);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	/*
56762306a36Sopenharmony_ci	 * Ensure we have at least one interrupt allocated to take faults for
56862306a36Sopenharmony_ci	 * kernel contexts that may not have allocated any AFU IRQs at all:
56962306a36Sopenharmony_ci	 */
57062306a36Sopenharmony_ci	if (ctx->irqs.range[0] == 0) {
57162306a36Sopenharmony_ci		rc = afu_register_irqs(ctx, 0);
57262306a36Sopenharmony_ci		if (rc)
57362306a36Sopenharmony_ci			goto out_free;
57462306a36Sopenharmony_ci	}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	for (r = 0; r < CXL_IRQ_RANGES; r++) {
57762306a36Sopenharmony_ci		for (i = 0; i < ctx->irqs.range[r]; i++) {
57862306a36Sopenharmony_ci			if (r == 0 && i == 0) {
57962306a36Sopenharmony_ci				elem->pslVirtualIsn = cpu_to_be32(ctx->irqs.offset[0]);
58062306a36Sopenharmony_ci			} else {
58162306a36Sopenharmony_ci				idx = ctx->irqs.offset[r] + i - adapter->guest->irq_base_offset;
58262306a36Sopenharmony_ci				elem->applicationVirtualIsnBitmap[idx / 8] |= 0x80 >> (idx % 8);
58362306a36Sopenharmony_ci			}
58462306a36Sopenharmony_ci		}
58562306a36Sopenharmony_ci	}
58662306a36Sopenharmony_ci	elem->common.amr = cpu_to_be64(amr);
58762306a36Sopenharmony_ci	elem->common.wed = cpu_to_be64(wed);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	disable_afu_irqs(ctx);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	rc = cxl_h_attach_process(ctx->afu->guest->handle, elem,
59262306a36Sopenharmony_ci				&ctx->process_token, &mmio_addr, &mmio_size);
59362306a36Sopenharmony_ci	if (rc == H_SUCCESS) {
59462306a36Sopenharmony_ci		if (ctx->master || !ctx->afu->pp_psa) {
59562306a36Sopenharmony_ci			ctx->psn_phys = ctx->afu->psn_phys;
59662306a36Sopenharmony_ci			ctx->psn_size = ctx->afu->adapter->ps_size;
59762306a36Sopenharmony_ci		} else {
59862306a36Sopenharmony_ci			ctx->psn_phys = mmio_addr;
59962306a36Sopenharmony_ci			ctx->psn_size = mmio_size;
60062306a36Sopenharmony_ci		}
60162306a36Sopenharmony_ci		if (ctx->afu->pp_psa && mmio_size &&
60262306a36Sopenharmony_ci			ctx->afu->pp_size == 0) {
60362306a36Sopenharmony_ci			/*
60462306a36Sopenharmony_ci			 * There's no property in the device tree to read the
60562306a36Sopenharmony_ci			 * pp_size. We only find out at the 1st attach.
60662306a36Sopenharmony_ci			 * Compared to bare-metal, it is too late and we
60762306a36Sopenharmony_ci			 * should really lock here. However, on powerVM,
60862306a36Sopenharmony_ci			 * pp_size is really only used to display in /sys.
60962306a36Sopenharmony_ci			 * Being discussed with pHyp for their next release.
61062306a36Sopenharmony_ci			 */
61162306a36Sopenharmony_ci			ctx->afu->pp_size = mmio_size;
61262306a36Sopenharmony_ci		}
61362306a36Sopenharmony_ci		/* from PAPR: process element is bytes 4-7 of process token */
61462306a36Sopenharmony_ci		ctx->external_pe = ctx->process_token & 0xFFFFFFFF;
61562306a36Sopenharmony_ci		pr_devel("CXL pe=%i is known as %i for pHyp, mmio_size=%#llx",
61662306a36Sopenharmony_ci			ctx->pe, ctx->external_pe, ctx->psn_size);
61762306a36Sopenharmony_ci		ctx->pe_inserted = true;
61862306a36Sopenharmony_ci		enable_afu_irqs(ctx);
61962306a36Sopenharmony_ci	}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ciout_free:
62262306a36Sopenharmony_ci	free_page((u64)elem);
62362306a36Sopenharmony_ci	return rc;
62462306a36Sopenharmony_ci}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_cistatic int guest_attach_process(struct cxl_context *ctx, bool kernel, u64 wed, u64 amr)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	pr_devel("in %s\n", __func__);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	ctx->kernel = kernel;
63162306a36Sopenharmony_ci	if (ctx->afu->current_mode == CXL_MODE_DIRECTED)
63262306a36Sopenharmony_ci		return attach_afu_directed(ctx, wed, amr);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	/* dedicated mode not supported on FW840 */
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	return -EINVAL;
63762306a36Sopenharmony_ci}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_cistatic int detach_afu_directed(struct cxl_context *ctx)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	if (!ctx->pe_inserted)
64262306a36Sopenharmony_ci		return 0;
64362306a36Sopenharmony_ci	if (cxl_h_detach_process(ctx->afu->guest->handle, ctx->process_token))
64462306a36Sopenharmony_ci		return -1;
64562306a36Sopenharmony_ci	return 0;
64662306a36Sopenharmony_ci}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_cistatic int guest_detach_process(struct cxl_context *ctx)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	pr_devel("in %s\n", __func__);
65162306a36Sopenharmony_ci	trace_cxl_detach(ctx);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	if (!cxl_ops->link_ok(ctx->afu->adapter, ctx->afu))
65462306a36Sopenharmony_ci		return -EIO;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	if (ctx->afu->current_mode == CXL_MODE_DIRECTED)
65762306a36Sopenharmony_ci		return detach_afu_directed(ctx);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	return -EINVAL;
66062306a36Sopenharmony_ci}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cistatic void guest_release_afu(struct device *dev)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	struct cxl_afu *afu = to_cxl_afu(dev);
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	pr_devel("%s\n", __func__);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	idr_destroy(&afu->contexts_idr);
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	kfree(afu->guest);
67162306a36Sopenharmony_ci	kfree(afu);
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_cissize_t cxl_guest_read_afu_vpd(struct cxl_afu *afu, void *buf, size_t len)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci	return guest_collect_vpd(NULL, afu, buf, len);
67762306a36Sopenharmony_ci}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci#define ERR_BUFF_MAX_COPY_SIZE PAGE_SIZE
68062306a36Sopenharmony_cistatic ssize_t guest_afu_read_err_buffer(struct cxl_afu *afu, char *buf,
68162306a36Sopenharmony_ci					loff_t off, size_t count)
68262306a36Sopenharmony_ci{
68362306a36Sopenharmony_ci	void *tbuf = NULL;
68462306a36Sopenharmony_ci	int rc = 0;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	tbuf = (void *) get_zeroed_page(GFP_KERNEL);
68762306a36Sopenharmony_ci	if (!tbuf)
68862306a36Sopenharmony_ci		return -ENOMEM;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	rc = cxl_h_get_afu_err(afu->guest->handle,
69162306a36Sopenharmony_ci			       off & 0x7,
69262306a36Sopenharmony_ci			       virt_to_phys(tbuf),
69362306a36Sopenharmony_ci			       count);
69462306a36Sopenharmony_ci	if (rc)
69562306a36Sopenharmony_ci		goto err;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	if (count > ERR_BUFF_MAX_COPY_SIZE)
69862306a36Sopenharmony_ci		count = ERR_BUFF_MAX_COPY_SIZE - (off & 0x7);
69962306a36Sopenharmony_ci	memcpy(buf, tbuf, count);
70062306a36Sopenharmony_cierr:
70162306a36Sopenharmony_ci	free_page((u64)tbuf);
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	return rc;
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_cistatic int guest_afu_check_and_enable(struct cxl_afu *afu)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	return 0;
70962306a36Sopenharmony_ci}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_cistatic bool guest_support_attributes(const char *attr_name,
71262306a36Sopenharmony_ci				     enum cxl_attrs type)
71362306a36Sopenharmony_ci{
71462306a36Sopenharmony_ci	switch (type) {
71562306a36Sopenharmony_ci	case CXL_ADAPTER_ATTRS:
71662306a36Sopenharmony_ci		if ((strcmp(attr_name, "base_image") == 0) ||
71762306a36Sopenharmony_ci			(strcmp(attr_name, "load_image_on_perst") == 0) ||
71862306a36Sopenharmony_ci			(strcmp(attr_name, "perst_reloads_same_image") == 0) ||
71962306a36Sopenharmony_ci			(strcmp(attr_name, "image_loaded") == 0))
72062306a36Sopenharmony_ci			return false;
72162306a36Sopenharmony_ci		break;
72262306a36Sopenharmony_ci	case CXL_AFU_MASTER_ATTRS:
72362306a36Sopenharmony_ci		if ((strcmp(attr_name, "pp_mmio_off") == 0))
72462306a36Sopenharmony_ci			return false;
72562306a36Sopenharmony_ci		break;
72662306a36Sopenharmony_ci	case CXL_AFU_ATTRS:
72762306a36Sopenharmony_ci		break;
72862306a36Sopenharmony_ci	default:
72962306a36Sopenharmony_ci		break;
73062306a36Sopenharmony_ci	}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	return true;
73362306a36Sopenharmony_ci}
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_cistatic int activate_afu_directed(struct cxl_afu *afu)
73662306a36Sopenharmony_ci{
73762306a36Sopenharmony_ci	int rc;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	dev_info(&afu->dev, "Activating AFU(%d) directed mode\n", afu->slice);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	afu->current_mode = CXL_MODE_DIRECTED;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	afu->num_procs = afu->max_procs_virtualised;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	if ((rc = cxl_chardev_m_afu_add(afu)))
74662306a36Sopenharmony_ci		return rc;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	if ((rc = cxl_sysfs_afu_m_add(afu)))
74962306a36Sopenharmony_ci		goto err;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	if ((rc = cxl_chardev_s_afu_add(afu)))
75262306a36Sopenharmony_ci		goto err1;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	return 0;
75562306a36Sopenharmony_cierr1:
75662306a36Sopenharmony_ci	cxl_sysfs_afu_m_remove(afu);
75762306a36Sopenharmony_cierr:
75862306a36Sopenharmony_ci	cxl_chardev_afu_remove(afu);
75962306a36Sopenharmony_ci	return rc;
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_cistatic int guest_afu_activate_mode(struct cxl_afu *afu, int mode)
76362306a36Sopenharmony_ci{
76462306a36Sopenharmony_ci	if (!mode)
76562306a36Sopenharmony_ci		return 0;
76662306a36Sopenharmony_ci	if (!(mode & afu->modes_supported))
76762306a36Sopenharmony_ci		return -EINVAL;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	if (mode == CXL_MODE_DIRECTED)
77062306a36Sopenharmony_ci		return activate_afu_directed(afu);
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	if (mode == CXL_MODE_DEDICATED)
77362306a36Sopenharmony_ci		dev_err(&afu->dev, "Dedicated mode not supported\n");
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	return -EINVAL;
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_cistatic int deactivate_afu_directed(struct cxl_afu *afu)
77962306a36Sopenharmony_ci{
78062306a36Sopenharmony_ci	dev_info(&afu->dev, "Deactivating AFU(%d) directed mode\n", afu->slice);
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	afu->current_mode = 0;
78362306a36Sopenharmony_ci	afu->num_procs = 0;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	cxl_sysfs_afu_m_remove(afu);
78662306a36Sopenharmony_ci	cxl_chardev_afu_remove(afu);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	cxl_ops->afu_reset(afu);
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	return 0;
79162306a36Sopenharmony_ci}
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_cistatic int guest_afu_deactivate_mode(struct cxl_afu *afu, int mode)
79462306a36Sopenharmony_ci{
79562306a36Sopenharmony_ci	if (!mode)
79662306a36Sopenharmony_ci		return 0;
79762306a36Sopenharmony_ci	if (!(mode & afu->modes_supported))
79862306a36Sopenharmony_ci		return -EINVAL;
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	if (mode == CXL_MODE_DIRECTED)
80162306a36Sopenharmony_ci		return deactivate_afu_directed(afu);
80262306a36Sopenharmony_ci	return 0;
80362306a36Sopenharmony_ci}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_cistatic int guest_afu_reset(struct cxl_afu *afu)
80662306a36Sopenharmony_ci{
80762306a36Sopenharmony_ci	pr_devel("AFU(%d) reset request\n", afu->slice);
80862306a36Sopenharmony_ci	return cxl_h_reset_afu(afu->guest->handle);
80962306a36Sopenharmony_ci}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_cistatic int guest_map_slice_regs(struct cxl_afu *afu)
81262306a36Sopenharmony_ci{
81362306a36Sopenharmony_ci	if (!(afu->p2n_mmio = ioremap(afu->guest->p2n_phys, afu->guest->p2n_size))) {
81462306a36Sopenharmony_ci		dev_err(&afu->dev, "Error mapping AFU(%d) MMIO regions\n",
81562306a36Sopenharmony_ci			afu->slice);
81662306a36Sopenharmony_ci		return -ENOMEM;
81762306a36Sopenharmony_ci	}
81862306a36Sopenharmony_ci	return 0;
81962306a36Sopenharmony_ci}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_cistatic void guest_unmap_slice_regs(struct cxl_afu *afu)
82262306a36Sopenharmony_ci{
82362306a36Sopenharmony_ci	if (afu->p2n_mmio)
82462306a36Sopenharmony_ci		iounmap(afu->p2n_mmio);
82562306a36Sopenharmony_ci}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_cistatic int afu_update_state(struct cxl_afu *afu)
82862306a36Sopenharmony_ci{
82962306a36Sopenharmony_ci	int rc, cur_state;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	rc = afu_read_error_state(afu, &cur_state);
83262306a36Sopenharmony_ci	if (rc)
83362306a36Sopenharmony_ci		return rc;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	if (afu->guest->previous_state == cur_state)
83662306a36Sopenharmony_ci		return 0;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	pr_devel("AFU(%d) update state to %#x\n", afu->slice, cur_state);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	switch (cur_state) {
84162306a36Sopenharmony_ci	case H_STATE_NORMAL:
84262306a36Sopenharmony_ci		afu->guest->previous_state = cur_state;
84362306a36Sopenharmony_ci		break;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	case H_STATE_DISABLE:
84662306a36Sopenharmony_ci		pci_error_handlers(afu, CXL_ERROR_DETECTED_EVENT,
84762306a36Sopenharmony_ci				pci_channel_io_frozen);
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci		cxl_context_detach_all(afu);
85062306a36Sopenharmony_ci		if ((rc = cxl_ops->afu_reset(afu)))
85162306a36Sopenharmony_ci			pr_devel("reset hcall failed %d\n", rc);
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci		rc = afu_read_error_state(afu, &cur_state);
85462306a36Sopenharmony_ci		if (!rc && cur_state == H_STATE_NORMAL) {
85562306a36Sopenharmony_ci			pci_error_handlers(afu, CXL_SLOT_RESET_EVENT,
85662306a36Sopenharmony_ci					pci_channel_io_normal);
85762306a36Sopenharmony_ci			pci_error_handlers(afu, CXL_RESUME_EVENT, 0);
85862306a36Sopenharmony_ci		}
85962306a36Sopenharmony_ci		afu->guest->previous_state = 0;
86062306a36Sopenharmony_ci		break;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	case H_STATE_TEMP_UNAVAILABLE:
86362306a36Sopenharmony_ci		afu->guest->previous_state = cur_state;
86462306a36Sopenharmony_ci		break;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	case H_STATE_PERM_UNAVAILABLE:
86762306a36Sopenharmony_ci		dev_err(&afu->dev, "AFU is in permanent error state\n");
86862306a36Sopenharmony_ci		pci_error_handlers(afu, CXL_ERROR_DETECTED_EVENT,
86962306a36Sopenharmony_ci				pci_channel_io_perm_failure);
87062306a36Sopenharmony_ci		afu->guest->previous_state = cur_state;
87162306a36Sopenharmony_ci		break;
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	default:
87462306a36Sopenharmony_ci		pr_err("Unexpected AFU(%d) error state: %#x\n",
87562306a36Sopenharmony_ci		       afu->slice, cur_state);
87662306a36Sopenharmony_ci		return -EINVAL;
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	return rc;
88062306a36Sopenharmony_ci}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_cistatic void afu_handle_errstate(struct work_struct *work)
88362306a36Sopenharmony_ci{
88462306a36Sopenharmony_ci	struct cxl_afu_guest *afu_guest =
88562306a36Sopenharmony_ci		container_of(to_delayed_work(work), struct cxl_afu_guest, work_err);
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	if (!afu_update_state(afu_guest->parent) &&
88862306a36Sopenharmony_ci	    afu_guest->previous_state == H_STATE_PERM_UNAVAILABLE)
88962306a36Sopenharmony_ci		return;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	if (afu_guest->handle_err)
89262306a36Sopenharmony_ci		schedule_delayed_work(&afu_guest->work_err,
89362306a36Sopenharmony_ci				      msecs_to_jiffies(3000));
89462306a36Sopenharmony_ci}
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_cistatic bool guest_link_ok(struct cxl *cxl, struct cxl_afu *afu)
89762306a36Sopenharmony_ci{
89862306a36Sopenharmony_ci	int state;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	if (afu && (!afu_read_error_state(afu, &state))) {
90162306a36Sopenharmony_ci		if (state == H_STATE_NORMAL)
90262306a36Sopenharmony_ci			return true;
90362306a36Sopenharmony_ci	}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	return false;
90662306a36Sopenharmony_ci}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_cistatic int afu_properties_look_ok(struct cxl_afu *afu)
90962306a36Sopenharmony_ci{
91062306a36Sopenharmony_ci	if (afu->pp_irqs < 0) {
91162306a36Sopenharmony_ci		dev_err(&afu->dev, "Unexpected per-process minimum interrupt value\n");
91262306a36Sopenharmony_ci		return -EINVAL;
91362306a36Sopenharmony_ci	}
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	if (afu->max_procs_virtualised < 1) {
91662306a36Sopenharmony_ci		dev_err(&afu->dev, "Unexpected max number of processes virtualised value\n");
91762306a36Sopenharmony_ci		return -EINVAL;
91862306a36Sopenharmony_ci	}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	return 0;
92162306a36Sopenharmony_ci}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ciint cxl_guest_init_afu(struct cxl *adapter, int slice, struct device_node *afu_np)
92462306a36Sopenharmony_ci{
92562306a36Sopenharmony_ci	struct cxl_afu *afu;
92662306a36Sopenharmony_ci	bool free = true;
92762306a36Sopenharmony_ci	int rc;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	pr_devel("in %s - AFU(%d)\n", __func__, slice);
93062306a36Sopenharmony_ci	if (!(afu = cxl_alloc_afu(adapter, slice)))
93162306a36Sopenharmony_ci		return -ENOMEM;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	if (!(afu->guest = kzalloc(sizeof(struct cxl_afu_guest), GFP_KERNEL))) {
93462306a36Sopenharmony_ci		kfree(afu);
93562306a36Sopenharmony_ci		return -ENOMEM;
93662306a36Sopenharmony_ci	}
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	if ((rc = dev_set_name(&afu->dev, "afu%i.%i",
93962306a36Sopenharmony_ci					  adapter->adapter_num,
94062306a36Sopenharmony_ci					  slice)))
94162306a36Sopenharmony_ci		goto err1;
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	adapter->slices++;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	if ((rc = cxl_of_read_afu_handle(afu, afu_np)))
94662306a36Sopenharmony_ci		goto err1;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	if ((rc = cxl_ops->afu_reset(afu)))
94962306a36Sopenharmony_ci		goto err1;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	if ((rc = cxl_of_read_afu_properties(afu, afu_np)))
95262306a36Sopenharmony_ci		goto err1;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	if ((rc = afu_properties_look_ok(afu)))
95562306a36Sopenharmony_ci		goto err1;
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	if ((rc = guest_map_slice_regs(afu)))
95862306a36Sopenharmony_ci		goto err1;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	if ((rc = guest_register_serr_irq(afu)))
96162306a36Sopenharmony_ci		goto err2;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	/*
96462306a36Sopenharmony_ci	 * After we call this function we must not free the afu directly, even
96562306a36Sopenharmony_ci	 * if it returns an error!
96662306a36Sopenharmony_ci	 */
96762306a36Sopenharmony_ci	if ((rc = cxl_register_afu(afu)))
96862306a36Sopenharmony_ci		goto err_put_dev;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	if ((rc = cxl_sysfs_afu_add(afu)))
97162306a36Sopenharmony_ci		goto err_del_dev;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	/*
97462306a36Sopenharmony_ci	 * pHyp doesn't expose the programming models supported by the
97562306a36Sopenharmony_ci	 * AFU. pHyp currently only supports directed mode. If it adds
97662306a36Sopenharmony_ci	 * dedicated mode later, this version of cxl has no way to
97762306a36Sopenharmony_ci	 * detect it. So we'll initialize the driver, but the first
97862306a36Sopenharmony_ci	 * attach will fail.
97962306a36Sopenharmony_ci	 * Being discussed with pHyp to do better (likely new property)
98062306a36Sopenharmony_ci	 */
98162306a36Sopenharmony_ci	if (afu->max_procs_virtualised == 1)
98262306a36Sopenharmony_ci		afu->modes_supported = CXL_MODE_DEDICATED;
98362306a36Sopenharmony_ci	else
98462306a36Sopenharmony_ci		afu->modes_supported = CXL_MODE_DIRECTED;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	if ((rc = cxl_afu_select_best_mode(afu)))
98762306a36Sopenharmony_ci		goto err_remove_sysfs;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	adapter->afu[afu->slice] = afu;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	afu->enabled = true;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	/*
99462306a36Sopenharmony_ci	 * wake up the cpu periodically to check the state
99562306a36Sopenharmony_ci	 * of the AFU using "afu" stored in the guest structure.
99662306a36Sopenharmony_ci	 */
99762306a36Sopenharmony_ci	afu->guest->parent = afu;
99862306a36Sopenharmony_ci	afu->guest->handle_err = true;
99962306a36Sopenharmony_ci	INIT_DELAYED_WORK(&afu->guest->work_err, afu_handle_errstate);
100062306a36Sopenharmony_ci	schedule_delayed_work(&afu->guest->work_err, msecs_to_jiffies(1000));
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	if ((rc = cxl_pci_vphb_add(afu)))
100362306a36Sopenharmony_ci		dev_info(&afu->dev, "Can't register vPHB\n");
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	return 0;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_cierr_remove_sysfs:
100862306a36Sopenharmony_ci	cxl_sysfs_afu_remove(afu);
100962306a36Sopenharmony_cierr_del_dev:
101062306a36Sopenharmony_ci	device_del(&afu->dev);
101162306a36Sopenharmony_cierr_put_dev:
101262306a36Sopenharmony_ci	put_device(&afu->dev);
101362306a36Sopenharmony_ci	free = false;
101462306a36Sopenharmony_ci	guest_release_serr_irq(afu);
101562306a36Sopenharmony_cierr2:
101662306a36Sopenharmony_ci	guest_unmap_slice_regs(afu);
101762306a36Sopenharmony_cierr1:
101862306a36Sopenharmony_ci	if (free) {
101962306a36Sopenharmony_ci		kfree(afu->guest);
102062306a36Sopenharmony_ci		kfree(afu);
102162306a36Sopenharmony_ci	}
102262306a36Sopenharmony_ci	return rc;
102362306a36Sopenharmony_ci}
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_civoid cxl_guest_remove_afu(struct cxl_afu *afu)
102662306a36Sopenharmony_ci{
102762306a36Sopenharmony_ci	if (!afu)
102862306a36Sopenharmony_ci		return;
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	/* flush and stop pending job */
103162306a36Sopenharmony_ci	afu->guest->handle_err = false;
103262306a36Sopenharmony_ci	flush_delayed_work(&afu->guest->work_err);
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	cxl_pci_vphb_remove(afu);
103562306a36Sopenharmony_ci	cxl_sysfs_afu_remove(afu);
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	spin_lock(&afu->adapter->afu_list_lock);
103862306a36Sopenharmony_ci	afu->adapter->afu[afu->slice] = NULL;
103962306a36Sopenharmony_ci	spin_unlock(&afu->adapter->afu_list_lock);
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	cxl_context_detach_all(afu);
104262306a36Sopenharmony_ci	cxl_ops->afu_deactivate_mode(afu, afu->current_mode);
104362306a36Sopenharmony_ci	guest_release_serr_irq(afu);
104462306a36Sopenharmony_ci	guest_unmap_slice_regs(afu);
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	device_unregister(&afu->dev);
104762306a36Sopenharmony_ci}
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_cistatic void free_adapter(struct cxl *adapter)
105062306a36Sopenharmony_ci{
105162306a36Sopenharmony_ci	struct irq_avail *cur;
105262306a36Sopenharmony_ci	int i;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	if (adapter->guest) {
105562306a36Sopenharmony_ci		if (adapter->guest->irq_avail) {
105662306a36Sopenharmony_ci			for (i = 0; i < adapter->guest->irq_nranges; i++) {
105762306a36Sopenharmony_ci				cur = &adapter->guest->irq_avail[i];
105862306a36Sopenharmony_ci				bitmap_free(cur->bitmap);
105962306a36Sopenharmony_ci			}
106062306a36Sopenharmony_ci			kfree(adapter->guest->irq_avail);
106162306a36Sopenharmony_ci		}
106262306a36Sopenharmony_ci		kfree(adapter->guest->status);
106362306a36Sopenharmony_ci		kfree(adapter->guest);
106462306a36Sopenharmony_ci	}
106562306a36Sopenharmony_ci	cxl_remove_adapter_nr(adapter);
106662306a36Sopenharmony_ci	kfree(adapter);
106762306a36Sopenharmony_ci}
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_cistatic int properties_look_ok(struct cxl *adapter)
107062306a36Sopenharmony_ci{
107162306a36Sopenharmony_ci	/* The absence of this property means that the operational
107262306a36Sopenharmony_ci	 * status is unknown or okay
107362306a36Sopenharmony_ci	 */
107462306a36Sopenharmony_ci	if (strlen(adapter->guest->status) &&
107562306a36Sopenharmony_ci	    strcmp(adapter->guest->status, "okay")) {
107662306a36Sopenharmony_ci		pr_err("ABORTING:Bad operational status of the device\n");
107762306a36Sopenharmony_ci		return -EINVAL;
107862306a36Sopenharmony_ci	}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	return 0;
108162306a36Sopenharmony_ci}
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_cissize_t cxl_guest_read_adapter_vpd(struct cxl *adapter, void *buf, size_t len)
108462306a36Sopenharmony_ci{
108562306a36Sopenharmony_ci	return guest_collect_vpd(adapter, NULL, buf, len);
108662306a36Sopenharmony_ci}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_civoid cxl_guest_remove_adapter(struct cxl *adapter)
108962306a36Sopenharmony_ci{
109062306a36Sopenharmony_ci	pr_devel("in %s\n", __func__);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	cxl_sysfs_adapter_remove(adapter);
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	cxl_guest_remove_chardev(adapter);
109562306a36Sopenharmony_ci	device_unregister(&adapter->dev);
109662306a36Sopenharmony_ci}
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_cistatic void release_adapter(struct device *dev)
109962306a36Sopenharmony_ci{
110062306a36Sopenharmony_ci	free_adapter(to_cxl_adapter(dev));
110162306a36Sopenharmony_ci}
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_cistruct cxl *cxl_guest_init_adapter(struct device_node *np, struct platform_device *pdev)
110462306a36Sopenharmony_ci{
110562306a36Sopenharmony_ci	struct cxl *adapter;
110662306a36Sopenharmony_ci	bool free = true;
110762306a36Sopenharmony_ci	int rc;
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	if (!(adapter = cxl_alloc_adapter()))
111062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	if (!(adapter->guest = kzalloc(sizeof(struct cxl_guest), GFP_KERNEL))) {
111362306a36Sopenharmony_ci		free_adapter(adapter);
111462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
111562306a36Sopenharmony_ci	}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	adapter->slices = 0;
111862306a36Sopenharmony_ci	adapter->guest->pdev = pdev;
111962306a36Sopenharmony_ci	adapter->dev.parent = &pdev->dev;
112062306a36Sopenharmony_ci	adapter->dev.release = release_adapter;
112162306a36Sopenharmony_ci	dev_set_drvdata(&pdev->dev, adapter);
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	/*
112462306a36Sopenharmony_ci	 * Hypervisor controls PSL timebase initialization (p1 register).
112562306a36Sopenharmony_ci	 * On FW840, PSL is initialized.
112662306a36Sopenharmony_ci	 */
112762306a36Sopenharmony_ci	adapter->psl_timebase_synced = true;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	if ((rc = cxl_of_read_adapter_handle(adapter, np)))
113062306a36Sopenharmony_ci		goto err1;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	if ((rc = cxl_of_read_adapter_properties(adapter, np)))
113362306a36Sopenharmony_ci		goto err1;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	if ((rc = properties_look_ok(adapter)))
113662306a36Sopenharmony_ci		goto err1;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	if ((rc = cxl_guest_add_chardev(adapter)))
113962306a36Sopenharmony_ci		goto err1;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	/*
114262306a36Sopenharmony_ci	 * After we call this function we must not free the adapter directly,
114362306a36Sopenharmony_ci	 * even if it returns an error!
114462306a36Sopenharmony_ci	 */
114562306a36Sopenharmony_ci	if ((rc = cxl_register_adapter(adapter)))
114662306a36Sopenharmony_ci		goto err_put_dev;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	if ((rc = cxl_sysfs_adapter_add(adapter)))
114962306a36Sopenharmony_ci		goto err_del_dev;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	/* release the context lock as the adapter is configured */
115262306a36Sopenharmony_ci	cxl_adapter_context_unlock(adapter);
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	return adapter;
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_cierr_del_dev:
115762306a36Sopenharmony_ci	device_del(&adapter->dev);
115862306a36Sopenharmony_cierr_put_dev:
115962306a36Sopenharmony_ci	put_device(&adapter->dev);
116062306a36Sopenharmony_ci	free = false;
116162306a36Sopenharmony_ci	cxl_guest_remove_chardev(adapter);
116262306a36Sopenharmony_cierr1:
116362306a36Sopenharmony_ci	if (free)
116462306a36Sopenharmony_ci		free_adapter(adapter);
116562306a36Sopenharmony_ci	return ERR_PTR(rc);
116662306a36Sopenharmony_ci}
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_civoid cxl_guest_reload_module(struct cxl *adapter)
116962306a36Sopenharmony_ci{
117062306a36Sopenharmony_ci	struct platform_device *pdev;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	pdev = adapter->guest->pdev;
117362306a36Sopenharmony_ci	cxl_guest_remove_adapter(adapter);
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	cxl_of_probe(pdev);
117662306a36Sopenharmony_ci}
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ciconst struct cxl_backend_ops cxl_guest_ops = {
117962306a36Sopenharmony_ci	.module = THIS_MODULE,
118062306a36Sopenharmony_ci	.adapter_reset = guest_reset,
118162306a36Sopenharmony_ci	.alloc_one_irq = guest_alloc_one_irq,
118262306a36Sopenharmony_ci	.release_one_irq = guest_release_one_irq,
118362306a36Sopenharmony_ci	.alloc_irq_ranges = guest_alloc_irq_ranges,
118462306a36Sopenharmony_ci	.release_irq_ranges = guest_release_irq_ranges,
118562306a36Sopenharmony_ci	.setup_irq = NULL,
118662306a36Sopenharmony_ci	.handle_psl_slice_error = guest_handle_psl_slice_error,
118762306a36Sopenharmony_ci	.psl_interrupt = guest_psl_irq,
118862306a36Sopenharmony_ci	.ack_irq = guest_ack_irq,
118962306a36Sopenharmony_ci	.attach_process = guest_attach_process,
119062306a36Sopenharmony_ci	.detach_process = guest_detach_process,
119162306a36Sopenharmony_ci	.update_ivtes = NULL,
119262306a36Sopenharmony_ci	.support_attributes = guest_support_attributes,
119362306a36Sopenharmony_ci	.link_ok = guest_link_ok,
119462306a36Sopenharmony_ci	.release_afu = guest_release_afu,
119562306a36Sopenharmony_ci	.afu_read_err_buffer = guest_afu_read_err_buffer,
119662306a36Sopenharmony_ci	.afu_check_and_enable = guest_afu_check_and_enable,
119762306a36Sopenharmony_ci	.afu_activate_mode = guest_afu_activate_mode,
119862306a36Sopenharmony_ci	.afu_deactivate_mode = guest_afu_deactivate_mode,
119962306a36Sopenharmony_ci	.afu_reset = guest_afu_reset,
120062306a36Sopenharmony_ci	.afu_cr_read8 = guest_afu_cr_read8,
120162306a36Sopenharmony_ci	.afu_cr_read16 = guest_afu_cr_read16,
120262306a36Sopenharmony_ci	.afu_cr_read32 = guest_afu_cr_read32,
120362306a36Sopenharmony_ci	.afu_cr_read64 = guest_afu_cr_read64,
120462306a36Sopenharmony_ci	.afu_cr_write8 = guest_afu_cr_write8,
120562306a36Sopenharmony_ci	.afu_cr_write16 = guest_afu_cr_write16,
120662306a36Sopenharmony_ci	.afu_cr_write32 = guest_afu_cr_write32,
120762306a36Sopenharmony_ci	.read_adapter_vpd = cxl_guest_read_adapter_vpd,
120862306a36Sopenharmony_ci};
1209