162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
362306a36Sopenharmony_ci#include <linux/memregion.h>
462306a36Sopenharmony_ci#include <linux/genalloc.h>
562306a36Sopenharmony_ci#include <linux/device.h>
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/slab.h>
862306a36Sopenharmony_ci#include <linux/uuid.h>
962306a36Sopenharmony_ci#include <linux/sort.h>
1062306a36Sopenharmony_ci#include <linux/idr.h>
1162306a36Sopenharmony_ci#include <cxlmem.h>
1262306a36Sopenharmony_ci#include <cxl.h>
1362306a36Sopenharmony_ci#include "core.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/**
1662306a36Sopenharmony_ci * DOC: cxl core region
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * CXL Regions represent mapped memory capacity in system physical address
1962306a36Sopenharmony_ci * space. Whereas the CXL Root Decoders identify the bounds of potential CXL
2062306a36Sopenharmony_ci * Memory ranges, Regions represent the active mapped capacity by the HDM
2162306a36Sopenharmony_ci * Decoder Capability structures throughout the Host Bridges, Switches, and
2262306a36Sopenharmony_ci * Endpoints in the topology.
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * Region configuration has ordering constraints. UUID may be set at any time
2562306a36Sopenharmony_ci * but is only visible for persistent regions.
2662306a36Sopenharmony_ci * 1. Interleave granularity
2762306a36Sopenharmony_ci * 2. Interleave size
2862306a36Sopenharmony_ci * 3. Decoder targets
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic struct cxl_region *to_cxl_region(struct device *dev);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic ssize_t uuid_show(struct device *dev, struct device_attribute *attr,
3462306a36Sopenharmony_ci			 char *buf)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	struct cxl_region *cxlr = to_cxl_region(dev);
3762306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
3862306a36Sopenharmony_ci	ssize_t rc;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	rc = down_read_interruptible(&cxl_region_rwsem);
4162306a36Sopenharmony_ci	if (rc)
4262306a36Sopenharmony_ci		return rc;
4362306a36Sopenharmony_ci	if (cxlr->mode != CXL_DECODER_PMEM)
4462306a36Sopenharmony_ci		rc = sysfs_emit(buf, "\n");
4562306a36Sopenharmony_ci	else
4662306a36Sopenharmony_ci		rc = sysfs_emit(buf, "%pUb\n", &p->uuid);
4762306a36Sopenharmony_ci	up_read(&cxl_region_rwsem);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	return rc;
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic int is_dup(struct device *match, void *data)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	struct cxl_region_params *p;
5562306a36Sopenharmony_ci	struct cxl_region *cxlr;
5662306a36Sopenharmony_ci	uuid_t *uuid = data;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	if (!is_cxl_region(match))
5962306a36Sopenharmony_ci		return 0;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	lockdep_assert_held(&cxl_region_rwsem);
6262306a36Sopenharmony_ci	cxlr = to_cxl_region(match);
6362306a36Sopenharmony_ci	p = &cxlr->params;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	if (uuid_equal(&p->uuid, uuid)) {
6662306a36Sopenharmony_ci		dev_dbg(match, "already has uuid: %pUb\n", uuid);
6762306a36Sopenharmony_ci		return -EBUSY;
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	return 0;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic ssize_t uuid_store(struct device *dev, struct device_attribute *attr,
7462306a36Sopenharmony_ci			  const char *buf, size_t len)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	struct cxl_region *cxlr = to_cxl_region(dev);
7762306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
7862306a36Sopenharmony_ci	uuid_t temp;
7962306a36Sopenharmony_ci	ssize_t rc;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	if (len != UUID_STRING_LEN + 1)
8262306a36Sopenharmony_ci		return -EINVAL;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	rc = uuid_parse(buf, &temp);
8562306a36Sopenharmony_ci	if (rc)
8662306a36Sopenharmony_ci		return rc;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (uuid_is_null(&temp))
8962306a36Sopenharmony_ci		return -EINVAL;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	rc = down_write_killable(&cxl_region_rwsem);
9262306a36Sopenharmony_ci	if (rc)
9362306a36Sopenharmony_ci		return rc;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (uuid_equal(&p->uuid, &temp))
9662306a36Sopenharmony_ci		goto out;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	rc = -EBUSY;
9962306a36Sopenharmony_ci	if (p->state >= CXL_CONFIG_ACTIVE)
10062306a36Sopenharmony_ci		goto out;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	rc = bus_for_each_dev(&cxl_bus_type, NULL, &temp, is_dup);
10362306a36Sopenharmony_ci	if (rc < 0)
10462306a36Sopenharmony_ci		goto out;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	uuid_copy(&p->uuid, &temp);
10762306a36Sopenharmony_ciout:
10862306a36Sopenharmony_ci	up_write(&cxl_region_rwsem);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	if (rc)
11162306a36Sopenharmony_ci		return rc;
11262306a36Sopenharmony_ci	return len;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_cistatic DEVICE_ATTR_RW(uuid);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic struct cxl_region_ref *cxl_rr_load(struct cxl_port *port,
11762306a36Sopenharmony_ci					  struct cxl_region *cxlr)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	return xa_load(&port->regions, (unsigned long)cxlr);
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic int cxl_region_invalidate_memregion(struct cxl_region *cxlr)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	if (!cpu_cache_has_invalidate_memregion()) {
12562306a36Sopenharmony_ci		if (IS_ENABLED(CONFIG_CXL_REGION_INVALIDATION_TEST)) {
12662306a36Sopenharmony_ci			dev_warn_once(
12762306a36Sopenharmony_ci				&cxlr->dev,
12862306a36Sopenharmony_ci				"Bypassing cpu_cache_invalidate_memregion() for testing!\n");
12962306a36Sopenharmony_ci			return 0;
13062306a36Sopenharmony_ci		} else {
13162306a36Sopenharmony_ci			dev_err(&cxlr->dev,
13262306a36Sopenharmony_ci				"Failed to synchronize CPU cache state\n");
13362306a36Sopenharmony_ci			return -ENXIO;
13462306a36Sopenharmony_ci		}
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	cpu_cache_invalidate_memregion(IORES_DESC_CXL);
13862306a36Sopenharmony_ci	return 0;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic int cxl_region_decode_reset(struct cxl_region *cxlr, int count)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
14462306a36Sopenharmony_ci	int i, rc = 0;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	/*
14762306a36Sopenharmony_ci	 * Before region teardown attempt to flush, and if the flush
14862306a36Sopenharmony_ci	 * fails cancel the region teardown for data consistency
14962306a36Sopenharmony_ci	 * concerns
15062306a36Sopenharmony_ci	 */
15162306a36Sopenharmony_ci	rc = cxl_region_invalidate_memregion(cxlr);
15262306a36Sopenharmony_ci	if (rc)
15362306a36Sopenharmony_ci		return rc;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	for (i = count - 1; i >= 0; i--) {
15662306a36Sopenharmony_ci		struct cxl_endpoint_decoder *cxled = p->targets[i];
15762306a36Sopenharmony_ci		struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
15862306a36Sopenharmony_ci		struct cxl_port *iter = cxled_to_port(cxled);
15962306a36Sopenharmony_ci		struct cxl_dev_state *cxlds = cxlmd->cxlds;
16062306a36Sopenharmony_ci		struct cxl_ep *ep;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci		if (cxlds->rcd)
16362306a36Sopenharmony_ci			goto endpoint_reset;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci		while (!is_cxl_root(to_cxl_port(iter->dev.parent)))
16662306a36Sopenharmony_ci			iter = to_cxl_port(iter->dev.parent);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci		for (ep = cxl_ep_load(iter, cxlmd); iter;
16962306a36Sopenharmony_ci		     iter = ep->next, ep = cxl_ep_load(iter, cxlmd)) {
17062306a36Sopenharmony_ci			struct cxl_region_ref *cxl_rr;
17162306a36Sopenharmony_ci			struct cxl_decoder *cxld;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci			cxl_rr = cxl_rr_load(iter, cxlr);
17462306a36Sopenharmony_ci			cxld = cxl_rr->decoder;
17562306a36Sopenharmony_ci			if (cxld->reset)
17662306a36Sopenharmony_ci				rc = cxld->reset(cxld);
17762306a36Sopenharmony_ci			if (rc)
17862306a36Sopenharmony_ci				return rc;
17962306a36Sopenharmony_ci			set_bit(CXL_REGION_F_NEEDS_RESET, &cxlr->flags);
18062306a36Sopenharmony_ci		}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ciendpoint_reset:
18362306a36Sopenharmony_ci		rc = cxled->cxld.reset(&cxled->cxld);
18462306a36Sopenharmony_ci		if (rc)
18562306a36Sopenharmony_ci			return rc;
18662306a36Sopenharmony_ci		set_bit(CXL_REGION_F_NEEDS_RESET, &cxlr->flags);
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/* all decoders associated with this region have been torn down */
19062306a36Sopenharmony_ci	clear_bit(CXL_REGION_F_NEEDS_RESET, &cxlr->flags);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return 0;
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic int commit_decoder(struct cxl_decoder *cxld)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	struct cxl_switch_decoder *cxlsd = NULL;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (cxld->commit)
20062306a36Sopenharmony_ci		return cxld->commit(cxld);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (is_switch_decoder(&cxld->dev))
20362306a36Sopenharmony_ci		cxlsd = to_cxl_switch_decoder(&cxld->dev);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	if (dev_WARN_ONCE(&cxld->dev, !cxlsd || cxlsd->nr_targets > 1,
20662306a36Sopenharmony_ci			  "->commit() is required\n"))
20762306a36Sopenharmony_ci		return -ENXIO;
20862306a36Sopenharmony_ci	return 0;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic int cxl_region_decode_commit(struct cxl_region *cxlr)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
21462306a36Sopenharmony_ci	int i, rc = 0;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	for (i = 0; i < p->nr_targets; i++) {
21762306a36Sopenharmony_ci		struct cxl_endpoint_decoder *cxled = p->targets[i];
21862306a36Sopenharmony_ci		struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
21962306a36Sopenharmony_ci		struct cxl_region_ref *cxl_rr;
22062306a36Sopenharmony_ci		struct cxl_decoder *cxld;
22162306a36Sopenharmony_ci		struct cxl_port *iter;
22262306a36Sopenharmony_ci		struct cxl_ep *ep;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci		/* commit bottom up */
22562306a36Sopenharmony_ci		for (iter = cxled_to_port(cxled); !is_cxl_root(iter);
22662306a36Sopenharmony_ci		     iter = to_cxl_port(iter->dev.parent)) {
22762306a36Sopenharmony_ci			cxl_rr = cxl_rr_load(iter, cxlr);
22862306a36Sopenharmony_ci			cxld = cxl_rr->decoder;
22962306a36Sopenharmony_ci			rc = commit_decoder(cxld);
23062306a36Sopenharmony_ci			if (rc)
23162306a36Sopenharmony_ci				break;
23262306a36Sopenharmony_ci		}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci		if (rc) {
23562306a36Sopenharmony_ci			/* programming @iter failed, teardown */
23662306a36Sopenharmony_ci			for (ep = cxl_ep_load(iter, cxlmd); ep && iter;
23762306a36Sopenharmony_ci			     iter = ep->next, ep = cxl_ep_load(iter, cxlmd)) {
23862306a36Sopenharmony_ci				cxl_rr = cxl_rr_load(iter, cxlr);
23962306a36Sopenharmony_ci				cxld = cxl_rr->decoder;
24062306a36Sopenharmony_ci				if (cxld->reset)
24162306a36Sopenharmony_ci					cxld->reset(cxld);
24262306a36Sopenharmony_ci			}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci			cxled->cxld.reset(&cxled->cxld);
24562306a36Sopenharmony_ci			goto err;
24662306a36Sopenharmony_ci		}
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	return 0;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cierr:
25262306a36Sopenharmony_ci	/* undo the targets that were successfully committed */
25362306a36Sopenharmony_ci	cxl_region_decode_reset(cxlr, i);
25462306a36Sopenharmony_ci	return rc;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic ssize_t commit_store(struct device *dev, struct device_attribute *attr,
25862306a36Sopenharmony_ci			    const char *buf, size_t len)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct cxl_region *cxlr = to_cxl_region(dev);
26162306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
26262306a36Sopenharmony_ci	bool commit;
26362306a36Sopenharmony_ci	ssize_t rc;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	rc = kstrtobool(buf, &commit);
26662306a36Sopenharmony_ci	if (rc)
26762306a36Sopenharmony_ci		return rc;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	rc = down_write_killable(&cxl_region_rwsem);
27062306a36Sopenharmony_ci	if (rc)
27162306a36Sopenharmony_ci		return rc;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	/* Already in the requested state? */
27462306a36Sopenharmony_ci	if (commit && p->state >= CXL_CONFIG_COMMIT)
27562306a36Sopenharmony_ci		goto out;
27662306a36Sopenharmony_ci	if (!commit && p->state < CXL_CONFIG_COMMIT)
27762306a36Sopenharmony_ci		goto out;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	/* Not ready to commit? */
28062306a36Sopenharmony_ci	if (commit && p->state < CXL_CONFIG_ACTIVE) {
28162306a36Sopenharmony_ci		rc = -ENXIO;
28262306a36Sopenharmony_ci		goto out;
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	/*
28662306a36Sopenharmony_ci	 * Invalidate caches before region setup to drop any speculative
28762306a36Sopenharmony_ci	 * consumption of this address space
28862306a36Sopenharmony_ci	 */
28962306a36Sopenharmony_ci	rc = cxl_region_invalidate_memregion(cxlr);
29062306a36Sopenharmony_ci	if (rc)
29162306a36Sopenharmony_ci		goto out;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	if (commit) {
29462306a36Sopenharmony_ci		rc = cxl_region_decode_commit(cxlr);
29562306a36Sopenharmony_ci		if (rc == 0)
29662306a36Sopenharmony_ci			p->state = CXL_CONFIG_COMMIT;
29762306a36Sopenharmony_ci	} else {
29862306a36Sopenharmony_ci		p->state = CXL_CONFIG_RESET_PENDING;
29962306a36Sopenharmony_ci		up_write(&cxl_region_rwsem);
30062306a36Sopenharmony_ci		device_release_driver(&cxlr->dev);
30162306a36Sopenharmony_ci		down_write(&cxl_region_rwsem);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci		/*
30462306a36Sopenharmony_ci		 * The lock was dropped, so need to revalidate that the reset is
30562306a36Sopenharmony_ci		 * still pending.
30662306a36Sopenharmony_ci		 */
30762306a36Sopenharmony_ci		if (p->state == CXL_CONFIG_RESET_PENDING) {
30862306a36Sopenharmony_ci			rc = cxl_region_decode_reset(cxlr, p->interleave_ways);
30962306a36Sopenharmony_ci			/*
31062306a36Sopenharmony_ci			 * Revert to committed since there may still be active
31162306a36Sopenharmony_ci			 * decoders associated with this region, or move forward
31262306a36Sopenharmony_ci			 * to active to mark the reset successful
31362306a36Sopenharmony_ci			 */
31462306a36Sopenharmony_ci			if (rc)
31562306a36Sopenharmony_ci				p->state = CXL_CONFIG_COMMIT;
31662306a36Sopenharmony_ci			else
31762306a36Sopenharmony_ci				p->state = CXL_CONFIG_ACTIVE;
31862306a36Sopenharmony_ci		}
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ciout:
32262306a36Sopenharmony_ci	up_write(&cxl_region_rwsem);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	if (rc)
32562306a36Sopenharmony_ci		return rc;
32662306a36Sopenharmony_ci	return len;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic ssize_t commit_show(struct device *dev, struct device_attribute *attr,
33062306a36Sopenharmony_ci			   char *buf)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	struct cxl_region *cxlr = to_cxl_region(dev);
33362306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
33462306a36Sopenharmony_ci	ssize_t rc;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	rc = down_read_interruptible(&cxl_region_rwsem);
33762306a36Sopenharmony_ci	if (rc)
33862306a36Sopenharmony_ci		return rc;
33962306a36Sopenharmony_ci	rc = sysfs_emit(buf, "%d\n", p->state >= CXL_CONFIG_COMMIT);
34062306a36Sopenharmony_ci	up_read(&cxl_region_rwsem);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	return rc;
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_cistatic DEVICE_ATTR_RW(commit);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic umode_t cxl_region_visible(struct kobject *kobj, struct attribute *a,
34762306a36Sopenharmony_ci				  int n)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj);
35062306a36Sopenharmony_ci	struct cxl_region *cxlr = to_cxl_region(dev);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	/*
35362306a36Sopenharmony_ci	 * Support tooling that expects to find a 'uuid' attribute for all
35462306a36Sopenharmony_ci	 * regions regardless of mode.
35562306a36Sopenharmony_ci	 */
35662306a36Sopenharmony_ci	if (a == &dev_attr_uuid.attr && cxlr->mode != CXL_DECODER_PMEM)
35762306a36Sopenharmony_ci		return 0444;
35862306a36Sopenharmony_ci	return a->mode;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic ssize_t interleave_ways_show(struct device *dev,
36262306a36Sopenharmony_ci				    struct device_attribute *attr, char *buf)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	struct cxl_region *cxlr = to_cxl_region(dev);
36562306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
36662306a36Sopenharmony_ci	ssize_t rc;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	rc = down_read_interruptible(&cxl_region_rwsem);
36962306a36Sopenharmony_ci	if (rc)
37062306a36Sopenharmony_ci		return rc;
37162306a36Sopenharmony_ci	rc = sysfs_emit(buf, "%d\n", p->interleave_ways);
37262306a36Sopenharmony_ci	up_read(&cxl_region_rwsem);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	return rc;
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic const struct attribute_group *get_cxl_region_target_group(void);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic ssize_t interleave_ways_store(struct device *dev,
38062306a36Sopenharmony_ci				     struct device_attribute *attr,
38162306a36Sopenharmony_ci				     const char *buf, size_t len)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev->parent);
38462306a36Sopenharmony_ci	struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld;
38562306a36Sopenharmony_ci	struct cxl_region *cxlr = to_cxl_region(dev);
38662306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
38762306a36Sopenharmony_ci	unsigned int val, save;
38862306a36Sopenharmony_ci	int rc;
38962306a36Sopenharmony_ci	u8 iw;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	rc = kstrtouint(buf, 0, &val);
39262306a36Sopenharmony_ci	if (rc)
39362306a36Sopenharmony_ci		return rc;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	rc = ways_to_eiw(val, &iw);
39662306a36Sopenharmony_ci	if (rc)
39762306a36Sopenharmony_ci		return rc;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	/*
40062306a36Sopenharmony_ci	 * Even for x3, x6, and x12 interleaves the region interleave must be a
40162306a36Sopenharmony_ci	 * power of 2 multiple of the host bridge interleave.
40262306a36Sopenharmony_ci	 */
40362306a36Sopenharmony_ci	if (!is_power_of_2(val / cxld->interleave_ways) ||
40462306a36Sopenharmony_ci	    (val % cxld->interleave_ways)) {
40562306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "invalid interleave: %d\n", val);
40662306a36Sopenharmony_ci		return -EINVAL;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	rc = down_write_killable(&cxl_region_rwsem);
41062306a36Sopenharmony_ci	if (rc)
41162306a36Sopenharmony_ci		return rc;
41262306a36Sopenharmony_ci	if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) {
41362306a36Sopenharmony_ci		rc = -EBUSY;
41462306a36Sopenharmony_ci		goto out;
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	save = p->interleave_ways;
41862306a36Sopenharmony_ci	p->interleave_ways = val;
41962306a36Sopenharmony_ci	rc = sysfs_update_group(&cxlr->dev.kobj, get_cxl_region_target_group());
42062306a36Sopenharmony_ci	if (rc)
42162306a36Sopenharmony_ci		p->interleave_ways = save;
42262306a36Sopenharmony_ciout:
42362306a36Sopenharmony_ci	up_write(&cxl_region_rwsem);
42462306a36Sopenharmony_ci	if (rc)
42562306a36Sopenharmony_ci		return rc;
42662306a36Sopenharmony_ci	return len;
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(interleave_ways);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cistatic ssize_t interleave_granularity_show(struct device *dev,
43162306a36Sopenharmony_ci					   struct device_attribute *attr,
43262306a36Sopenharmony_ci					   char *buf)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	struct cxl_region *cxlr = to_cxl_region(dev);
43562306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
43662306a36Sopenharmony_ci	ssize_t rc;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	rc = down_read_interruptible(&cxl_region_rwsem);
43962306a36Sopenharmony_ci	if (rc)
44062306a36Sopenharmony_ci		return rc;
44162306a36Sopenharmony_ci	rc = sysfs_emit(buf, "%d\n", p->interleave_granularity);
44262306a36Sopenharmony_ci	up_read(&cxl_region_rwsem);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	return rc;
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic ssize_t interleave_granularity_store(struct device *dev,
44862306a36Sopenharmony_ci					    struct device_attribute *attr,
44962306a36Sopenharmony_ci					    const char *buf, size_t len)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev->parent);
45262306a36Sopenharmony_ci	struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld;
45362306a36Sopenharmony_ci	struct cxl_region *cxlr = to_cxl_region(dev);
45462306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
45562306a36Sopenharmony_ci	int rc, val;
45662306a36Sopenharmony_ci	u16 ig;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	rc = kstrtoint(buf, 0, &val);
45962306a36Sopenharmony_ci	if (rc)
46062306a36Sopenharmony_ci		return rc;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	rc = granularity_to_eig(val, &ig);
46362306a36Sopenharmony_ci	if (rc)
46462306a36Sopenharmony_ci		return rc;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	/*
46762306a36Sopenharmony_ci	 * When the host-bridge is interleaved, disallow region granularity !=
46862306a36Sopenharmony_ci	 * root granularity. Regions with a granularity less than the root
46962306a36Sopenharmony_ci	 * interleave result in needing multiple endpoints to support a single
47062306a36Sopenharmony_ci	 * slot in the interleave (possible to support in the future). Regions
47162306a36Sopenharmony_ci	 * with a granularity greater than the root interleave result in invalid
47262306a36Sopenharmony_ci	 * DPA translations (invalid to support).
47362306a36Sopenharmony_ci	 */
47462306a36Sopenharmony_ci	if (cxld->interleave_ways > 1 && val != cxld->interleave_granularity)
47562306a36Sopenharmony_ci		return -EINVAL;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	rc = down_write_killable(&cxl_region_rwsem);
47862306a36Sopenharmony_ci	if (rc)
47962306a36Sopenharmony_ci		return rc;
48062306a36Sopenharmony_ci	if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) {
48162306a36Sopenharmony_ci		rc = -EBUSY;
48262306a36Sopenharmony_ci		goto out;
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	p->interleave_granularity = val;
48662306a36Sopenharmony_ciout:
48762306a36Sopenharmony_ci	up_write(&cxl_region_rwsem);
48862306a36Sopenharmony_ci	if (rc)
48962306a36Sopenharmony_ci		return rc;
49062306a36Sopenharmony_ci	return len;
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_cistatic DEVICE_ATTR_RW(interleave_granularity);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_cistatic ssize_t resource_show(struct device *dev, struct device_attribute *attr,
49562306a36Sopenharmony_ci			     char *buf)
49662306a36Sopenharmony_ci{
49762306a36Sopenharmony_ci	struct cxl_region *cxlr = to_cxl_region(dev);
49862306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
49962306a36Sopenharmony_ci	u64 resource = -1ULL;
50062306a36Sopenharmony_ci	ssize_t rc;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	rc = down_read_interruptible(&cxl_region_rwsem);
50362306a36Sopenharmony_ci	if (rc)
50462306a36Sopenharmony_ci		return rc;
50562306a36Sopenharmony_ci	if (p->res)
50662306a36Sopenharmony_ci		resource = p->res->start;
50762306a36Sopenharmony_ci	rc = sysfs_emit(buf, "%#llx\n", resource);
50862306a36Sopenharmony_ci	up_read(&cxl_region_rwsem);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	return rc;
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(resource);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_cistatic ssize_t mode_show(struct device *dev, struct device_attribute *attr,
51562306a36Sopenharmony_ci			 char *buf)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	struct cxl_region *cxlr = to_cxl_region(dev);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", cxl_decoder_mode_name(cxlr->mode));
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(mode);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_cistatic int alloc_hpa(struct cxl_region *cxlr, resource_size_t size)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
52662306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
52762306a36Sopenharmony_ci	struct resource *res;
52862306a36Sopenharmony_ci	u64 remainder = 0;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	lockdep_assert_held_write(&cxl_region_rwsem);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	/* Nothing to do... */
53362306a36Sopenharmony_ci	if (p->res && resource_size(p->res) == size)
53462306a36Sopenharmony_ci		return 0;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	/* To change size the old size must be freed first */
53762306a36Sopenharmony_ci	if (p->res)
53862306a36Sopenharmony_ci		return -EBUSY;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE)
54162306a36Sopenharmony_ci		return -EBUSY;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	/* ways, granularity and uuid (if PMEM) need to be set before HPA */
54462306a36Sopenharmony_ci	if (!p->interleave_ways || !p->interleave_granularity ||
54562306a36Sopenharmony_ci	    (cxlr->mode == CXL_DECODER_PMEM && uuid_is_null(&p->uuid)))
54662306a36Sopenharmony_ci		return -ENXIO;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	div64_u64_rem(size, (u64)SZ_256M * p->interleave_ways, &remainder);
54962306a36Sopenharmony_ci	if (remainder)
55062306a36Sopenharmony_ci		return -EINVAL;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	res = alloc_free_mem_region(cxlrd->res, size, SZ_256M,
55362306a36Sopenharmony_ci				    dev_name(&cxlr->dev));
55462306a36Sopenharmony_ci	if (IS_ERR(res)) {
55562306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "failed to allocate HPA: %ld\n",
55662306a36Sopenharmony_ci			PTR_ERR(res));
55762306a36Sopenharmony_ci		return PTR_ERR(res);
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	p->res = res;
56162306a36Sopenharmony_ci	p->state = CXL_CONFIG_INTERLEAVE_ACTIVE;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	return 0;
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic void cxl_region_iomem_release(struct cxl_region *cxlr)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	if (device_is_registered(&cxlr->dev))
57162306a36Sopenharmony_ci		lockdep_assert_held_write(&cxl_region_rwsem);
57262306a36Sopenharmony_ci	if (p->res) {
57362306a36Sopenharmony_ci		/*
57462306a36Sopenharmony_ci		 * Autodiscovered regions may not have been able to insert their
57562306a36Sopenharmony_ci		 * resource.
57662306a36Sopenharmony_ci		 */
57762306a36Sopenharmony_ci		if (p->res->parent)
57862306a36Sopenharmony_ci			remove_resource(p->res);
57962306a36Sopenharmony_ci		kfree(p->res);
58062306a36Sopenharmony_ci		p->res = NULL;
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic int free_hpa(struct cxl_region *cxlr)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	lockdep_assert_held_write(&cxl_region_rwsem);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	if (!p->res)
59162306a36Sopenharmony_ci		return 0;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	if (p->state >= CXL_CONFIG_ACTIVE)
59462306a36Sopenharmony_ci		return -EBUSY;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	cxl_region_iomem_release(cxlr);
59762306a36Sopenharmony_ci	p->state = CXL_CONFIG_IDLE;
59862306a36Sopenharmony_ci	return 0;
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_cistatic ssize_t size_store(struct device *dev, struct device_attribute *attr,
60262306a36Sopenharmony_ci			  const char *buf, size_t len)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	struct cxl_region *cxlr = to_cxl_region(dev);
60562306a36Sopenharmony_ci	u64 val;
60662306a36Sopenharmony_ci	int rc;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	rc = kstrtou64(buf, 0, &val);
60962306a36Sopenharmony_ci	if (rc)
61062306a36Sopenharmony_ci		return rc;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	rc = down_write_killable(&cxl_region_rwsem);
61362306a36Sopenharmony_ci	if (rc)
61462306a36Sopenharmony_ci		return rc;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	if (val)
61762306a36Sopenharmony_ci		rc = alloc_hpa(cxlr, val);
61862306a36Sopenharmony_ci	else
61962306a36Sopenharmony_ci		rc = free_hpa(cxlr);
62062306a36Sopenharmony_ci	up_write(&cxl_region_rwsem);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	if (rc)
62362306a36Sopenharmony_ci		return rc;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	return len;
62662306a36Sopenharmony_ci}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_cistatic ssize_t size_show(struct device *dev, struct device_attribute *attr,
62962306a36Sopenharmony_ci			 char *buf)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	struct cxl_region *cxlr = to_cxl_region(dev);
63262306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
63362306a36Sopenharmony_ci	u64 size = 0;
63462306a36Sopenharmony_ci	ssize_t rc;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	rc = down_read_interruptible(&cxl_region_rwsem);
63762306a36Sopenharmony_ci	if (rc)
63862306a36Sopenharmony_ci		return rc;
63962306a36Sopenharmony_ci	if (p->res)
64062306a36Sopenharmony_ci		size = resource_size(p->res);
64162306a36Sopenharmony_ci	rc = sysfs_emit(buf, "%#llx\n", size);
64262306a36Sopenharmony_ci	up_read(&cxl_region_rwsem);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	return rc;
64562306a36Sopenharmony_ci}
64662306a36Sopenharmony_cistatic DEVICE_ATTR_RW(size);
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_cistatic struct attribute *cxl_region_attrs[] = {
64962306a36Sopenharmony_ci	&dev_attr_uuid.attr,
65062306a36Sopenharmony_ci	&dev_attr_commit.attr,
65162306a36Sopenharmony_ci	&dev_attr_interleave_ways.attr,
65262306a36Sopenharmony_ci	&dev_attr_interleave_granularity.attr,
65362306a36Sopenharmony_ci	&dev_attr_resource.attr,
65462306a36Sopenharmony_ci	&dev_attr_size.attr,
65562306a36Sopenharmony_ci	&dev_attr_mode.attr,
65662306a36Sopenharmony_ci	NULL,
65762306a36Sopenharmony_ci};
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_cistatic const struct attribute_group cxl_region_group = {
66062306a36Sopenharmony_ci	.attrs = cxl_region_attrs,
66162306a36Sopenharmony_ci	.is_visible = cxl_region_visible,
66262306a36Sopenharmony_ci};
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_cistatic size_t show_targetN(struct cxl_region *cxlr, char *buf, int pos)
66562306a36Sopenharmony_ci{
66662306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
66762306a36Sopenharmony_ci	struct cxl_endpoint_decoder *cxled;
66862306a36Sopenharmony_ci	int rc;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	rc = down_read_interruptible(&cxl_region_rwsem);
67162306a36Sopenharmony_ci	if (rc)
67262306a36Sopenharmony_ci		return rc;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	if (pos >= p->interleave_ways) {
67562306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "position %d out of range %d\n", pos,
67662306a36Sopenharmony_ci			p->interleave_ways);
67762306a36Sopenharmony_ci		rc = -ENXIO;
67862306a36Sopenharmony_ci		goto out;
67962306a36Sopenharmony_ci	}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	cxled = p->targets[pos];
68262306a36Sopenharmony_ci	if (!cxled)
68362306a36Sopenharmony_ci		rc = sysfs_emit(buf, "\n");
68462306a36Sopenharmony_ci	else
68562306a36Sopenharmony_ci		rc = sysfs_emit(buf, "%s\n", dev_name(&cxled->cxld.dev));
68662306a36Sopenharmony_ciout:
68762306a36Sopenharmony_ci	up_read(&cxl_region_rwsem);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	return rc;
69062306a36Sopenharmony_ci}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_cistatic int match_free_decoder(struct device *dev, void *data)
69362306a36Sopenharmony_ci{
69462306a36Sopenharmony_ci	struct cxl_decoder *cxld;
69562306a36Sopenharmony_ci	int *id = data;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	if (!is_switch_decoder(dev))
69862306a36Sopenharmony_ci		return 0;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	cxld = to_cxl_decoder(dev);
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	/* enforce ordered allocation */
70362306a36Sopenharmony_ci	if (cxld->id != *id)
70462306a36Sopenharmony_ci		return 0;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	if (!cxld->region)
70762306a36Sopenharmony_ci		return 1;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	(*id)++;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	return 0;
71262306a36Sopenharmony_ci}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_cistatic int match_auto_decoder(struct device *dev, void *data)
71562306a36Sopenharmony_ci{
71662306a36Sopenharmony_ci	struct cxl_region_params *p = data;
71762306a36Sopenharmony_ci	struct cxl_decoder *cxld;
71862306a36Sopenharmony_ci	struct range *r;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	if (!is_switch_decoder(dev))
72162306a36Sopenharmony_ci		return 0;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	cxld = to_cxl_decoder(dev);
72462306a36Sopenharmony_ci	r = &cxld->hpa_range;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	if (p->res && p->res->start == r->start && p->res->end == r->end)
72762306a36Sopenharmony_ci		return 1;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	return 0;
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_cistatic struct cxl_decoder *
73362306a36Sopenharmony_cicxl_region_find_decoder(struct cxl_port *port,
73462306a36Sopenharmony_ci			struct cxl_endpoint_decoder *cxled,
73562306a36Sopenharmony_ci			struct cxl_region *cxlr)
73662306a36Sopenharmony_ci{
73762306a36Sopenharmony_ci	struct device *dev;
73862306a36Sopenharmony_ci	int id = 0;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	if (port == cxled_to_port(cxled))
74162306a36Sopenharmony_ci		return &cxled->cxld;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	if (test_bit(CXL_REGION_F_AUTO, &cxlr->flags))
74462306a36Sopenharmony_ci		dev = device_find_child(&port->dev, &cxlr->params,
74562306a36Sopenharmony_ci					match_auto_decoder);
74662306a36Sopenharmony_ci	else
74762306a36Sopenharmony_ci		dev = device_find_child(&port->dev, &id, match_free_decoder);
74862306a36Sopenharmony_ci	if (!dev)
74962306a36Sopenharmony_ci		return NULL;
75062306a36Sopenharmony_ci	/*
75162306a36Sopenharmony_ci	 * This decoder is pinned registered as long as the endpoint decoder is
75262306a36Sopenharmony_ci	 * registered, and endpoint decoder unregistration holds the
75362306a36Sopenharmony_ci	 * cxl_region_rwsem over unregister events, so no need to hold on to
75462306a36Sopenharmony_ci	 * this extra reference.
75562306a36Sopenharmony_ci	 */
75662306a36Sopenharmony_ci	put_device(dev);
75762306a36Sopenharmony_ci	return to_cxl_decoder(dev);
75862306a36Sopenharmony_ci}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_cistatic bool auto_order_ok(struct cxl_port *port, struct cxl_region *cxlr_iter,
76162306a36Sopenharmony_ci			  struct cxl_decoder *cxld)
76262306a36Sopenharmony_ci{
76362306a36Sopenharmony_ci	struct cxl_region_ref *rr = cxl_rr_load(port, cxlr_iter);
76462306a36Sopenharmony_ci	struct cxl_decoder *cxld_iter = rr->decoder;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	/*
76762306a36Sopenharmony_ci	 * Allow the out of order assembly of auto-discovered regions.
76862306a36Sopenharmony_ci	 * Per CXL Spec 3.1 8.2.4.20.12 software must commit decoders
76962306a36Sopenharmony_ci	 * in HPA order. Confirm that the decoder with the lesser HPA
77062306a36Sopenharmony_ci	 * starting address has the lesser id.
77162306a36Sopenharmony_ci	 */
77262306a36Sopenharmony_ci	dev_dbg(&cxld->dev, "check for HPA violation %s:%d < %s:%d\n",
77362306a36Sopenharmony_ci		dev_name(&cxld->dev), cxld->id,
77462306a36Sopenharmony_ci		dev_name(&cxld_iter->dev), cxld_iter->id);
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	if (cxld_iter->id > cxld->id)
77762306a36Sopenharmony_ci		return true;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	return false;
78062306a36Sopenharmony_ci}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_cistatic struct cxl_region_ref *
78362306a36Sopenharmony_cialloc_region_ref(struct cxl_port *port, struct cxl_region *cxlr,
78462306a36Sopenharmony_ci		 struct cxl_endpoint_decoder *cxled)
78562306a36Sopenharmony_ci{
78662306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
78762306a36Sopenharmony_ci	struct cxl_region_ref *cxl_rr, *iter;
78862306a36Sopenharmony_ci	unsigned long index;
78962306a36Sopenharmony_ci	int rc;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	xa_for_each(&port->regions, index, iter) {
79262306a36Sopenharmony_ci		struct cxl_region_params *ip = &iter->region->params;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci		if (!ip->res || ip->res->start < p->res->start)
79562306a36Sopenharmony_ci			continue;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci		if (test_bit(CXL_REGION_F_AUTO, &cxlr->flags)) {
79862306a36Sopenharmony_ci			struct cxl_decoder *cxld;
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci			cxld = cxl_region_find_decoder(port, cxled, cxlr);
80162306a36Sopenharmony_ci			if (auto_order_ok(port, iter->region, cxld))
80262306a36Sopenharmony_ci				continue;
80362306a36Sopenharmony_ci		}
80462306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "%s: HPA order violation %s:%pr vs %pr\n",
80562306a36Sopenharmony_ci			dev_name(&port->dev),
80662306a36Sopenharmony_ci			dev_name(&iter->region->dev), ip->res, p->res);
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci		return ERR_PTR(-EBUSY);
80962306a36Sopenharmony_ci	}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	cxl_rr = kzalloc(sizeof(*cxl_rr), GFP_KERNEL);
81262306a36Sopenharmony_ci	if (!cxl_rr)
81362306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
81462306a36Sopenharmony_ci	cxl_rr->port = port;
81562306a36Sopenharmony_ci	cxl_rr->region = cxlr;
81662306a36Sopenharmony_ci	cxl_rr->nr_targets = 1;
81762306a36Sopenharmony_ci	xa_init(&cxl_rr->endpoints);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	rc = xa_insert(&port->regions, (unsigned long)cxlr, cxl_rr, GFP_KERNEL);
82062306a36Sopenharmony_ci	if (rc) {
82162306a36Sopenharmony_ci		dev_dbg(&cxlr->dev,
82262306a36Sopenharmony_ci			"%s: failed to track region reference: %d\n",
82362306a36Sopenharmony_ci			dev_name(&port->dev), rc);
82462306a36Sopenharmony_ci		kfree(cxl_rr);
82562306a36Sopenharmony_ci		return ERR_PTR(rc);
82662306a36Sopenharmony_ci	}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	return cxl_rr;
82962306a36Sopenharmony_ci}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_cistatic void cxl_rr_free_decoder(struct cxl_region_ref *cxl_rr)
83262306a36Sopenharmony_ci{
83362306a36Sopenharmony_ci	struct cxl_region *cxlr = cxl_rr->region;
83462306a36Sopenharmony_ci	struct cxl_decoder *cxld = cxl_rr->decoder;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	if (!cxld)
83762306a36Sopenharmony_ci		return;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	dev_WARN_ONCE(&cxlr->dev, cxld->region != cxlr, "region mismatch\n");
84062306a36Sopenharmony_ci	if (cxld->region == cxlr) {
84162306a36Sopenharmony_ci		cxld->region = NULL;
84262306a36Sopenharmony_ci		put_device(&cxlr->dev);
84362306a36Sopenharmony_ci	}
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_cistatic void free_region_ref(struct cxl_region_ref *cxl_rr)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	struct cxl_port *port = cxl_rr->port;
84962306a36Sopenharmony_ci	struct cxl_region *cxlr = cxl_rr->region;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	cxl_rr_free_decoder(cxl_rr);
85262306a36Sopenharmony_ci	xa_erase(&port->regions, (unsigned long)cxlr);
85362306a36Sopenharmony_ci	xa_destroy(&cxl_rr->endpoints);
85462306a36Sopenharmony_ci	kfree(cxl_rr);
85562306a36Sopenharmony_ci}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_cistatic int cxl_rr_ep_add(struct cxl_region_ref *cxl_rr,
85862306a36Sopenharmony_ci			 struct cxl_endpoint_decoder *cxled)
85962306a36Sopenharmony_ci{
86062306a36Sopenharmony_ci	int rc;
86162306a36Sopenharmony_ci	struct cxl_port *port = cxl_rr->port;
86262306a36Sopenharmony_ci	struct cxl_region *cxlr = cxl_rr->region;
86362306a36Sopenharmony_ci	struct cxl_decoder *cxld = cxl_rr->decoder;
86462306a36Sopenharmony_ci	struct cxl_ep *ep = cxl_ep_load(port, cxled_to_memdev(cxled));
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	if (ep) {
86762306a36Sopenharmony_ci		rc = xa_insert(&cxl_rr->endpoints, (unsigned long)cxled, ep,
86862306a36Sopenharmony_ci			       GFP_KERNEL);
86962306a36Sopenharmony_ci		if (rc)
87062306a36Sopenharmony_ci			return rc;
87162306a36Sopenharmony_ci	}
87262306a36Sopenharmony_ci	cxl_rr->nr_eps++;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	if (!cxld->region) {
87562306a36Sopenharmony_ci		cxld->region = cxlr;
87662306a36Sopenharmony_ci		get_device(&cxlr->dev);
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	return 0;
88062306a36Sopenharmony_ci}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_cistatic int cxl_rr_alloc_decoder(struct cxl_port *port, struct cxl_region *cxlr,
88362306a36Sopenharmony_ci				struct cxl_endpoint_decoder *cxled,
88462306a36Sopenharmony_ci				struct cxl_region_ref *cxl_rr)
88562306a36Sopenharmony_ci{
88662306a36Sopenharmony_ci	struct cxl_decoder *cxld;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	cxld = cxl_region_find_decoder(port, cxled, cxlr);
88962306a36Sopenharmony_ci	if (!cxld) {
89062306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "%s: no decoder available\n",
89162306a36Sopenharmony_ci			dev_name(&port->dev));
89262306a36Sopenharmony_ci		return -EBUSY;
89362306a36Sopenharmony_ci	}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	if (cxld->region) {
89662306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "%s: %s already attached to %s\n",
89762306a36Sopenharmony_ci			dev_name(&port->dev), dev_name(&cxld->dev),
89862306a36Sopenharmony_ci			dev_name(&cxld->region->dev));
89962306a36Sopenharmony_ci		return -EBUSY;
90062306a36Sopenharmony_ci	}
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	/*
90362306a36Sopenharmony_ci	 * Endpoints should already match the region type, but backstop that
90462306a36Sopenharmony_ci	 * assumption with an assertion. Switch-decoders change mapping-type
90562306a36Sopenharmony_ci	 * based on what is mapped when they are assigned to a region.
90662306a36Sopenharmony_ci	 */
90762306a36Sopenharmony_ci	dev_WARN_ONCE(&cxlr->dev,
90862306a36Sopenharmony_ci		      port == cxled_to_port(cxled) &&
90962306a36Sopenharmony_ci			      cxld->target_type != cxlr->type,
91062306a36Sopenharmony_ci		      "%s:%s mismatch decoder type %d -> %d\n",
91162306a36Sopenharmony_ci		      dev_name(&cxled_to_memdev(cxled)->dev),
91262306a36Sopenharmony_ci		      dev_name(&cxld->dev), cxld->target_type, cxlr->type);
91362306a36Sopenharmony_ci	cxld->target_type = cxlr->type;
91462306a36Sopenharmony_ci	cxl_rr->decoder = cxld;
91562306a36Sopenharmony_ci	return 0;
91662306a36Sopenharmony_ci}
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci/**
91962306a36Sopenharmony_ci * cxl_port_attach_region() - track a region's interest in a port by endpoint
92062306a36Sopenharmony_ci * @port: port to add a new region reference 'struct cxl_region_ref'
92162306a36Sopenharmony_ci * @cxlr: region to attach to @port
92262306a36Sopenharmony_ci * @cxled: endpoint decoder used to create or further pin a region reference
92362306a36Sopenharmony_ci * @pos: interleave position of @cxled in @cxlr
92462306a36Sopenharmony_ci *
92562306a36Sopenharmony_ci * The attach event is an opportunity to validate CXL decode setup
92662306a36Sopenharmony_ci * constraints and record metadata needed for programming HDM decoders,
92762306a36Sopenharmony_ci * in particular decoder target lists.
92862306a36Sopenharmony_ci *
92962306a36Sopenharmony_ci * The steps are:
93062306a36Sopenharmony_ci *
93162306a36Sopenharmony_ci * - validate that there are no other regions with a higher HPA already
93262306a36Sopenharmony_ci *   associated with @port
93362306a36Sopenharmony_ci * - establish a region reference if one is not already present
93462306a36Sopenharmony_ci *
93562306a36Sopenharmony_ci *   - additionally allocate a decoder instance that will host @cxlr on
93662306a36Sopenharmony_ci *     @port
93762306a36Sopenharmony_ci *
93862306a36Sopenharmony_ci * - pin the region reference by the endpoint
93962306a36Sopenharmony_ci * - account for how many entries in @port's target list are needed to
94062306a36Sopenharmony_ci *   cover all of the added endpoints.
94162306a36Sopenharmony_ci */
94262306a36Sopenharmony_cistatic int cxl_port_attach_region(struct cxl_port *port,
94362306a36Sopenharmony_ci				  struct cxl_region *cxlr,
94462306a36Sopenharmony_ci				  struct cxl_endpoint_decoder *cxled, int pos)
94562306a36Sopenharmony_ci{
94662306a36Sopenharmony_ci	struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
94762306a36Sopenharmony_ci	struct cxl_ep *ep = cxl_ep_load(port, cxlmd);
94862306a36Sopenharmony_ci	struct cxl_region_ref *cxl_rr;
94962306a36Sopenharmony_ci	bool nr_targets_inc = false;
95062306a36Sopenharmony_ci	struct cxl_decoder *cxld;
95162306a36Sopenharmony_ci	unsigned long index;
95262306a36Sopenharmony_ci	int rc = -EBUSY;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	lockdep_assert_held_write(&cxl_region_rwsem);
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	cxl_rr = cxl_rr_load(port, cxlr);
95762306a36Sopenharmony_ci	if (cxl_rr) {
95862306a36Sopenharmony_ci		struct cxl_ep *ep_iter;
95962306a36Sopenharmony_ci		int found = 0;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci		/*
96262306a36Sopenharmony_ci		 * Walk the existing endpoints that have been attached to
96362306a36Sopenharmony_ci		 * @cxlr at @port and see if they share the same 'next' port
96462306a36Sopenharmony_ci		 * in the downstream direction. I.e. endpoints that share common
96562306a36Sopenharmony_ci		 * upstream switch.
96662306a36Sopenharmony_ci		 */
96762306a36Sopenharmony_ci		xa_for_each(&cxl_rr->endpoints, index, ep_iter) {
96862306a36Sopenharmony_ci			if (ep_iter == ep)
96962306a36Sopenharmony_ci				continue;
97062306a36Sopenharmony_ci			if (ep_iter->next == ep->next) {
97162306a36Sopenharmony_ci				found++;
97262306a36Sopenharmony_ci				break;
97362306a36Sopenharmony_ci			}
97462306a36Sopenharmony_ci		}
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci		/*
97762306a36Sopenharmony_ci		 * New target port, or @port is an endpoint port that always
97862306a36Sopenharmony_ci		 * accounts its own local decode as a target.
97962306a36Sopenharmony_ci		 */
98062306a36Sopenharmony_ci		if (!found || !ep->next) {
98162306a36Sopenharmony_ci			cxl_rr->nr_targets++;
98262306a36Sopenharmony_ci			nr_targets_inc = true;
98362306a36Sopenharmony_ci		}
98462306a36Sopenharmony_ci	} else {
98562306a36Sopenharmony_ci		cxl_rr = alloc_region_ref(port, cxlr, cxled);
98662306a36Sopenharmony_ci		if (IS_ERR(cxl_rr)) {
98762306a36Sopenharmony_ci			dev_dbg(&cxlr->dev,
98862306a36Sopenharmony_ci				"%s: failed to allocate region reference\n",
98962306a36Sopenharmony_ci				dev_name(&port->dev));
99062306a36Sopenharmony_ci			return PTR_ERR(cxl_rr);
99162306a36Sopenharmony_ci		}
99262306a36Sopenharmony_ci		nr_targets_inc = true;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci		rc = cxl_rr_alloc_decoder(port, cxlr, cxled, cxl_rr);
99562306a36Sopenharmony_ci		if (rc)
99662306a36Sopenharmony_ci			goto out_erase;
99762306a36Sopenharmony_ci	}
99862306a36Sopenharmony_ci	cxld = cxl_rr->decoder;
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	rc = cxl_rr_ep_add(cxl_rr, cxled);
100162306a36Sopenharmony_ci	if (rc) {
100262306a36Sopenharmony_ci		dev_dbg(&cxlr->dev,
100362306a36Sopenharmony_ci			"%s: failed to track endpoint %s:%s reference\n",
100462306a36Sopenharmony_ci			dev_name(&port->dev), dev_name(&cxlmd->dev),
100562306a36Sopenharmony_ci			dev_name(&cxld->dev));
100662306a36Sopenharmony_ci		goto out_erase;
100762306a36Sopenharmony_ci	}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	dev_dbg(&cxlr->dev,
101062306a36Sopenharmony_ci		"%s:%s %s add: %s:%s @ %d next: %s nr_eps: %d nr_targets: %d\n",
101162306a36Sopenharmony_ci		dev_name(port->uport_dev), dev_name(&port->dev),
101262306a36Sopenharmony_ci		dev_name(&cxld->dev), dev_name(&cxlmd->dev),
101362306a36Sopenharmony_ci		dev_name(&cxled->cxld.dev), pos,
101462306a36Sopenharmony_ci		ep ? ep->next ? dev_name(ep->next->uport_dev) :
101562306a36Sopenharmony_ci				      dev_name(&cxlmd->dev) :
101662306a36Sopenharmony_ci			   "none",
101762306a36Sopenharmony_ci		cxl_rr->nr_eps, cxl_rr->nr_targets);
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	return 0;
102062306a36Sopenharmony_ciout_erase:
102162306a36Sopenharmony_ci	if (nr_targets_inc)
102262306a36Sopenharmony_ci		cxl_rr->nr_targets--;
102362306a36Sopenharmony_ci	if (cxl_rr->nr_eps == 0)
102462306a36Sopenharmony_ci		free_region_ref(cxl_rr);
102562306a36Sopenharmony_ci	return rc;
102662306a36Sopenharmony_ci}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_cistatic void cxl_port_detach_region(struct cxl_port *port,
102962306a36Sopenharmony_ci				   struct cxl_region *cxlr,
103062306a36Sopenharmony_ci				   struct cxl_endpoint_decoder *cxled)
103162306a36Sopenharmony_ci{
103262306a36Sopenharmony_ci	struct cxl_region_ref *cxl_rr;
103362306a36Sopenharmony_ci	struct cxl_ep *ep = NULL;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	lockdep_assert_held_write(&cxl_region_rwsem);
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	cxl_rr = cxl_rr_load(port, cxlr);
103862306a36Sopenharmony_ci	if (!cxl_rr)
103962306a36Sopenharmony_ci		return;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	/*
104262306a36Sopenharmony_ci	 * Endpoint ports do not carry cxl_ep references, and they
104362306a36Sopenharmony_ci	 * never target more than one endpoint by definition
104462306a36Sopenharmony_ci	 */
104562306a36Sopenharmony_ci	if (cxl_rr->decoder == &cxled->cxld)
104662306a36Sopenharmony_ci		cxl_rr->nr_eps--;
104762306a36Sopenharmony_ci	else
104862306a36Sopenharmony_ci		ep = xa_erase(&cxl_rr->endpoints, (unsigned long)cxled);
104962306a36Sopenharmony_ci	if (ep) {
105062306a36Sopenharmony_ci		struct cxl_ep *ep_iter;
105162306a36Sopenharmony_ci		unsigned long index;
105262306a36Sopenharmony_ci		int found = 0;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci		cxl_rr->nr_eps--;
105562306a36Sopenharmony_ci		xa_for_each(&cxl_rr->endpoints, index, ep_iter) {
105662306a36Sopenharmony_ci			if (ep_iter->next == ep->next) {
105762306a36Sopenharmony_ci				found++;
105862306a36Sopenharmony_ci				break;
105962306a36Sopenharmony_ci			}
106062306a36Sopenharmony_ci		}
106162306a36Sopenharmony_ci		if (!found)
106262306a36Sopenharmony_ci			cxl_rr->nr_targets--;
106362306a36Sopenharmony_ci	}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	if (cxl_rr->nr_eps == 0)
106662306a36Sopenharmony_ci		free_region_ref(cxl_rr);
106762306a36Sopenharmony_ci}
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_cistatic int check_last_peer(struct cxl_endpoint_decoder *cxled,
107062306a36Sopenharmony_ci			   struct cxl_ep *ep, struct cxl_region_ref *cxl_rr,
107162306a36Sopenharmony_ci			   int distance)
107262306a36Sopenharmony_ci{
107362306a36Sopenharmony_ci	struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
107462306a36Sopenharmony_ci	struct cxl_region *cxlr = cxl_rr->region;
107562306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
107662306a36Sopenharmony_ci	struct cxl_endpoint_decoder *cxled_peer;
107762306a36Sopenharmony_ci	struct cxl_port *port = cxl_rr->port;
107862306a36Sopenharmony_ci	struct cxl_memdev *cxlmd_peer;
107962306a36Sopenharmony_ci	struct cxl_ep *ep_peer;
108062306a36Sopenharmony_ci	int pos = cxled->pos;
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	/*
108362306a36Sopenharmony_ci	 * If this position wants to share a dport with the last endpoint mapped
108462306a36Sopenharmony_ci	 * then that endpoint, at index 'position - distance', must also be
108562306a36Sopenharmony_ci	 * mapped by this dport.
108662306a36Sopenharmony_ci	 */
108762306a36Sopenharmony_ci	if (pos < distance) {
108862306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "%s:%s: cannot host %s:%s at %d\n",
108962306a36Sopenharmony_ci			dev_name(port->uport_dev), dev_name(&port->dev),
109062306a36Sopenharmony_ci			dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos);
109162306a36Sopenharmony_ci		return -ENXIO;
109262306a36Sopenharmony_ci	}
109362306a36Sopenharmony_ci	cxled_peer = p->targets[pos - distance];
109462306a36Sopenharmony_ci	cxlmd_peer = cxled_to_memdev(cxled_peer);
109562306a36Sopenharmony_ci	ep_peer = cxl_ep_load(port, cxlmd_peer);
109662306a36Sopenharmony_ci	if (ep->dport != ep_peer->dport) {
109762306a36Sopenharmony_ci		dev_dbg(&cxlr->dev,
109862306a36Sopenharmony_ci			"%s:%s: %s:%s pos %d mismatched peer %s:%s\n",
109962306a36Sopenharmony_ci			dev_name(port->uport_dev), dev_name(&port->dev),
110062306a36Sopenharmony_ci			dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos,
110162306a36Sopenharmony_ci			dev_name(&cxlmd_peer->dev),
110262306a36Sopenharmony_ci			dev_name(&cxled_peer->cxld.dev));
110362306a36Sopenharmony_ci		return -ENXIO;
110462306a36Sopenharmony_ci	}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	return 0;
110762306a36Sopenharmony_ci}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_cistatic int cxl_port_setup_targets(struct cxl_port *port,
111062306a36Sopenharmony_ci				  struct cxl_region *cxlr,
111162306a36Sopenharmony_ci				  struct cxl_endpoint_decoder *cxled)
111262306a36Sopenharmony_ci{
111362306a36Sopenharmony_ci	struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
111462306a36Sopenharmony_ci	int parent_iw, parent_ig, ig, iw, rc, inc = 0, pos = cxled->pos;
111562306a36Sopenharmony_ci	struct cxl_port *parent_port = to_cxl_port(port->dev.parent);
111662306a36Sopenharmony_ci	struct cxl_region_ref *cxl_rr = cxl_rr_load(port, cxlr);
111762306a36Sopenharmony_ci	struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
111862306a36Sopenharmony_ci	struct cxl_ep *ep = cxl_ep_load(port, cxlmd);
111962306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
112062306a36Sopenharmony_ci	struct cxl_decoder *cxld = cxl_rr->decoder;
112162306a36Sopenharmony_ci	struct cxl_switch_decoder *cxlsd;
112262306a36Sopenharmony_ci	u16 eig, peig;
112362306a36Sopenharmony_ci	u8 eiw, peiw;
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	/*
112662306a36Sopenharmony_ci	 * While root level decoders support x3, x6, x12, switch level
112762306a36Sopenharmony_ci	 * decoders only support powers of 2 up to x16.
112862306a36Sopenharmony_ci	 */
112962306a36Sopenharmony_ci	if (!is_power_of_2(cxl_rr->nr_targets)) {
113062306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "%s:%s: invalid target count %d\n",
113162306a36Sopenharmony_ci			dev_name(port->uport_dev), dev_name(&port->dev),
113262306a36Sopenharmony_ci			cxl_rr->nr_targets);
113362306a36Sopenharmony_ci		return -EINVAL;
113462306a36Sopenharmony_ci	}
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	cxlsd = to_cxl_switch_decoder(&cxld->dev);
113762306a36Sopenharmony_ci	if (cxl_rr->nr_targets_set) {
113862306a36Sopenharmony_ci		int i, distance;
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci		/*
114162306a36Sopenharmony_ci		 * Passthrough decoders impose no distance requirements between
114262306a36Sopenharmony_ci		 * peers
114362306a36Sopenharmony_ci		 */
114462306a36Sopenharmony_ci		if (cxl_rr->nr_targets == 1)
114562306a36Sopenharmony_ci			distance = 0;
114662306a36Sopenharmony_ci		else
114762306a36Sopenharmony_ci			distance = p->nr_targets / cxl_rr->nr_targets;
114862306a36Sopenharmony_ci		for (i = 0; i < cxl_rr->nr_targets_set; i++)
114962306a36Sopenharmony_ci			if (ep->dport == cxlsd->target[i]) {
115062306a36Sopenharmony_ci				rc = check_last_peer(cxled, ep, cxl_rr,
115162306a36Sopenharmony_ci						     distance);
115262306a36Sopenharmony_ci				if (rc)
115362306a36Sopenharmony_ci					return rc;
115462306a36Sopenharmony_ci				goto out_target_set;
115562306a36Sopenharmony_ci			}
115662306a36Sopenharmony_ci		goto add_target;
115762306a36Sopenharmony_ci	}
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	if (is_cxl_root(parent_port)) {
116062306a36Sopenharmony_ci		/*
116162306a36Sopenharmony_ci		 * Root decoder IG is always set to value in CFMWS which
116262306a36Sopenharmony_ci		 * may be different than this region's IG.  We can use the
116362306a36Sopenharmony_ci		 * region's IG here since interleave_granularity_store()
116462306a36Sopenharmony_ci		 * does not allow interleaved host-bridges with
116562306a36Sopenharmony_ci		 * root IG != region IG.
116662306a36Sopenharmony_ci		 */
116762306a36Sopenharmony_ci		parent_ig = p->interleave_granularity;
116862306a36Sopenharmony_ci		parent_iw = cxlrd->cxlsd.cxld.interleave_ways;
116962306a36Sopenharmony_ci		/*
117062306a36Sopenharmony_ci		 * For purposes of address bit routing, use power-of-2 math for
117162306a36Sopenharmony_ci		 * switch ports.
117262306a36Sopenharmony_ci		 */
117362306a36Sopenharmony_ci		if (!is_power_of_2(parent_iw))
117462306a36Sopenharmony_ci			parent_iw /= 3;
117562306a36Sopenharmony_ci	} else {
117662306a36Sopenharmony_ci		struct cxl_region_ref *parent_rr;
117762306a36Sopenharmony_ci		struct cxl_decoder *parent_cxld;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci		parent_rr = cxl_rr_load(parent_port, cxlr);
118062306a36Sopenharmony_ci		parent_cxld = parent_rr->decoder;
118162306a36Sopenharmony_ci		parent_ig = parent_cxld->interleave_granularity;
118262306a36Sopenharmony_ci		parent_iw = parent_cxld->interleave_ways;
118362306a36Sopenharmony_ci	}
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	rc = granularity_to_eig(parent_ig, &peig);
118662306a36Sopenharmony_ci	if (rc) {
118762306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "%s:%s: invalid parent granularity: %d\n",
118862306a36Sopenharmony_ci			dev_name(parent_port->uport_dev),
118962306a36Sopenharmony_ci			dev_name(&parent_port->dev), parent_ig);
119062306a36Sopenharmony_ci		return rc;
119162306a36Sopenharmony_ci	}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	rc = ways_to_eiw(parent_iw, &peiw);
119462306a36Sopenharmony_ci	if (rc) {
119562306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "%s:%s: invalid parent interleave: %d\n",
119662306a36Sopenharmony_ci			dev_name(parent_port->uport_dev),
119762306a36Sopenharmony_ci			dev_name(&parent_port->dev), parent_iw);
119862306a36Sopenharmony_ci		return rc;
119962306a36Sopenharmony_ci	}
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	iw = cxl_rr->nr_targets;
120262306a36Sopenharmony_ci	rc = ways_to_eiw(iw, &eiw);
120362306a36Sopenharmony_ci	if (rc) {
120462306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "%s:%s: invalid port interleave: %d\n",
120562306a36Sopenharmony_ci			dev_name(port->uport_dev), dev_name(&port->dev), iw);
120662306a36Sopenharmony_ci		return rc;
120762306a36Sopenharmony_ci	}
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	/*
121062306a36Sopenharmony_ci	 * Interleave granularity is a multiple of @parent_port granularity.
121162306a36Sopenharmony_ci	 * Multiplier is the parent port interleave ways.
121262306a36Sopenharmony_ci	 */
121362306a36Sopenharmony_ci	rc = granularity_to_eig(parent_ig * parent_iw, &eig);
121462306a36Sopenharmony_ci	if (rc) {
121562306a36Sopenharmony_ci		dev_dbg(&cxlr->dev,
121662306a36Sopenharmony_ci			"%s: invalid granularity calculation (%d * %d)\n",
121762306a36Sopenharmony_ci			dev_name(&parent_port->dev), parent_ig, parent_iw);
121862306a36Sopenharmony_ci		return rc;
121962306a36Sopenharmony_ci	}
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	rc = eig_to_granularity(eig, &ig);
122262306a36Sopenharmony_ci	if (rc) {
122362306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "%s:%s: invalid interleave: %d\n",
122462306a36Sopenharmony_ci			dev_name(port->uport_dev), dev_name(&port->dev),
122562306a36Sopenharmony_ci			256 << eig);
122662306a36Sopenharmony_ci		return rc;
122762306a36Sopenharmony_ci	}
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	if (iw > 8 || iw > cxlsd->nr_targets) {
123062306a36Sopenharmony_ci		dev_dbg(&cxlr->dev,
123162306a36Sopenharmony_ci			"%s:%s:%s: ways: %d overflows targets: %d\n",
123262306a36Sopenharmony_ci			dev_name(port->uport_dev), dev_name(&port->dev),
123362306a36Sopenharmony_ci			dev_name(&cxld->dev), iw, cxlsd->nr_targets);
123462306a36Sopenharmony_ci		return -ENXIO;
123562306a36Sopenharmony_ci	}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	if (test_bit(CXL_REGION_F_AUTO, &cxlr->flags)) {
123862306a36Sopenharmony_ci		if (cxld->interleave_ways != iw ||
123962306a36Sopenharmony_ci		    cxld->interleave_granularity != ig ||
124062306a36Sopenharmony_ci		    cxld->hpa_range.start != p->res->start ||
124162306a36Sopenharmony_ci		    cxld->hpa_range.end != p->res->end ||
124262306a36Sopenharmony_ci		    ((cxld->flags & CXL_DECODER_F_ENABLE) == 0)) {
124362306a36Sopenharmony_ci			dev_err(&cxlr->dev,
124462306a36Sopenharmony_ci				"%s:%s %s expected iw: %d ig: %d %pr\n",
124562306a36Sopenharmony_ci				dev_name(port->uport_dev), dev_name(&port->dev),
124662306a36Sopenharmony_ci				__func__, iw, ig, p->res);
124762306a36Sopenharmony_ci			dev_err(&cxlr->dev,
124862306a36Sopenharmony_ci				"%s:%s %s got iw: %d ig: %d state: %s %#llx:%#llx\n",
124962306a36Sopenharmony_ci				dev_name(port->uport_dev), dev_name(&port->dev),
125062306a36Sopenharmony_ci				__func__, cxld->interleave_ways,
125162306a36Sopenharmony_ci				cxld->interleave_granularity,
125262306a36Sopenharmony_ci				(cxld->flags & CXL_DECODER_F_ENABLE) ?
125362306a36Sopenharmony_ci					"enabled" :
125462306a36Sopenharmony_ci					"disabled",
125562306a36Sopenharmony_ci				cxld->hpa_range.start, cxld->hpa_range.end);
125662306a36Sopenharmony_ci			return -ENXIO;
125762306a36Sopenharmony_ci		}
125862306a36Sopenharmony_ci	} else {
125962306a36Sopenharmony_ci		cxld->interleave_ways = iw;
126062306a36Sopenharmony_ci		cxld->interleave_granularity = ig;
126162306a36Sopenharmony_ci		cxld->hpa_range = (struct range) {
126262306a36Sopenharmony_ci			.start = p->res->start,
126362306a36Sopenharmony_ci			.end = p->res->end,
126462306a36Sopenharmony_ci		};
126562306a36Sopenharmony_ci	}
126662306a36Sopenharmony_ci	dev_dbg(&cxlr->dev, "%s:%s iw: %d ig: %d\n", dev_name(port->uport_dev),
126762306a36Sopenharmony_ci		dev_name(&port->dev), iw, ig);
126862306a36Sopenharmony_ciadd_target:
126962306a36Sopenharmony_ci	if (cxl_rr->nr_targets_set == cxl_rr->nr_targets) {
127062306a36Sopenharmony_ci		dev_dbg(&cxlr->dev,
127162306a36Sopenharmony_ci			"%s:%s: targets full trying to add %s:%s at %d\n",
127262306a36Sopenharmony_ci			dev_name(port->uport_dev), dev_name(&port->dev),
127362306a36Sopenharmony_ci			dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos);
127462306a36Sopenharmony_ci		return -ENXIO;
127562306a36Sopenharmony_ci	}
127662306a36Sopenharmony_ci	if (test_bit(CXL_REGION_F_AUTO, &cxlr->flags)) {
127762306a36Sopenharmony_ci		if (cxlsd->target[cxl_rr->nr_targets_set] != ep->dport) {
127862306a36Sopenharmony_ci			dev_dbg(&cxlr->dev, "%s:%s: %s expected %s at %d\n",
127962306a36Sopenharmony_ci				dev_name(port->uport_dev), dev_name(&port->dev),
128062306a36Sopenharmony_ci				dev_name(&cxlsd->cxld.dev),
128162306a36Sopenharmony_ci				dev_name(ep->dport->dport_dev),
128262306a36Sopenharmony_ci				cxl_rr->nr_targets_set);
128362306a36Sopenharmony_ci			return -ENXIO;
128462306a36Sopenharmony_ci		}
128562306a36Sopenharmony_ci	} else
128662306a36Sopenharmony_ci		cxlsd->target[cxl_rr->nr_targets_set] = ep->dport;
128762306a36Sopenharmony_ci	inc = 1;
128862306a36Sopenharmony_ciout_target_set:
128962306a36Sopenharmony_ci	cxl_rr->nr_targets_set += inc;
129062306a36Sopenharmony_ci	dev_dbg(&cxlr->dev, "%s:%s target[%d] = %s for %s:%s @ %d\n",
129162306a36Sopenharmony_ci		dev_name(port->uport_dev), dev_name(&port->dev),
129262306a36Sopenharmony_ci		cxl_rr->nr_targets_set - 1, dev_name(ep->dport->dport_dev),
129362306a36Sopenharmony_ci		dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos);
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	return 0;
129662306a36Sopenharmony_ci}
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_cistatic void cxl_port_reset_targets(struct cxl_port *port,
129962306a36Sopenharmony_ci				   struct cxl_region *cxlr)
130062306a36Sopenharmony_ci{
130162306a36Sopenharmony_ci	struct cxl_region_ref *cxl_rr = cxl_rr_load(port, cxlr);
130262306a36Sopenharmony_ci	struct cxl_decoder *cxld;
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	/*
130562306a36Sopenharmony_ci	 * After the last endpoint has been detached the entire cxl_rr may now
130662306a36Sopenharmony_ci	 * be gone.
130762306a36Sopenharmony_ci	 */
130862306a36Sopenharmony_ci	if (!cxl_rr)
130962306a36Sopenharmony_ci		return;
131062306a36Sopenharmony_ci	cxl_rr->nr_targets_set = 0;
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	cxld = cxl_rr->decoder;
131362306a36Sopenharmony_ci	cxld->hpa_range = (struct range) {
131462306a36Sopenharmony_ci		.start = 0,
131562306a36Sopenharmony_ci		.end = -1,
131662306a36Sopenharmony_ci	};
131762306a36Sopenharmony_ci}
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_cistatic void cxl_region_teardown_targets(struct cxl_region *cxlr)
132062306a36Sopenharmony_ci{
132162306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
132262306a36Sopenharmony_ci	struct cxl_endpoint_decoder *cxled;
132362306a36Sopenharmony_ci	struct cxl_dev_state *cxlds;
132462306a36Sopenharmony_ci	struct cxl_memdev *cxlmd;
132562306a36Sopenharmony_ci	struct cxl_port *iter;
132662306a36Sopenharmony_ci	struct cxl_ep *ep;
132762306a36Sopenharmony_ci	int i;
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	/*
133062306a36Sopenharmony_ci	 * In the auto-discovery case skip automatic teardown since the
133162306a36Sopenharmony_ci	 * address space is already active
133262306a36Sopenharmony_ci	 */
133362306a36Sopenharmony_ci	if (test_bit(CXL_REGION_F_AUTO, &cxlr->flags))
133462306a36Sopenharmony_ci		return;
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	for (i = 0; i < p->nr_targets; i++) {
133762306a36Sopenharmony_ci		cxled = p->targets[i];
133862306a36Sopenharmony_ci		cxlmd = cxled_to_memdev(cxled);
133962306a36Sopenharmony_ci		cxlds = cxlmd->cxlds;
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci		if (cxlds->rcd)
134262306a36Sopenharmony_ci			continue;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci		iter = cxled_to_port(cxled);
134562306a36Sopenharmony_ci		while (!is_cxl_root(to_cxl_port(iter->dev.parent)))
134662306a36Sopenharmony_ci			iter = to_cxl_port(iter->dev.parent);
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci		for (ep = cxl_ep_load(iter, cxlmd); iter;
134962306a36Sopenharmony_ci		     iter = ep->next, ep = cxl_ep_load(iter, cxlmd))
135062306a36Sopenharmony_ci			cxl_port_reset_targets(iter, cxlr);
135162306a36Sopenharmony_ci	}
135262306a36Sopenharmony_ci}
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_cistatic int cxl_region_setup_targets(struct cxl_region *cxlr)
135562306a36Sopenharmony_ci{
135662306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
135762306a36Sopenharmony_ci	struct cxl_endpoint_decoder *cxled;
135862306a36Sopenharmony_ci	struct cxl_dev_state *cxlds;
135962306a36Sopenharmony_ci	int i, rc, rch = 0, vh = 0;
136062306a36Sopenharmony_ci	struct cxl_memdev *cxlmd;
136162306a36Sopenharmony_ci	struct cxl_port *iter;
136262306a36Sopenharmony_ci	struct cxl_ep *ep;
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	for (i = 0; i < p->nr_targets; i++) {
136562306a36Sopenharmony_ci		cxled = p->targets[i];
136662306a36Sopenharmony_ci		cxlmd = cxled_to_memdev(cxled);
136762306a36Sopenharmony_ci		cxlds = cxlmd->cxlds;
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci		/* validate that all targets agree on topology */
137062306a36Sopenharmony_ci		if (!cxlds->rcd) {
137162306a36Sopenharmony_ci			vh++;
137262306a36Sopenharmony_ci		} else {
137362306a36Sopenharmony_ci			rch++;
137462306a36Sopenharmony_ci			continue;
137562306a36Sopenharmony_ci		}
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci		iter = cxled_to_port(cxled);
137862306a36Sopenharmony_ci		while (!is_cxl_root(to_cxl_port(iter->dev.parent)))
137962306a36Sopenharmony_ci			iter = to_cxl_port(iter->dev.parent);
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci		/*
138262306a36Sopenharmony_ci		 * Descend the topology tree programming / validating
138362306a36Sopenharmony_ci		 * targets while looking for conflicts.
138462306a36Sopenharmony_ci		 */
138562306a36Sopenharmony_ci		for (ep = cxl_ep_load(iter, cxlmd); iter;
138662306a36Sopenharmony_ci		     iter = ep->next, ep = cxl_ep_load(iter, cxlmd)) {
138762306a36Sopenharmony_ci			rc = cxl_port_setup_targets(iter, cxlr, cxled);
138862306a36Sopenharmony_ci			if (rc) {
138962306a36Sopenharmony_ci				cxl_region_teardown_targets(cxlr);
139062306a36Sopenharmony_ci				return rc;
139162306a36Sopenharmony_ci			}
139262306a36Sopenharmony_ci		}
139362306a36Sopenharmony_ci	}
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	if (rch && vh) {
139662306a36Sopenharmony_ci		dev_err(&cxlr->dev, "mismatched CXL topologies detected\n");
139762306a36Sopenharmony_ci		cxl_region_teardown_targets(cxlr);
139862306a36Sopenharmony_ci		return -ENXIO;
139962306a36Sopenharmony_ci	}
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	return 0;
140262306a36Sopenharmony_ci}
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_cistatic int cxl_region_validate_position(struct cxl_region *cxlr,
140562306a36Sopenharmony_ci					struct cxl_endpoint_decoder *cxled,
140662306a36Sopenharmony_ci					int pos)
140762306a36Sopenharmony_ci{
140862306a36Sopenharmony_ci	struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
140962306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
141062306a36Sopenharmony_ci	int i;
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	if (pos < 0 || pos >= p->interleave_ways) {
141362306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "position %d out of range %d\n", pos,
141462306a36Sopenharmony_ci			p->interleave_ways);
141562306a36Sopenharmony_ci		return -ENXIO;
141662306a36Sopenharmony_ci	}
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	if (p->targets[pos] == cxled)
141962306a36Sopenharmony_ci		return 0;
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	if (p->targets[pos]) {
142262306a36Sopenharmony_ci		struct cxl_endpoint_decoder *cxled_target = p->targets[pos];
142362306a36Sopenharmony_ci		struct cxl_memdev *cxlmd_target = cxled_to_memdev(cxled_target);
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "position %d already assigned to %s:%s\n",
142662306a36Sopenharmony_ci			pos, dev_name(&cxlmd_target->dev),
142762306a36Sopenharmony_ci			dev_name(&cxled_target->cxld.dev));
142862306a36Sopenharmony_ci		return -EBUSY;
142962306a36Sopenharmony_ci	}
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	for (i = 0; i < p->interleave_ways; i++) {
143262306a36Sopenharmony_ci		struct cxl_endpoint_decoder *cxled_target;
143362306a36Sopenharmony_ci		struct cxl_memdev *cxlmd_target;
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci		cxled_target = p->targets[i];
143662306a36Sopenharmony_ci		if (!cxled_target)
143762306a36Sopenharmony_ci			continue;
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci		cxlmd_target = cxled_to_memdev(cxled_target);
144062306a36Sopenharmony_ci		if (cxlmd_target == cxlmd) {
144162306a36Sopenharmony_ci			dev_dbg(&cxlr->dev,
144262306a36Sopenharmony_ci				"%s already specified at position %d via: %s\n",
144362306a36Sopenharmony_ci				dev_name(&cxlmd->dev), pos,
144462306a36Sopenharmony_ci				dev_name(&cxled_target->cxld.dev));
144562306a36Sopenharmony_ci			return -EBUSY;
144662306a36Sopenharmony_ci		}
144762306a36Sopenharmony_ci	}
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	return 0;
145062306a36Sopenharmony_ci}
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_cistatic int cxl_region_attach_position(struct cxl_region *cxlr,
145362306a36Sopenharmony_ci				      struct cxl_root_decoder *cxlrd,
145462306a36Sopenharmony_ci				      struct cxl_endpoint_decoder *cxled,
145562306a36Sopenharmony_ci				      const struct cxl_dport *dport, int pos)
145662306a36Sopenharmony_ci{
145762306a36Sopenharmony_ci	struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
145862306a36Sopenharmony_ci	struct cxl_port *iter;
145962306a36Sopenharmony_ci	int rc;
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	if (cxlrd->calc_hb(cxlrd, pos) != dport) {
146262306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "%s:%s invalid target position for %s\n",
146362306a36Sopenharmony_ci			dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
146462306a36Sopenharmony_ci			dev_name(&cxlrd->cxlsd.cxld.dev));
146562306a36Sopenharmony_ci		return -ENXIO;
146662306a36Sopenharmony_ci	}
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	for (iter = cxled_to_port(cxled); !is_cxl_root(iter);
146962306a36Sopenharmony_ci	     iter = to_cxl_port(iter->dev.parent)) {
147062306a36Sopenharmony_ci		rc = cxl_port_attach_region(iter, cxlr, cxled, pos);
147162306a36Sopenharmony_ci		if (rc)
147262306a36Sopenharmony_ci			goto err;
147362306a36Sopenharmony_ci	}
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	return 0;
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_cierr:
147862306a36Sopenharmony_ci	for (iter = cxled_to_port(cxled); !is_cxl_root(iter);
147962306a36Sopenharmony_ci	     iter = to_cxl_port(iter->dev.parent))
148062306a36Sopenharmony_ci		cxl_port_detach_region(iter, cxlr, cxled);
148162306a36Sopenharmony_ci	return rc;
148262306a36Sopenharmony_ci}
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_cistatic int cxl_region_attach_auto(struct cxl_region *cxlr,
148562306a36Sopenharmony_ci				  struct cxl_endpoint_decoder *cxled, int pos)
148662306a36Sopenharmony_ci{
148762306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	if (cxled->state != CXL_DECODER_STATE_AUTO) {
149062306a36Sopenharmony_ci		dev_err(&cxlr->dev,
149162306a36Sopenharmony_ci			"%s: unable to add decoder to autodetected region\n",
149262306a36Sopenharmony_ci			dev_name(&cxled->cxld.dev));
149362306a36Sopenharmony_ci		return -EINVAL;
149462306a36Sopenharmony_ci	}
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	if (pos >= 0) {
149762306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "%s: expected auto position, not %d\n",
149862306a36Sopenharmony_ci			dev_name(&cxled->cxld.dev), pos);
149962306a36Sopenharmony_ci		return -EINVAL;
150062306a36Sopenharmony_ci	}
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci	if (p->nr_targets >= p->interleave_ways) {
150362306a36Sopenharmony_ci		dev_err(&cxlr->dev, "%s: no more target slots available\n",
150462306a36Sopenharmony_ci			dev_name(&cxled->cxld.dev));
150562306a36Sopenharmony_ci		return -ENXIO;
150662306a36Sopenharmony_ci	}
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	/*
150962306a36Sopenharmony_ci	 * Temporarily record the endpoint decoder into the target array. Yes,
151062306a36Sopenharmony_ci	 * this means that userspace can view devices in the wrong position
151162306a36Sopenharmony_ci	 * before the region activates, and must be careful to understand when
151262306a36Sopenharmony_ci	 * it might be racing region autodiscovery.
151362306a36Sopenharmony_ci	 */
151462306a36Sopenharmony_ci	pos = p->nr_targets;
151562306a36Sopenharmony_ci	p->targets[pos] = cxled;
151662306a36Sopenharmony_ci	cxled->pos = pos;
151762306a36Sopenharmony_ci	p->nr_targets++;
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	return 0;
152062306a36Sopenharmony_ci}
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_cistatic int cmp_interleave_pos(const void *a, const void *b)
152362306a36Sopenharmony_ci{
152462306a36Sopenharmony_ci	struct cxl_endpoint_decoder *cxled_a = *(typeof(cxled_a) *)a;
152562306a36Sopenharmony_ci	struct cxl_endpoint_decoder *cxled_b = *(typeof(cxled_b) *)b;
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	return cxled_a->pos - cxled_b->pos;
152862306a36Sopenharmony_ci}
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_cistatic struct cxl_port *next_port(struct cxl_port *port)
153162306a36Sopenharmony_ci{
153262306a36Sopenharmony_ci	if (!port->parent_dport)
153362306a36Sopenharmony_ci		return NULL;
153462306a36Sopenharmony_ci	return port->parent_dport->port;
153562306a36Sopenharmony_ci}
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_cistatic int match_switch_decoder_by_range(struct device *dev, void *data)
153862306a36Sopenharmony_ci{
153962306a36Sopenharmony_ci	struct cxl_switch_decoder *cxlsd;
154062306a36Sopenharmony_ci	struct range *r1, *r2 = data;
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci	if (!is_switch_decoder(dev))
154362306a36Sopenharmony_ci		return 0;
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	cxlsd = to_cxl_switch_decoder(dev);
154662306a36Sopenharmony_ci	r1 = &cxlsd->cxld.hpa_range;
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	if (is_root_decoder(dev))
154962306a36Sopenharmony_ci		return range_contains(r1, r2);
155062306a36Sopenharmony_ci	return (r1->start == r2->start && r1->end == r2->end);
155162306a36Sopenharmony_ci}
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_cistatic int find_pos_and_ways(struct cxl_port *port, struct range *range,
155462306a36Sopenharmony_ci			     int *pos, int *ways)
155562306a36Sopenharmony_ci{
155662306a36Sopenharmony_ci	struct cxl_switch_decoder *cxlsd;
155762306a36Sopenharmony_ci	struct cxl_port *parent;
155862306a36Sopenharmony_ci	struct device *dev;
155962306a36Sopenharmony_ci	int rc = -ENXIO;
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci	parent = next_port(port);
156262306a36Sopenharmony_ci	if (!parent)
156362306a36Sopenharmony_ci		return rc;
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	dev = device_find_child(&parent->dev, range,
156662306a36Sopenharmony_ci				match_switch_decoder_by_range);
156762306a36Sopenharmony_ci	if (!dev) {
156862306a36Sopenharmony_ci		dev_err(port->uport_dev,
156962306a36Sopenharmony_ci			"failed to find decoder mapping %#llx-%#llx\n",
157062306a36Sopenharmony_ci			range->start, range->end);
157162306a36Sopenharmony_ci		return rc;
157262306a36Sopenharmony_ci	}
157362306a36Sopenharmony_ci	cxlsd = to_cxl_switch_decoder(dev);
157462306a36Sopenharmony_ci	*ways = cxlsd->cxld.interleave_ways;
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	for (int i = 0; i < *ways; i++) {
157762306a36Sopenharmony_ci		if (cxlsd->target[i] == port->parent_dport) {
157862306a36Sopenharmony_ci			*pos = i;
157962306a36Sopenharmony_ci			rc = 0;
158062306a36Sopenharmony_ci			break;
158162306a36Sopenharmony_ci		}
158262306a36Sopenharmony_ci	}
158362306a36Sopenharmony_ci	put_device(dev);
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	return rc;
158662306a36Sopenharmony_ci}
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci/**
158962306a36Sopenharmony_ci * cxl_calc_interleave_pos() - calculate an endpoint position in a region
159062306a36Sopenharmony_ci * @cxled: endpoint decoder member of given region
159162306a36Sopenharmony_ci *
159262306a36Sopenharmony_ci * The endpoint position is calculated by traversing the topology from
159362306a36Sopenharmony_ci * the endpoint to the root decoder and iteratively applying this
159462306a36Sopenharmony_ci * calculation:
159562306a36Sopenharmony_ci *
159662306a36Sopenharmony_ci *    position = position * parent_ways + parent_pos;
159762306a36Sopenharmony_ci *
159862306a36Sopenharmony_ci * ...where @position is inferred from switch and root decoder target lists.
159962306a36Sopenharmony_ci *
160062306a36Sopenharmony_ci * Return: position >= 0 on success
160162306a36Sopenharmony_ci *	   -ENXIO on failure
160262306a36Sopenharmony_ci */
160362306a36Sopenharmony_cistatic int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled)
160462306a36Sopenharmony_ci{
160562306a36Sopenharmony_ci	struct cxl_port *iter, *port = cxled_to_port(cxled);
160662306a36Sopenharmony_ci	struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
160762306a36Sopenharmony_ci	struct range *range = &cxled->cxld.hpa_range;
160862306a36Sopenharmony_ci	int parent_ways = 0, parent_pos = 0, pos = 0;
160962306a36Sopenharmony_ci	int rc;
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	/*
161262306a36Sopenharmony_ci	 * Example: the expected interleave order of the 4-way region shown
161362306a36Sopenharmony_ci	 * below is: mem0, mem2, mem1, mem3
161462306a36Sopenharmony_ci	 *
161562306a36Sopenharmony_ci	 *		  root_port
161662306a36Sopenharmony_ci	 *                 /      \
161762306a36Sopenharmony_ci	 *      host_bridge_0    host_bridge_1
161862306a36Sopenharmony_ci	 *        |    |           |    |
161962306a36Sopenharmony_ci	 *       mem0 mem1        mem2 mem3
162062306a36Sopenharmony_ci	 *
162162306a36Sopenharmony_ci	 * In the example the calculator will iterate twice. The first iteration
162262306a36Sopenharmony_ci	 * uses the mem position in the host-bridge and the ways of the host-
162362306a36Sopenharmony_ci	 * bridge to generate the first, or local, position. The second
162462306a36Sopenharmony_ci	 * iteration uses the host-bridge position in the root_port and the ways
162562306a36Sopenharmony_ci	 * of the root_port to refine the position.
162662306a36Sopenharmony_ci	 *
162762306a36Sopenharmony_ci	 * A trace of the calculation per endpoint looks like this:
162862306a36Sopenharmony_ci	 * mem0: pos = 0 * 2 + 0    mem2: pos = 0 * 2 + 0
162962306a36Sopenharmony_ci	 *       pos = 0 * 2 + 0          pos = 0 * 2 + 1
163062306a36Sopenharmony_ci	 *       pos: 0                   pos: 1
163162306a36Sopenharmony_ci	 *
163262306a36Sopenharmony_ci	 * mem1: pos = 0 * 2 + 1    mem3: pos = 0 * 2 + 1
163362306a36Sopenharmony_ci	 *       pos = 1 * 2 + 0          pos = 1 * 2 + 1
163462306a36Sopenharmony_ci	 *       pos: 2                   pos = 3
163562306a36Sopenharmony_ci	 *
163662306a36Sopenharmony_ci	 * Note that while this example is simple, the method applies to more
163762306a36Sopenharmony_ci	 * complex topologies, including those with switches.
163862306a36Sopenharmony_ci	 */
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci	/* Iterate from endpoint to root_port refining the position */
164162306a36Sopenharmony_ci	for (iter = port; iter; iter = next_port(iter)) {
164262306a36Sopenharmony_ci		if (is_cxl_root(iter))
164362306a36Sopenharmony_ci			break;
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci		rc = find_pos_and_ways(iter, range, &parent_pos, &parent_ways);
164662306a36Sopenharmony_ci		if (rc)
164762306a36Sopenharmony_ci			return rc;
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci		pos = pos * parent_ways + parent_pos;
165062306a36Sopenharmony_ci	}
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci	dev_dbg(&cxlmd->dev,
165362306a36Sopenharmony_ci		"decoder:%s parent:%s port:%s range:%#llx-%#llx pos:%d\n",
165462306a36Sopenharmony_ci		dev_name(&cxled->cxld.dev), dev_name(cxlmd->dev.parent),
165562306a36Sopenharmony_ci		dev_name(&port->dev), range->start, range->end, pos);
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci	return pos;
165862306a36Sopenharmony_ci}
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_cistatic int cxl_region_sort_targets(struct cxl_region *cxlr)
166162306a36Sopenharmony_ci{
166262306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
166362306a36Sopenharmony_ci	int i, rc = 0;
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci	for (i = 0; i < p->nr_targets; i++) {
166662306a36Sopenharmony_ci		struct cxl_endpoint_decoder *cxled = p->targets[i];
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci		cxled->pos = cxl_calc_interleave_pos(cxled);
166962306a36Sopenharmony_ci		/*
167062306a36Sopenharmony_ci		 * Record that sorting failed, but still continue to calc
167162306a36Sopenharmony_ci		 * cxled->pos so that follow-on code paths can reliably
167262306a36Sopenharmony_ci		 * do p->targets[cxled->pos] to self-reference their entry.
167362306a36Sopenharmony_ci		 */
167462306a36Sopenharmony_ci		if (cxled->pos < 0)
167562306a36Sopenharmony_ci			rc = -ENXIO;
167662306a36Sopenharmony_ci	}
167762306a36Sopenharmony_ci	/* Keep the cxlr target list in interleave position order */
167862306a36Sopenharmony_ci	sort(p->targets, p->nr_targets, sizeof(p->targets[0]),
167962306a36Sopenharmony_ci	     cmp_interleave_pos, NULL);
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci	dev_dbg(&cxlr->dev, "region sort %s\n", rc ? "failed" : "successful");
168262306a36Sopenharmony_ci	return rc;
168362306a36Sopenharmony_ci}
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_cistatic int cxl_region_attach(struct cxl_region *cxlr,
168662306a36Sopenharmony_ci			     struct cxl_endpoint_decoder *cxled, int pos)
168762306a36Sopenharmony_ci{
168862306a36Sopenharmony_ci	struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
168962306a36Sopenharmony_ci	struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
169062306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
169162306a36Sopenharmony_ci	struct cxl_port *ep_port, *root_port;
169262306a36Sopenharmony_ci	struct cxl_dport *dport;
169362306a36Sopenharmony_ci	int rc = -ENXIO;
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci	if (cxled->mode != cxlr->mode) {
169662306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "%s region mode: %d mismatch: %d\n",
169762306a36Sopenharmony_ci			dev_name(&cxled->cxld.dev), cxlr->mode, cxled->mode);
169862306a36Sopenharmony_ci		return -EINVAL;
169962306a36Sopenharmony_ci	}
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci	if (cxled->mode == CXL_DECODER_DEAD) {
170262306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "%s dead\n", dev_name(&cxled->cxld.dev));
170362306a36Sopenharmony_ci		return -ENODEV;
170462306a36Sopenharmony_ci	}
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	/* all full of members, or interleave config not established? */
170762306a36Sopenharmony_ci	if (p->state > CXL_CONFIG_INTERLEAVE_ACTIVE) {
170862306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "region already active\n");
170962306a36Sopenharmony_ci		return -EBUSY;
171062306a36Sopenharmony_ci	} else if (p->state < CXL_CONFIG_INTERLEAVE_ACTIVE) {
171162306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "interleave config missing\n");
171262306a36Sopenharmony_ci		return -ENXIO;
171362306a36Sopenharmony_ci	}
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci	if (p->nr_targets >= p->interleave_ways) {
171662306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "region already has %d endpoints\n",
171762306a36Sopenharmony_ci			p->nr_targets);
171862306a36Sopenharmony_ci		return -EINVAL;
171962306a36Sopenharmony_ci	}
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	ep_port = cxled_to_port(cxled);
172262306a36Sopenharmony_ci	root_port = cxlrd_to_port(cxlrd);
172362306a36Sopenharmony_ci	dport = cxl_find_dport_by_dev(root_port, ep_port->host_bridge);
172462306a36Sopenharmony_ci	if (!dport) {
172562306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "%s:%s invalid target for %s\n",
172662306a36Sopenharmony_ci			dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
172762306a36Sopenharmony_ci			dev_name(cxlr->dev.parent));
172862306a36Sopenharmony_ci		return -ENXIO;
172962306a36Sopenharmony_ci	}
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_ci	if (cxled->cxld.target_type != cxlr->type) {
173262306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "%s:%s type mismatch: %d vs %d\n",
173362306a36Sopenharmony_ci			dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
173462306a36Sopenharmony_ci			cxled->cxld.target_type, cxlr->type);
173562306a36Sopenharmony_ci		return -ENXIO;
173662306a36Sopenharmony_ci	}
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci	if (!cxled->dpa_res) {
173962306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "%s:%s: missing DPA allocation.\n",
174062306a36Sopenharmony_ci			dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev));
174162306a36Sopenharmony_ci		return -ENXIO;
174262306a36Sopenharmony_ci	}
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci	if (resource_size(cxled->dpa_res) * p->interleave_ways !=
174562306a36Sopenharmony_ci	    resource_size(p->res)) {
174662306a36Sopenharmony_ci		dev_dbg(&cxlr->dev,
174762306a36Sopenharmony_ci			"%s:%s: decoder-size-%#llx * ways-%d != region-size-%#llx\n",
174862306a36Sopenharmony_ci			dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
174962306a36Sopenharmony_ci			(u64)resource_size(cxled->dpa_res), p->interleave_ways,
175062306a36Sopenharmony_ci			(u64)resource_size(p->res));
175162306a36Sopenharmony_ci		return -EINVAL;
175262306a36Sopenharmony_ci	}
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci	if (test_bit(CXL_REGION_F_AUTO, &cxlr->flags)) {
175562306a36Sopenharmony_ci		int i;
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci		rc = cxl_region_attach_auto(cxlr, cxled, pos);
175862306a36Sopenharmony_ci		if (rc)
175962306a36Sopenharmony_ci			return rc;
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_ci		/* await more targets to arrive... */
176262306a36Sopenharmony_ci		if (p->nr_targets < p->interleave_ways)
176362306a36Sopenharmony_ci			return 0;
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci		/*
176662306a36Sopenharmony_ci		 * All targets are here, which implies all PCI enumeration that
176762306a36Sopenharmony_ci		 * affects this region has been completed. Walk the topology to
176862306a36Sopenharmony_ci		 * sort the devices into their relative region decode position.
176962306a36Sopenharmony_ci		 */
177062306a36Sopenharmony_ci		rc = cxl_region_sort_targets(cxlr);
177162306a36Sopenharmony_ci		if (rc)
177262306a36Sopenharmony_ci			return rc;
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci		for (i = 0; i < p->nr_targets; i++) {
177562306a36Sopenharmony_ci			cxled = p->targets[i];
177662306a36Sopenharmony_ci			ep_port = cxled_to_port(cxled);
177762306a36Sopenharmony_ci			dport = cxl_find_dport_by_dev(root_port,
177862306a36Sopenharmony_ci						      ep_port->host_bridge);
177962306a36Sopenharmony_ci			rc = cxl_region_attach_position(cxlr, cxlrd, cxled,
178062306a36Sopenharmony_ci							dport, i);
178162306a36Sopenharmony_ci			if (rc)
178262306a36Sopenharmony_ci				return rc;
178362306a36Sopenharmony_ci		}
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci		rc = cxl_region_setup_targets(cxlr);
178662306a36Sopenharmony_ci		if (rc)
178762306a36Sopenharmony_ci			return rc;
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci		/*
179062306a36Sopenharmony_ci		 * If target setup succeeds in the autodiscovery case
179162306a36Sopenharmony_ci		 * then the region is already committed.
179262306a36Sopenharmony_ci		 */
179362306a36Sopenharmony_ci		p->state = CXL_CONFIG_COMMIT;
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci		return 0;
179662306a36Sopenharmony_ci	}
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci	rc = cxl_region_validate_position(cxlr, cxled, pos);
179962306a36Sopenharmony_ci	if (rc)
180062306a36Sopenharmony_ci		return rc;
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci	rc = cxl_region_attach_position(cxlr, cxlrd, cxled, dport, pos);
180362306a36Sopenharmony_ci	if (rc)
180462306a36Sopenharmony_ci		return rc;
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_ci	p->targets[pos] = cxled;
180762306a36Sopenharmony_ci	cxled->pos = pos;
180862306a36Sopenharmony_ci	p->nr_targets++;
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_ci	if (p->nr_targets == p->interleave_ways) {
181162306a36Sopenharmony_ci		rc = cxl_region_setup_targets(cxlr);
181262306a36Sopenharmony_ci		if (rc)
181362306a36Sopenharmony_ci			return rc;
181462306a36Sopenharmony_ci		p->state = CXL_CONFIG_ACTIVE;
181562306a36Sopenharmony_ci	}
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci	cxled->cxld.interleave_ways = p->interleave_ways;
181862306a36Sopenharmony_ci	cxled->cxld.interleave_granularity = p->interleave_granularity;
181962306a36Sopenharmony_ci	cxled->cxld.hpa_range = (struct range) {
182062306a36Sopenharmony_ci		.start = p->res->start,
182162306a36Sopenharmony_ci		.end = p->res->end,
182262306a36Sopenharmony_ci	};
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	if (p->nr_targets != p->interleave_ways)
182562306a36Sopenharmony_ci		return 0;
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	/*
182862306a36Sopenharmony_ci	 * Test the auto-discovery position calculator function
182962306a36Sopenharmony_ci	 * against this successfully created user-defined region.
183062306a36Sopenharmony_ci	 * A fail message here means that this interleave config
183162306a36Sopenharmony_ci	 * will fail when presented as CXL_REGION_F_AUTO.
183262306a36Sopenharmony_ci	 */
183362306a36Sopenharmony_ci	for (int i = 0; i < p->nr_targets; i++) {
183462306a36Sopenharmony_ci		struct cxl_endpoint_decoder *cxled = p->targets[i];
183562306a36Sopenharmony_ci		int test_pos;
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci		test_pos = cxl_calc_interleave_pos(cxled);
183862306a36Sopenharmony_ci		dev_dbg(&cxled->cxld.dev,
183962306a36Sopenharmony_ci			"Test cxl_calc_interleave_pos(): %s test_pos:%d cxled->pos:%d\n",
184062306a36Sopenharmony_ci			(test_pos == cxled->pos) ? "success" : "fail",
184162306a36Sopenharmony_ci			test_pos, cxled->pos);
184262306a36Sopenharmony_ci	}
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_ci	return 0;
184562306a36Sopenharmony_ci}
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_cistatic int cxl_region_detach(struct cxl_endpoint_decoder *cxled)
184862306a36Sopenharmony_ci{
184962306a36Sopenharmony_ci	struct cxl_port *iter, *ep_port = cxled_to_port(cxled);
185062306a36Sopenharmony_ci	struct cxl_region *cxlr = cxled->cxld.region;
185162306a36Sopenharmony_ci	struct cxl_region_params *p;
185262306a36Sopenharmony_ci	int rc = 0;
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_ci	lockdep_assert_held_write(&cxl_region_rwsem);
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci	if (!cxlr)
185762306a36Sopenharmony_ci		return 0;
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_ci	p = &cxlr->params;
186062306a36Sopenharmony_ci	get_device(&cxlr->dev);
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci	if (p->state > CXL_CONFIG_ACTIVE) {
186362306a36Sopenharmony_ci		/*
186462306a36Sopenharmony_ci		 * TODO: tear down all impacted regions if a device is
186562306a36Sopenharmony_ci		 * removed out of order
186662306a36Sopenharmony_ci		 */
186762306a36Sopenharmony_ci		rc = cxl_region_decode_reset(cxlr, p->interleave_ways);
186862306a36Sopenharmony_ci		if (rc)
186962306a36Sopenharmony_ci			goto out;
187062306a36Sopenharmony_ci		p->state = CXL_CONFIG_ACTIVE;
187162306a36Sopenharmony_ci	}
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci	for (iter = ep_port; !is_cxl_root(iter);
187462306a36Sopenharmony_ci	     iter = to_cxl_port(iter->dev.parent))
187562306a36Sopenharmony_ci		cxl_port_detach_region(iter, cxlr, cxled);
187662306a36Sopenharmony_ci
187762306a36Sopenharmony_ci	if (cxled->pos < 0 || cxled->pos >= p->interleave_ways ||
187862306a36Sopenharmony_ci	    p->targets[cxled->pos] != cxled) {
187962306a36Sopenharmony_ci		struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci		dev_WARN_ONCE(&cxlr->dev, 1, "expected %s:%s at position %d\n",
188262306a36Sopenharmony_ci			      dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
188362306a36Sopenharmony_ci			      cxled->pos);
188462306a36Sopenharmony_ci		goto out;
188562306a36Sopenharmony_ci	}
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	if (p->state == CXL_CONFIG_ACTIVE) {
188862306a36Sopenharmony_ci		p->state = CXL_CONFIG_INTERLEAVE_ACTIVE;
188962306a36Sopenharmony_ci		cxl_region_teardown_targets(cxlr);
189062306a36Sopenharmony_ci	}
189162306a36Sopenharmony_ci	p->targets[cxled->pos] = NULL;
189262306a36Sopenharmony_ci	p->nr_targets--;
189362306a36Sopenharmony_ci	cxled->cxld.hpa_range = (struct range) {
189462306a36Sopenharmony_ci		.start = 0,
189562306a36Sopenharmony_ci		.end = -1,
189662306a36Sopenharmony_ci	};
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci	/* notify the region driver that one of its targets has departed */
189962306a36Sopenharmony_ci	up_write(&cxl_region_rwsem);
190062306a36Sopenharmony_ci	device_release_driver(&cxlr->dev);
190162306a36Sopenharmony_ci	down_write(&cxl_region_rwsem);
190262306a36Sopenharmony_ciout:
190362306a36Sopenharmony_ci	put_device(&cxlr->dev);
190462306a36Sopenharmony_ci	return rc;
190562306a36Sopenharmony_ci}
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_civoid cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled)
190862306a36Sopenharmony_ci{
190962306a36Sopenharmony_ci	down_write(&cxl_region_rwsem);
191062306a36Sopenharmony_ci	cxled->mode = CXL_DECODER_DEAD;
191162306a36Sopenharmony_ci	cxl_region_detach(cxled);
191262306a36Sopenharmony_ci	up_write(&cxl_region_rwsem);
191362306a36Sopenharmony_ci}
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_cistatic int attach_target(struct cxl_region *cxlr,
191662306a36Sopenharmony_ci			 struct cxl_endpoint_decoder *cxled, int pos,
191762306a36Sopenharmony_ci			 unsigned int state)
191862306a36Sopenharmony_ci{
191962306a36Sopenharmony_ci	int rc = 0;
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_ci	if (state == TASK_INTERRUPTIBLE)
192262306a36Sopenharmony_ci		rc = down_write_killable(&cxl_region_rwsem);
192362306a36Sopenharmony_ci	else
192462306a36Sopenharmony_ci		down_write(&cxl_region_rwsem);
192562306a36Sopenharmony_ci	if (rc)
192662306a36Sopenharmony_ci		return rc;
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_ci	down_read(&cxl_dpa_rwsem);
192962306a36Sopenharmony_ci	rc = cxl_region_attach(cxlr, cxled, pos);
193062306a36Sopenharmony_ci	up_read(&cxl_dpa_rwsem);
193162306a36Sopenharmony_ci	up_write(&cxl_region_rwsem);
193262306a36Sopenharmony_ci	return rc;
193362306a36Sopenharmony_ci}
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_cistatic int detach_target(struct cxl_region *cxlr, int pos)
193662306a36Sopenharmony_ci{
193762306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
193862306a36Sopenharmony_ci	int rc;
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ci	rc = down_write_killable(&cxl_region_rwsem);
194162306a36Sopenharmony_ci	if (rc)
194262306a36Sopenharmony_ci		return rc;
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_ci	if (pos >= p->interleave_ways) {
194562306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "position %d out of range %d\n", pos,
194662306a36Sopenharmony_ci			p->interleave_ways);
194762306a36Sopenharmony_ci		rc = -ENXIO;
194862306a36Sopenharmony_ci		goto out;
194962306a36Sopenharmony_ci	}
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_ci	if (!p->targets[pos]) {
195262306a36Sopenharmony_ci		rc = 0;
195362306a36Sopenharmony_ci		goto out;
195462306a36Sopenharmony_ci	}
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_ci	rc = cxl_region_detach(p->targets[pos]);
195762306a36Sopenharmony_ciout:
195862306a36Sopenharmony_ci	up_write(&cxl_region_rwsem);
195962306a36Sopenharmony_ci	return rc;
196062306a36Sopenharmony_ci}
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_cistatic size_t store_targetN(struct cxl_region *cxlr, const char *buf, int pos,
196362306a36Sopenharmony_ci			    size_t len)
196462306a36Sopenharmony_ci{
196562306a36Sopenharmony_ci	int rc;
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci	if (sysfs_streq(buf, "\n"))
196862306a36Sopenharmony_ci		rc = detach_target(cxlr, pos);
196962306a36Sopenharmony_ci	else {
197062306a36Sopenharmony_ci		struct device *dev;
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci		dev = bus_find_device_by_name(&cxl_bus_type, NULL, buf);
197362306a36Sopenharmony_ci		if (!dev)
197462306a36Sopenharmony_ci			return -ENODEV;
197562306a36Sopenharmony_ci
197662306a36Sopenharmony_ci		if (!is_endpoint_decoder(dev)) {
197762306a36Sopenharmony_ci			rc = -EINVAL;
197862306a36Sopenharmony_ci			goto out;
197962306a36Sopenharmony_ci		}
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci		rc = attach_target(cxlr, to_cxl_endpoint_decoder(dev), pos,
198262306a36Sopenharmony_ci				   TASK_INTERRUPTIBLE);
198362306a36Sopenharmony_ciout:
198462306a36Sopenharmony_ci		put_device(dev);
198562306a36Sopenharmony_ci	}
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci	if (rc < 0)
198862306a36Sopenharmony_ci		return rc;
198962306a36Sopenharmony_ci	return len;
199062306a36Sopenharmony_ci}
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci#define TARGET_ATTR_RW(n)                                              \
199362306a36Sopenharmony_cistatic ssize_t target##n##_show(                                       \
199462306a36Sopenharmony_ci	struct device *dev, struct device_attribute *attr, char *buf)  \
199562306a36Sopenharmony_ci{                                                                      \
199662306a36Sopenharmony_ci	return show_targetN(to_cxl_region(dev), buf, (n));             \
199762306a36Sopenharmony_ci}                                                                      \
199862306a36Sopenharmony_cistatic ssize_t target##n##_store(struct device *dev,                   \
199962306a36Sopenharmony_ci				 struct device_attribute *attr,        \
200062306a36Sopenharmony_ci				 const char *buf, size_t len)          \
200162306a36Sopenharmony_ci{                                                                      \
200262306a36Sopenharmony_ci	return store_targetN(to_cxl_region(dev), buf, (n), len);       \
200362306a36Sopenharmony_ci}                                                                      \
200462306a36Sopenharmony_cistatic DEVICE_ATTR_RW(target##n)
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_ciTARGET_ATTR_RW(0);
200762306a36Sopenharmony_ciTARGET_ATTR_RW(1);
200862306a36Sopenharmony_ciTARGET_ATTR_RW(2);
200962306a36Sopenharmony_ciTARGET_ATTR_RW(3);
201062306a36Sopenharmony_ciTARGET_ATTR_RW(4);
201162306a36Sopenharmony_ciTARGET_ATTR_RW(5);
201262306a36Sopenharmony_ciTARGET_ATTR_RW(6);
201362306a36Sopenharmony_ciTARGET_ATTR_RW(7);
201462306a36Sopenharmony_ciTARGET_ATTR_RW(8);
201562306a36Sopenharmony_ciTARGET_ATTR_RW(9);
201662306a36Sopenharmony_ciTARGET_ATTR_RW(10);
201762306a36Sopenharmony_ciTARGET_ATTR_RW(11);
201862306a36Sopenharmony_ciTARGET_ATTR_RW(12);
201962306a36Sopenharmony_ciTARGET_ATTR_RW(13);
202062306a36Sopenharmony_ciTARGET_ATTR_RW(14);
202162306a36Sopenharmony_ciTARGET_ATTR_RW(15);
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_cistatic struct attribute *target_attrs[] = {
202462306a36Sopenharmony_ci	&dev_attr_target0.attr,
202562306a36Sopenharmony_ci	&dev_attr_target1.attr,
202662306a36Sopenharmony_ci	&dev_attr_target2.attr,
202762306a36Sopenharmony_ci	&dev_attr_target3.attr,
202862306a36Sopenharmony_ci	&dev_attr_target4.attr,
202962306a36Sopenharmony_ci	&dev_attr_target5.attr,
203062306a36Sopenharmony_ci	&dev_attr_target6.attr,
203162306a36Sopenharmony_ci	&dev_attr_target7.attr,
203262306a36Sopenharmony_ci	&dev_attr_target8.attr,
203362306a36Sopenharmony_ci	&dev_attr_target9.attr,
203462306a36Sopenharmony_ci	&dev_attr_target10.attr,
203562306a36Sopenharmony_ci	&dev_attr_target11.attr,
203662306a36Sopenharmony_ci	&dev_attr_target12.attr,
203762306a36Sopenharmony_ci	&dev_attr_target13.attr,
203862306a36Sopenharmony_ci	&dev_attr_target14.attr,
203962306a36Sopenharmony_ci	&dev_attr_target15.attr,
204062306a36Sopenharmony_ci	NULL,
204162306a36Sopenharmony_ci};
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_cistatic umode_t cxl_region_target_visible(struct kobject *kobj,
204462306a36Sopenharmony_ci					 struct attribute *a, int n)
204562306a36Sopenharmony_ci{
204662306a36Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj);
204762306a36Sopenharmony_ci	struct cxl_region *cxlr = to_cxl_region(dev);
204862306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_ci	if (n < p->interleave_ways)
205162306a36Sopenharmony_ci		return a->mode;
205262306a36Sopenharmony_ci	return 0;
205362306a36Sopenharmony_ci}
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_cistatic const struct attribute_group cxl_region_target_group = {
205662306a36Sopenharmony_ci	.attrs = target_attrs,
205762306a36Sopenharmony_ci	.is_visible = cxl_region_target_visible,
205862306a36Sopenharmony_ci};
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_cistatic const struct attribute_group *get_cxl_region_target_group(void)
206162306a36Sopenharmony_ci{
206262306a36Sopenharmony_ci	return &cxl_region_target_group;
206362306a36Sopenharmony_ci}
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_cistatic const struct attribute_group *region_groups[] = {
206662306a36Sopenharmony_ci	&cxl_base_attribute_group,
206762306a36Sopenharmony_ci	&cxl_region_group,
206862306a36Sopenharmony_ci	&cxl_region_target_group,
206962306a36Sopenharmony_ci	NULL,
207062306a36Sopenharmony_ci};
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_cistatic void cxl_region_release(struct device *dev)
207362306a36Sopenharmony_ci{
207462306a36Sopenharmony_ci	struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev->parent);
207562306a36Sopenharmony_ci	struct cxl_region *cxlr = to_cxl_region(dev);
207662306a36Sopenharmony_ci	int id = atomic_read(&cxlrd->region_id);
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci	/*
207962306a36Sopenharmony_ci	 * Try to reuse the recently idled id rather than the cached
208062306a36Sopenharmony_ci	 * next id to prevent the region id space from increasing
208162306a36Sopenharmony_ci	 * unnecessarily.
208262306a36Sopenharmony_ci	 */
208362306a36Sopenharmony_ci	if (cxlr->id < id)
208462306a36Sopenharmony_ci		if (atomic_try_cmpxchg(&cxlrd->region_id, &id, cxlr->id)) {
208562306a36Sopenharmony_ci			memregion_free(id);
208662306a36Sopenharmony_ci			goto out;
208762306a36Sopenharmony_ci		}
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_ci	memregion_free(cxlr->id);
209062306a36Sopenharmony_ciout:
209162306a36Sopenharmony_ci	put_device(dev->parent);
209262306a36Sopenharmony_ci	kfree(cxlr);
209362306a36Sopenharmony_ci}
209462306a36Sopenharmony_ci
209562306a36Sopenharmony_ciconst struct device_type cxl_region_type = {
209662306a36Sopenharmony_ci	.name = "cxl_region",
209762306a36Sopenharmony_ci	.release = cxl_region_release,
209862306a36Sopenharmony_ci	.groups = region_groups
209962306a36Sopenharmony_ci};
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_cibool is_cxl_region(struct device *dev)
210262306a36Sopenharmony_ci{
210362306a36Sopenharmony_ci	return dev->type == &cxl_region_type;
210462306a36Sopenharmony_ci}
210562306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(is_cxl_region, CXL);
210662306a36Sopenharmony_ci
210762306a36Sopenharmony_cistatic struct cxl_region *to_cxl_region(struct device *dev)
210862306a36Sopenharmony_ci{
210962306a36Sopenharmony_ci	if (dev_WARN_ONCE(dev, dev->type != &cxl_region_type,
211062306a36Sopenharmony_ci			  "not a cxl_region device\n"))
211162306a36Sopenharmony_ci		return NULL;
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci	return container_of(dev, struct cxl_region, dev);
211462306a36Sopenharmony_ci}
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_cistatic void unregister_region(void *dev)
211762306a36Sopenharmony_ci{
211862306a36Sopenharmony_ci	struct cxl_region *cxlr = to_cxl_region(dev);
211962306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
212062306a36Sopenharmony_ci	int i;
212162306a36Sopenharmony_ci
212262306a36Sopenharmony_ci	device_del(dev);
212362306a36Sopenharmony_ci
212462306a36Sopenharmony_ci	/*
212562306a36Sopenharmony_ci	 * Now that region sysfs is shutdown, the parameter block is now
212662306a36Sopenharmony_ci	 * read-only, so no need to hold the region rwsem to access the
212762306a36Sopenharmony_ci	 * region parameters.
212862306a36Sopenharmony_ci	 */
212962306a36Sopenharmony_ci	for (i = 0; i < p->interleave_ways; i++)
213062306a36Sopenharmony_ci		detach_target(cxlr, i);
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_ci	cxl_region_iomem_release(cxlr);
213362306a36Sopenharmony_ci	put_device(dev);
213462306a36Sopenharmony_ci}
213562306a36Sopenharmony_ci
213662306a36Sopenharmony_cistatic struct lock_class_key cxl_region_key;
213762306a36Sopenharmony_ci
213862306a36Sopenharmony_cistatic struct cxl_region *cxl_region_alloc(struct cxl_root_decoder *cxlrd, int id)
213962306a36Sopenharmony_ci{
214062306a36Sopenharmony_ci	struct cxl_region *cxlr;
214162306a36Sopenharmony_ci	struct device *dev;
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	cxlr = kzalloc(sizeof(*cxlr), GFP_KERNEL);
214462306a36Sopenharmony_ci	if (!cxlr) {
214562306a36Sopenharmony_ci		memregion_free(id);
214662306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
214762306a36Sopenharmony_ci	}
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci	dev = &cxlr->dev;
215062306a36Sopenharmony_ci	device_initialize(dev);
215162306a36Sopenharmony_ci	lockdep_set_class(&dev->mutex, &cxl_region_key);
215262306a36Sopenharmony_ci	dev->parent = &cxlrd->cxlsd.cxld.dev;
215362306a36Sopenharmony_ci	/*
215462306a36Sopenharmony_ci	 * Keep root decoder pinned through cxl_region_release to fixup
215562306a36Sopenharmony_ci	 * region id allocations
215662306a36Sopenharmony_ci	 */
215762306a36Sopenharmony_ci	get_device(dev->parent);
215862306a36Sopenharmony_ci	device_set_pm_not_required(dev);
215962306a36Sopenharmony_ci	dev->bus = &cxl_bus_type;
216062306a36Sopenharmony_ci	dev->type = &cxl_region_type;
216162306a36Sopenharmony_ci	cxlr->id = id;
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_ci	return cxlr;
216462306a36Sopenharmony_ci}
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_ci/**
216762306a36Sopenharmony_ci * devm_cxl_add_region - Adds a region to a decoder
216862306a36Sopenharmony_ci * @cxlrd: root decoder
216962306a36Sopenharmony_ci * @id: memregion id to create, or memregion_free() on failure
217062306a36Sopenharmony_ci * @mode: mode for the endpoint decoders of this region
217162306a36Sopenharmony_ci * @type: select whether this is an expander or accelerator (type-2 or type-3)
217262306a36Sopenharmony_ci *
217362306a36Sopenharmony_ci * This is the second step of region initialization. Regions exist within an
217462306a36Sopenharmony_ci * address space which is mapped by a @cxlrd.
217562306a36Sopenharmony_ci *
217662306a36Sopenharmony_ci * Return: 0 if the region was added to the @cxlrd, else returns negative error
217762306a36Sopenharmony_ci * code. The region will be named "regionZ" where Z is the unique region number.
217862306a36Sopenharmony_ci */
217962306a36Sopenharmony_cistatic struct cxl_region *devm_cxl_add_region(struct cxl_root_decoder *cxlrd,
218062306a36Sopenharmony_ci					      int id,
218162306a36Sopenharmony_ci					      enum cxl_decoder_mode mode,
218262306a36Sopenharmony_ci					      enum cxl_decoder_type type)
218362306a36Sopenharmony_ci{
218462306a36Sopenharmony_ci	struct cxl_port *port = to_cxl_port(cxlrd->cxlsd.cxld.dev.parent);
218562306a36Sopenharmony_ci	struct cxl_region *cxlr;
218662306a36Sopenharmony_ci	struct device *dev;
218762306a36Sopenharmony_ci	int rc;
218862306a36Sopenharmony_ci
218962306a36Sopenharmony_ci	switch (mode) {
219062306a36Sopenharmony_ci	case CXL_DECODER_RAM:
219162306a36Sopenharmony_ci	case CXL_DECODER_PMEM:
219262306a36Sopenharmony_ci		break;
219362306a36Sopenharmony_ci	default:
219462306a36Sopenharmony_ci		dev_err(&cxlrd->cxlsd.cxld.dev, "unsupported mode %d\n", mode);
219562306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
219662306a36Sopenharmony_ci	}
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci	cxlr = cxl_region_alloc(cxlrd, id);
219962306a36Sopenharmony_ci	if (IS_ERR(cxlr))
220062306a36Sopenharmony_ci		return cxlr;
220162306a36Sopenharmony_ci	cxlr->mode = mode;
220262306a36Sopenharmony_ci	cxlr->type = type;
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_ci	dev = &cxlr->dev;
220562306a36Sopenharmony_ci	rc = dev_set_name(dev, "region%d", id);
220662306a36Sopenharmony_ci	if (rc)
220762306a36Sopenharmony_ci		goto err;
220862306a36Sopenharmony_ci
220962306a36Sopenharmony_ci	rc = device_add(dev);
221062306a36Sopenharmony_ci	if (rc)
221162306a36Sopenharmony_ci		goto err;
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci	rc = devm_add_action_or_reset(port->uport_dev, unregister_region, cxlr);
221462306a36Sopenharmony_ci	if (rc)
221562306a36Sopenharmony_ci		return ERR_PTR(rc);
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_ci	dev_dbg(port->uport_dev, "%s: created %s\n",
221862306a36Sopenharmony_ci		dev_name(&cxlrd->cxlsd.cxld.dev), dev_name(dev));
221962306a36Sopenharmony_ci	return cxlr;
222062306a36Sopenharmony_ci
222162306a36Sopenharmony_cierr:
222262306a36Sopenharmony_ci	put_device(dev);
222362306a36Sopenharmony_ci	return ERR_PTR(rc);
222462306a36Sopenharmony_ci}
222562306a36Sopenharmony_ci
222662306a36Sopenharmony_cistatic ssize_t __create_region_show(struct cxl_root_decoder *cxlrd, char *buf)
222762306a36Sopenharmony_ci{
222862306a36Sopenharmony_ci	return sysfs_emit(buf, "region%u\n", atomic_read(&cxlrd->region_id));
222962306a36Sopenharmony_ci}
223062306a36Sopenharmony_ci
223162306a36Sopenharmony_cistatic ssize_t create_pmem_region_show(struct device *dev,
223262306a36Sopenharmony_ci				       struct device_attribute *attr, char *buf)
223362306a36Sopenharmony_ci{
223462306a36Sopenharmony_ci	return __create_region_show(to_cxl_root_decoder(dev), buf);
223562306a36Sopenharmony_ci}
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_cistatic ssize_t create_ram_region_show(struct device *dev,
223862306a36Sopenharmony_ci				      struct device_attribute *attr, char *buf)
223962306a36Sopenharmony_ci{
224062306a36Sopenharmony_ci	return __create_region_show(to_cxl_root_decoder(dev), buf);
224162306a36Sopenharmony_ci}
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_cistatic struct cxl_region *__create_region(struct cxl_root_decoder *cxlrd,
224462306a36Sopenharmony_ci					  enum cxl_decoder_mode mode, int id)
224562306a36Sopenharmony_ci{
224662306a36Sopenharmony_ci	int rc;
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_ci	rc = memregion_alloc(GFP_KERNEL);
224962306a36Sopenharmony_ci	if (rc < 0)
225062306a36Sopenharmony_ci		return ERR_PTR(rc);
225162306a36Sopenharmony_ci
225262306a36Sopenharmony_ci	if (atomic_cmpxchg(&cxlrd->region_id, id, rc) != id) {
225362306a36Sopenharmony_ci		memregion_free(rc);
225462306a36Sopenharmony_ci		return ERR_PTR(-EBUSY);
225562306a36Sopenharmony_ci	}
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_ci	return devm_cxl_add_region(cxlrd, id, mode, CXL_DECODER_HOSTONLYMEM);
225862306a36Sopenharmony_ci}
225962306a36Sopenharmony_ci
226062306a36Sopenharmony_cistatic ssize_t create_pmem_region_store(struct device *dev,
226162306a36Sopenharmony_ci					struct device_attribute *attr,
226262306a36Sopenharmony_ci					const char *buf, size_t len)
226362306a36Sopenharmony_ci{
226462306a36Sopenharmony_ci	struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev);
226562306a36Sopenharmony_ci	struct cxl_region *cxlr;
226662306a36Sopenharmony_ci	int rc, id;
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci	rc = sscanf(buf, "region%d\n", &id);
226962306a36Sopenharmony_ci	if (rc != 1)
227062306a36Sopenharmony_ci		return -EINVAL;
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci	cxlr = __create_region(cxlrd, CXL_DECODER_PMEM, id);
227362306a36Sopenharmony_ci	if (IS_ERR(cxlr))
227462306a36Sopenharmony_ci		return PTR_ERR(cxlr);
227562306a36Sopenharmony_ci
227662306a36Sopenharmony_ci	return len;
227762306a36Sopenharmony_ci}
227862306a36Sopenharmony_ciDEVICE_ATTR_RW(create_pmem_region);
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_cistatic ssize_t create_ram_region_store(struct device *dev,
228162306a36Sopenharmony_ci				       struct device_attribute *attr,
228262306a36Sopenharmony_ci				       const char *buf, size_t len)
228362306a36Sopenharmony_ci{
228462306a36Sopenharmony_ci	struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev);
228562306a36Sopenharmony_ci	struct cxl_region *cxlr;
228662306a36Sopenharmony_ci	int rc, id;
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_ci	rc = sscanf(buf, "region%d\n", &id);
228962306a36Sopenharmony_ci	if (rc != 1)
229062306a36Sopenharmony_ci		return -EINVAL;
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci	cxlr = __create_region(cxlrd, CXL_DECODER_RAM, id);
229362306a36Sopenharmony_ci	if (IS_ERR(cxlr))
229462306a36Sopenharmony_ci		return PTR_ERR(cxlr);
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_ci	return len;
229762306a36Sopenharmony_ci}
229862306a36Sopenharmony_ciDEVICE_ATTR_RW(create_ram_region);
229962306a36Sopenharmony_ci
230062306a36Sopenharmony_cistatic ssize_t region_show(struct device *dev, struct device_attribute *attr,
230162306a36Sopenharmony_ci			   char *buf)
230262306a36Sopenharmony_ci{
230362306a36Sopenharmony_ci	struct cxl_decoder *cxld = to_cxl_decoder(dev);
230462306a36Sopenharmony_ci	ssize_t rc;
230562306a36Sopenharmony_ci
230662306a36Sopenharmony_ci	rc = down_read_interruptible(&cxl_region_rwsem);
230762306a36Sopenharmony_ci	if (rc)
230862306a36Sopenharmony_ci		return rc;
230962306a36Sopenharmony_ci
231062306a36Sopenharmony_ci	if (cxld->region)
231162306a36Sopenharmony_ci		rc = sysfs_emit(buf, "%s\n", dev_name(&cxld->region->dev));
231262306a36Sopenharmony_ci	else
231362306a36Sopenharmony_ci		rc = sysfs_emit(buf, "\n");
231462306a36Sopenharmony_ci	up_read(&cxl_region_rwsem);
231562306a36Sopenharmony_ci
231662306a36Sopenharmony_ci	return rc;
231762306a36Sopenharmony_ci}
231862306a36Sopenharmony_ciDEVICE_ATTR_RO(region);
231962306a36Sopenharmony_ci
232062306a36Sopenharmony_cistatic struct cxl_region *
232162306a36Sopenharmony_cicxl_find_region_by_name(struct cxl_root_decoder *cxlrd, const char *name)
232262306a36Sopenharmony_ci{
232362306a36Sopenharmony_ci	struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld;
232462306a36Sopenharmony_ci	struct device *region_dev;
232562306a36Sopenharmony_ci
232662306a36Sopenharmony_ci	region_dev = device_find_child_by_name(&cxld->dev, name);
232762306a36Sopenharmony_ci	if (!region_dev)
232862306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
232962306a36Sopenharmony_ci
233062306a36Sopenharmony_ci	return to_cxl_region(region_dev);
233162306a36Sopenharmony_ci}
233262306a36Sopenharmony_ci
233362306a36Sopenharmony_cistatic ssize_t delete_region_store(struct device *dev,
233462306a36Sopenharmony_ci				   struct device_attribute *attr,
233562306a36Sopenharmony_ci				   const char *buf, size_t len)
233662306a36Sopenharmony_ci{
233762306a36Sopenharmony_ci	struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev);
233862306a36Sopenharmony_ci	struct cxl_port *port = to_cxl_port(dev->parent);
233962306a36Sopenharmony_ci	struct cxl_region *cxlr;
234062306a36Sopenharmony_ci
234162306a36Sopenharmony_ci	cxlr = cxl_find_region_by_name(cxlrd, buf);
234262306a36Sopenharmony_ci	if (IS_ERR(cxlr))
234362306a36Sopenharmony_ci		return PTR_ERR(cxlr);
234462306a36Sopenharmony_ci
234562306a36Sopenharmony_ci	devm_release_action(port->uport_dev, unregister_region, cxlr);
234662306a36Sopenharmony_ci	put_device(&cxlr->dev);
234762306a36Sopenharmony_ci
234862306a36Sopenharmony_ci	return len;
234962306a36Sopenharmony_ci}
235062306a36Sopenharmony_ciDEVICE_ATTR_WO(delete_region);
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_cistatic void cxl_pmem_region_release(struct device *dev)
235362306a36Sopenharmony_ci{
235462306a36Sopenharmony_ci	struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev);
235562306a36Sopenharmony_ci	int i;
235662306a36Sopenharmony_ci
235762306a36Sopenharmony_ci	for (i = 0; i < cxlr_pmem->nr_mappings; i++) {
235862306a36Sopenharmony_ci		struct cxl_memdev *cxlmd = cxlr_pmem->mapping[i].cxlmd;
235962306a36Sopenharmony_ci
236062306a36Sopenharmony_ci		put_device(&cxlmd->dev);
236162306a36Sopenharmony_ci	}
236262306a36Sopenharmony_ci
236362306a36Sopenharmony_ci	kfree(cxlr_pmem);
236462306a36Sopenharmony_ci}
236562306a36Sopenharmony_ci
236662306a36Sopenharmony_cistatic const struct attribute_group *cxl_pmem_region_attribute_groups[] = {
236762306a36Sopenharmony_ci	&cxl_base_attribute_group,
236862306a36Sopenharmony_ci	NULL,
236962306a36Sopenharmony_ci};
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_ciconst struct device_type cxl_pmem_region_type = {
237262306a36Sopenharmony_ci	.name = "cxl_pmem_region",
237362306a36Sopenharmony_ci	.release = cxl_pmem_region_release,
237462306a36Sopenharmony_ci	.groups = cxl_pmem_region_attribute_groups,
237562306a36Sopenharmony_ci};
237662306a36Sopenharmony_ci
237762306a36Sopenharmony_cibool is_cxl_pmem_region(struct device *dev)
237862306a36Sopenharmony_ci{
237962306a36Sopenharmony_ci	return dev->type == &cxl_pmem_region_type;
238062306a36Sopenharmony_ci}
238162306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(is_cxl_pmem_region, CXL);
238262306a36Sopenharmony_ci
238362306a36Sopenharmony_cistruct cxl_pmem_region *to_cxl_pmem_region(struct device *dev)
238462306a36Sopenharmony_ci{
238562306a36Sopenharmony_ci	if (dev_WARN_ONCE(dev, !is_cxl_pmem_region(dev),
238662306a36Sopenharmony_ci			  "not a cxl_pmem_region device\n"))
238762306a36Sopenharmony_ci		return NULL;
238862306a36Sopenharmony_ci	return container_of(dev, struct cxl_pmem_region, dev);
238962306a36Sopenharmony_ci}
239062306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(to_cxl_pmem_region, CXL);
239162306a36Sopenharmony_ci
239262306a36Sopenharmony_cistruct cxl_poison_context {
239362306a36Sopenharmony_ci	struct cxl_port *port;
239462306a36Sopenharmony_ci	enum cxl_decoder_mode mode;
239562306a36Sopenharmony_ci	u64 offset;
239662306a36Sopenharmony_ci};
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_cistatic int cxl_get_poison_unmapped(struct cxl_memdev *cxlmd,
239962306a36Sopenharmony_ci				   struct cxl_poison_context *ctx)
240062306a36Sopenharmony_ci{
240162306a36Sopenharmony_ci	struct cxl_dev_state *cxlds = cxlmd->cxlds;
240262306a36Sopenharmony_ci	u64 offset, length;
240362306a36Sopenharmony_ci	int rc = 0;
240462306a36Sopenharmony_ci
240562306a36Sopenharmony_ci	/*
240662306a36Sopenharmony_ci	 * Collect poison for the remaining unmapped resources
240762306a36Sopenharmony_ci	 * after poison is collected by committed endpoints.
240862306a36Sopenharmony_ci	 *
240962306a36Sopenharmony_ci	 * Knowing that PMEM must always follow RAM, get poison
241062306a36Sopenharmony_ci	 * for unmapped resources based on the last decoder's mode:
241162306a36Sopenharmony_ci	 *	ram: scan remains of ram range, then any pmem range
241262306a36Sopenharmony_ci	 *	pmem: scan remains of pmem range
241362306a36Sopenharmony_ci	 */
241462306a36Sopenharmony_ci
241562306a36Sopenharmony_ci	if (ctx->mode == CXL_DECODER_RAM) {
241662306a36Sopenharmony_ci		offset = ctx->offset;
241762306a36Sopenharmony_ci		length = resource_size(&cxlds->ram_res) - offset;
241862306a36Sopenharmony_ci		rc = cxl_mem_get_poison(cxlmd, offset, length, NULL);
241962306a36Sopenharmony_ci		if (rc == -EFAULT)
242062306a36Sopenharmony_ci			rc = 0;
242162306a36Sopenharmony_ci		if (rc)
242262306a36Sopenharmony_ci			return rc;
242362306a36Sopenharmony_ci	}
242462306a36Sopenharmony_ci	if (ctx->mode == CXL_DECODER_PMEM) {
242562306a36Sopenharmony_ci		offset = ctx->offset;
242662306a36Sopenharmony_ci		length = resource_size(&cxlds->dpa_res) - offset;
242762306a36Sopenharmony_ci		if (!length)
242862306a36Sopenharmony_ci			return 0;
242962306a36Sopenharmony_ci	} else if (resource_size(&cxlds->pmem_res)) {
243062306a36Sopenharmony_ci		offset = cxlds->pmem_res.start;
243162306a36Sopenharmony_ci		length = resource_size(&cxlds->pmem_res);
243262306a36Sopenharmony_ci	} else {
243362306a36Sopenharmony_ci		return 0;
243462306a36Sopenharmony_ci	}
243562306a36Sopenharmony_ci
243662306a36Sopenharmony_ci	return cxl_mem_get_poison(cxlmd, offset, length, NULL);
243762306a36Sopenharmony_ci}
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_cistatic int poison_by_decoder(struct device *dev, void *arg)
244062306a36Sopenharmony_ci{
244162306a36Sopenharmony_ci	struct cxl_poison_context *ctx = arg;
244262306a36Sopenharmony_ci	struct cxl_endpoint_decoder *cxled;
244362306a36Sopenharmony_ci	struct cxl_memdev *cxlmd;
244462306a36Sopenharmony_ci	u64 offset, length;
244562306a36Sopenharmony_ci	int rc = 0;
244662306a36Sopenharmony_ci
244762306a36Sopenharmony_ci	if (!is_endpoint_decoder(dev))
244862306a36Sopenharmony_ci		return rc;
244962306a36Sopenharmony_ci
245062306a36Sopenharmony_ci	cxled = to_cxl_endpoint_decoder(dev);
245162306a36Sopenharmony_ci	if (!cxled->dpa_res || !resource_size(cxled->dpa_res))
245262306a36Sopenharmony_ci		return rc;
245362306a36Sopenharmony_ci
245462306a36Sopenharmony_ci	/*
245562306a36Sopenharmony_ci	 * Regions are only created with single mode decoders: pmem or ram.
245662306a36Sopenharmony_ci	 * Linux does not support mixed mode decoders. This means that
245762306a36Sopenharmony_ci	 * reading poison per endpoint decoder adheres to the requirement
245862306a36Sopenharmony_ci	 * that poison reads of pmem and ram must be separated.
245962306a36Sopenharmony_ci	 * CXL 3.0 Spec 8.2.9.8.4.1
246062306a36Sopenharmony_ci	 */
246162306a36Sopenharmony_ci	if (cxled->mode == CXL_DECODER_MIXED) {
246262306a36Sopenharmony_ci		dev_dbg(dev, "poison list read unsupported in mixed mode\n");
246362306a36Sopenharmony_ci		return rc;
246462306a36Sopenharmony_ci	}
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_ci	cxlmd = cxled_to_memdev(cxled);
246762306a36Sopenharmony_ci	if (cxled->skip) {
246862306a36Sopenharmony_ci		offset = cxled->dpa_res->start - cxled->skip;
246962306a36Sopenharmony_ci		length = cxled->skip;
247062306a36Sopenharmony_ci		rc = cxl_mem_get_poison(cxlmd, offset, length, NULL);
247162306a36Sopenharmony_ci		if (rc == -EFAULT && cxled->mode == CXL_DECODER_RAM)
247262306a36Sopenharmony_ci			rc = 0;
247362306a36Sopenharmony_ci		if (rc)
247462306a36Sopenharmony_ci			return rc;
247562306a36Sopenharmony_ci	}
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_ci	offset = cxled->dpa_res->start;
247862306a36Sopenharmony_ci	length = cxled->dpa_res->end - offset + 1;
247962306a36Sopenharmony_ci	rc = cxl_mem_get_poison(cxlmd, offset, length, cxled->cxld.region);
248062306a36Sopenharmony_ci	if (rc == -EFAULT && cxled->mode == CXL_DECODER_RAM)
248162306a36Sopenharmony_ci		rc = 0;
248262306a36Sopenharmony_ci	if (rc)
248362306a36Sopenharmony_ci		return rc;
248462306a36Sopenharmony_ci
248562306a36Sopenharmony_ci	/* Iterate until commit_end is reached */
248662306a36Sopenharmony_ci	if (cxled->cxld.id == ctx->port->commit_end) {
248762306a36Sopenharmony_ci		ctx->offset = cxled->dpa_res->end + 1;
248862306a36Sopenharmony_ci		ctx->mode = cxled->mode;
248962306a36Sopenharmony_ci		return 1;
249062306a36Sopenharmony_ci	}
249162306a36Sopenharmony_ci
249262306a36Sopenharmony_ci	return 0;
249362306a36Sopenharmony_ci}
249462306a36Sopenharmony_ci
249562306a36Sopenharmony_ciint cxl_get_poison_by_endpoint(struct cxl_port *port)
249662306a36Sopenharmony_ci{
249762306a36Sopenharmony_ci	struct cxl_poison_context ctx;
249862306a36Sopenharmony_ci	int rc = 0;
249962306a36Sopenharmony_ci
250062306a36Sopenharmony_ci	ctx = (struct cxl_poison_context) {
250162306a36Sopenharmony_ci		.port = port
250262306a36Sopenharmony_ci	};
250362306a36Sopenharmony_ci
250462306a36Sopenharmony_ci	rc = device_for_each_child(&port->dev, &ctx, poison_by_decoder);
250562306a36Sopenharmony_ci	if (rc == 1)
250662306a36Sopenharmony_ci		rc = cxl_get_poison_unmapped(to_cxl_memdev(port->uport_dev),
250762306a36Sopenharmony_ci					     &ctx);
250862306a36Sopenharmony_ci
250962306a36Sopenharmony_ci	return rc;
251062306a36Sopenharmony_ci}
251162306a36Sopenharmony_ci
251262306a36Sopenharmony_cistatic struct lock_class_key cxl_pmem_region_key;
251362306a36Sopenharmony_ci
251462306a36Sopenharmony_cistatic struct cxl_pmem_region *cxl_pmem_region_alloc(struct cxl_region *cxlr)
251562306a36Sopenharmony_ci{
251662306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
251762306a36Sopenharmony_ci	struct cxl_nvdimm_bridge *cxl_nvb;
251862306a36Sopenharmony_ci	struct cxl_pmem_region *cxlr_pmem;
251962306a36Sopenharmony_ci	struct device *dev;
252062306a36Sopenharmony_ci	int i;
252162306a36Sopenharmony_ci
252262306a36Sopenharmony_ci	down_read(&cxl_region_rwsem);
252362306a36Sopenharmony_ci	if (p->state != CXL_CONFIG_COMMIT) {
252462306a36Sopenharmony_ci		cxlr_pmem = ERR_PTR(-ENXIO);
252562306a36Sopenharmony_ci		goto out;
252662306a36Sopenharmony_ci	}
252762306a36Sopenharmony_ci
252862306a36Sopenharmony_ci	cxlr_pmem = kzalloc(struct_size(cxlr_pmem, mapping, p->nr_targets),
252962306a36Sopenharmony_ci			    GFP_KERNEL);
253062306a36Sopenharmony_ci	if (!cxlr_pmem) {
253162306a36Sopenharmony_ci		cxlr_pmem = ERR_PTR(-ENOMEM);
253262306a36Sopenharmony_ci		goto out;
253362306a36Sopenharmony_ci	}
253462306a36Sopenharmony_ci
253562306a36Sopenharmony_ci	cxlr_pmem->hpa_range.start = p->res->start;
253662306a36Sopenharmony_ci	cxlr_pmem->hpa_range.end = p->res->end;
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci	/* Snapshot the region configuration underneath the cxl_region_rwsem */
253962306a36Sopenharmony_ci	cxlr_pmem->nr_mappings = p->nr_targets;
254062306a36Sopenharmony_ci	for (i = 0; i < p->nr_targets; i++) {
254162306a36Sopenharmony_ci		struct cxl_endpoint_decoder *cxled = p->targets[i];
254262306a36Sopenharmony_ci		struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
254362306a36Sopenharmony_ci		struct cxl_pmem_region_mapping *m = &cxlr_pmem->mapping[i];
254462306a36Sopenharmony_ci
254562306a36Sopenharmony_ci		/*
254662306a36Sopenharmony_ci		 * Regions never span CXL root devices, so by definition the
254762306a36Sopenharmony_ci		 * bridge for one device is the same for all.
254862306a36Sopenharmony_ci		 */
254962306a36Sopenharmony_ci		if (i == 0) {
255062306a36Sopenharmony_ci			cxl_nvb = cxl_find_nvdimm_bridge(cxlmd);
255162306a36Sopenharmony_ci			if (!cxl_nvb) {
255262306a36Sopenharmony_ci				cxlr_pmem = ERR_PTR(-ENODEV);
255362306a36Sopenharmony_ci				goto out;
255462306a36Sopenharmony_ci			}
255562306a36Sopenharmony_ci			cxlr->cxl_nvb = cxl_nvb;
255662306a36Sopenharmony_ci		}
255762306a36Sopenharmony_ci		m->cxlmd = cxlmd;
255862306a36Sopenharmony_ci		get_device(&cxlmd->dev);
255962306a36Sopenharmony_ci		m->start = cxled->dpa_res->start;
256062306a36Sopenharmony_ci		m->size = resource_size(cxled->dpa_res);
256162306a36Sopenharmony_ci		m->position = i;
256262306a36Sopenharmony_ci	}
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_ci	dev = &cxlr_pmem->dev;
256562306a36Sopenharmony_ci	cxlr_pmem->cxlr = cxlr;
256662306a36Sopenharmony_ci	cxlr->cxlr_pmem = cxlr_pmem;
256762306a36Sopenharmony_ci	device_initialize(dev);
256862306a36Sopenharmony_ci	lockdep_set_class(&dev->mutex, &cxl_pmem_region_key);
256962306a36Sopenharmony_ci	device_set_pm_not_required(dev);
257062306a36Sopenharmony_ci	dev->parent = &cxlr->dev;
257162306a36Sopenharmony_ci	dev->bus = &cxl_bus_type;
257262306a36Sopenharmony_ci	dev->type = &cxl_pmem_region_type;
257362306a36Sopenharmony_ciout:
257462306a36Sopenharmony_ci	up_read(&cxl_region_rwsem);
257562306a36Sopenharmony_ci
257662306a36Sopenharmony_ci	return cxlr_pmem;
257762306a36Sopenharmony_ci}
257862306a36Sopenharmony_ci
257962306a36Sopenharmony_cistatic void cxl_dax_region_release(struct device *dev)
258062306a36Sopenharmony_ci{
258162306a36Sopenharmony_ci	struct cxl_dax_region *cxlr_dax = to_cxl_dax_region(dev);
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_ci	kfree(cxlr_dax);
258462306a36Sopenharmony_ci}
258562306a36Sopenharmony_ci
258662306a36Sopenharmony_cistatic const struct attribute_group *cxl_dax_region_attribute_groups[] = {
258762306a36Sopenharmony_ci	&cxl_base_attribute_group,
258862306a36Sopenharmony_ci	NULL,
258962306a36Sopenharmony_ci};
259062306a36Sopenharmony_ci
259162306a36Sopenharmony_ciconst struct device_type cxl_dax_region_type = {
259262306a36Sopenharmony_ci	.name = "cxl_dax_region",
259362306a36Sopenharmony_ci	.release = cxl_dax_region_release,
259462306a36Sopenharmony_ci	.groups = cxl_dax_region_attribute_groups,
259562306a36Sopenharmony_ci};
259662306a36Sopenharmony_ci
259762306a36Sopenharmony_cistatic bool is_cxl_dax_region(struct device *dev)
259862306a36Sopenharmony_ci{
259962306a36Sopenharmony_ci	return dev->type == &cxl_dax_region_type;
260062306a36Sopenharmony_ci}
260162306a36Sopenharmony_ci
260262306a36Sopenharmony_cistruct cxl_dax_region *to_cxl_dax_region(struct device *dev)
260362306a36Sopenharmony_ci{
260462306a36Sopenharmony_ci	if (dev_WARN_ONCE(dev, !is_cxl_dax_region(dev),
260562306a36Sopenharmony_ci			  "not a cxl_dax_region device\n"))
260662306a36Sopenharmony_ci		return NULL;
260762306a36Sopenharmony_ci	return container_of(dev, struct cxl_dax_region, dev);
260862306a36Sopenharmony_ci}
260962306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(to_cxl_dax_region, CXL);
261062306a36Sopenharmony_ci
261162306a36Sopenharmony_cistatic struct lock_class_key cxl_dax_region_key;
261262306a36Sopenharmony_ci
261362306a36Sopenharmony_cistatic struct cxl_dax_region *cxl_dax_region_alloc(struct cxl_region *cxlr)
261462306a36Sopenharmony_ci{
261562306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
261662306a36Sopenharmony_ci	struct cxl_dax_region *cxlr_dax;
261762306a36Sopenharmony_ci	struct device *dev;
261862306a36Sopenharmony_ci
261962306a36Sopenharmony_ci	down_read(&cxl_region_rwsem);
262062306a36Sopenharmony_ci	if (p->state != CXL_CONFIG_COMMIT) {
262162306a36Sopenharmony_ci		cxlr_dax = ERR_PTR(-ENXIO);
262262306a36Sopenharmony_ci		goto out;
262362306a36Sopenharmony_ci	}
262462306a36Sopenharmony_ci
262562306a36Sopenharmony_ci	cxlr_dax = kzalloc(sizeof(*cxlr_dax), GFP_KERNEL);
262662306a36Sopenharmony_ci	if (!cxlr_dax) {
262762306a36Sopenharmony_ci		cxlr_dax = ERR_PTR(-ENOMEM);
262862306a36Sopenharmony_ci		goto out;
262962306a36Sopenharmony_ci	}
263062306a36Sopenharmony_ci
263162306a36Sopenharmony_ci	cxlr_dax->hpa_range.start = p->res->start;
263262306a36Sopenharmony_ci	cxlr_dax->hpa_range.end = p->res->end;
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci	dev = &cxlr_dax->dev;
263562306a36Sopenharmony_ci	cxlr_dax->cxlr = cxlr;
263662306a36Sopenharmony_ci	device_initialize(dev);
263762306a36Sopenharmony_ci	lockdep_set_class(&dev->mutex, &cxl_dax_region_key);
263862306a36Sopenharmony_ci	device_set_pm_not_required(dev);
263962306a36Sopenharmony_ci	dev->parent = &cxlr->dev;
264062306a36Sopenharmony_ci	dev->bus = &cxl_bus_type;
264162306a36Sopenharmony_ci	dev->type = &cxl_dax_region_type;
264262306a36Sopenharmony_ciout:
264362306a36Sopenharmony_ci	up_read(&cxl_region_rwsem);
264462306a36Sopenharmony_ci
264562306a36Sopenharmony_ci	return cxlr_dax;
264662306a36Sopenharmony_ci}
264762306a36Sopenharmony_ci
264862306a36Sopenharmony_cistatic void cxlr_pmem_unregister(void *_cxlr_pmem)
264962306a36Sopenharmony_ci{
265062306a36Sopenharmony_ci	struct cxl_pmem_region *cxlr_pmem = _cxlr_pmem;
265162306a36Sopenharmony_ci	struct cxl_region *cxlr = cxlr_pmem->cxlr;
265262306a36Sopenharmony_ci	struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb;
265362306a36Sopenharmony_ci
265462306a36Sopenharmony_ci	/*
265562306a36Sopenharmony_ci	 * Either the bridge is in ->remove() context under the device_lock(),
265662306a36Sopenharmony_ci	 * or cxlr_release_nvdimm() is cancelling the bridge's release action
265762306a36Sopenharmony_ci	 * for @cxlr_pmem and doing it itself (while manually holding the bridge
265862306a36Sopenharmony_ci	 * lock).
265962306a36Sopenharmony_ci	 */
266062306a36Sopenharmony_ci	device_lock_assert(&cxl_nvb->dev);
266162306a36Sopenharmony_ci	cxlr->cxlr_pmem = NULL;
266262306a36Sopenharmony_ci	cxlr_pmem->cxlr = NULL;
266362306a36Sopenharmony_ci	device_unregister(&cxlr_pmem->dev);
266462306a36Sopenharmony_ci}
266562306a36Sopenharmony_ci
266662306a36Sopenharmony_cistatic void cxlr_release_nvdimm(void *_cxlr)
266762306a36Sopenharmony_ci{
266862306a36Sopenharmony_ci	struct cxl_region *cxlr = _cxlr;
266962306a36Sopenharmony_ci	struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb;
267062306a36Sopenharmony_ci
267162306a36Sopenharmony_ci	device_lock(&cxl_nvb->dev);
267262306a36Sopenharmony_ci	if (cxlr->cxlr_pmem)
267362306a36Sopenharmony_ci		devm_release_action(&cxl_nvb->dev, cxlr_pmem_unregister,
267462306a36Sopenharmony_ci				    cxlr->cxlr_pmem);
267562306a36Sopenharmony_ci	device_unlock(&cxl_nvb->dev);
267662306a36Sopenharmony_ci	cxlr->cxl_nvb = NULL;
267762306a36Sopenharmony_ci	put_device(&cxl_nvb->dev);
267862306a36Sopenharmony_ci}
267962306a36Sopenharmony_ci
268062306a36Sopenharmony_ci/**
268162306a36Sopenharmony_ci * devm_cxl_add_pmem_region() - add a cxl_region-to-nd_region bridge
268262306a36Sopenharmony_ci * @cxlr: parent CXL region for this pmem region bridge device
268362306a36Sopenharmony_ci *
268462306a36Sopenharmony_ci * Return: 0 on success negative error code on failure.
268562306a36Sopenharmony_ci */
268662306a36Sopenharmony_cistatic int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
268762306a36Sopenharmony_ci{
268862306a36Sopenharmony_ci	struct cxl_pmem_region *cxlr_pmem;
268962306a36Sopenharmony_ci	struct cxl_nvdimm_bridge *cxl_nvb;
269062306a36Sopenharmony_ci	struct device *dev;
269162306a36Sopenharmony_ci	int rc;
269262306a36Sopenharmony_ci
269362306a36Sopenharmony_ci	cxlr_pmem = cxl_pmem_region_alloc(cxlr);
269462306a36Sopenharmony_ci	if (IS_ERR(cxlr_pmem))
269562306a36Sopenharmony_ci		return PTR_ERR(cxlr_pmem);
269662306a36Sopenharmony_ci	cxl_nvb = cxlr->cxl_nvb;
269762306a36Sopenharmony_ci
269862306a36Sopenharmony_ci	dev = &cxlr_pmem->dev;
269962306a36Sopenharmony_ci	rc = dev_set_name(dev, "pmem_region%d", cxlr->id);
270062306a36Sopenharmony_ci	if (rc)
270162306a36Sopenharmony_ci		goto err;
270262306a36Sopenharmony_ci
270362306a36Sopenharmony_ci	rc = device_add(dev);
270462306a36Sopenharmony_ci	if (rc)
270562306a36Sopenharmony_ci		goto err;
270662306a36Sopenharmony_ci
270762306a36Sopenharmony_ci	dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent),
270862306a36Sopenharmony_ci		dev_name(dev));
270962306a36Sopenharmony_ci
271062306a36Sopenharmony_ci	device_lock(&cxl_nvb->dev);
271162306a36Sopenharmony_ci	if (cxl_nvb->dev.driver)
271262306a36Sopenharmony_ci		rc = devm_add_action_or_reset(&cxl_nvb->dev,
271362306a36Sopenharmony_ci					      cxlr_pmem_unregister, cxlr_pmem);
271462306a36Sopenharmony_ci	else
271562306a36Sopenharmony_ci		rc = -ENXIO;
271662306a36Sopenharmony_ci	device_unlock(&cxl_nvb->dev);
271762306a36Sopenharmony_ci
271862306a36Sopenharmony_ci	if (rc)
271962306a36Sopenharmony_ci		goto err_bridge;
272062306a36Sopenharmony_ci
272162306a36Sopenharmony_ci	/* @cxlr carries a reference on @cxl_nvb until cxlr_release_nvdimm */
272262306a36Sopenharmony_ci	return devm_add_action_or_reset(&cxlr->dev, cxlr_release_nvdimm, cxlr);
272362306a36Sopenharmony_ci
272462306a36Sopenharmony_cierr:
272562306a36Sopenharmony_ci	put_device(dev);
272662306a36Sopenharmony_cierr_bridge:
272762306a36Sopenharmony_ci	put_device(&cxl_nvb->dev);
272862306a36Sopenharmony_ci	cxlr->cxl_nvb = NULL;
272962306a36Sopenharmony_ci	return rc;
273062306a36Sopenharmony_ci}
273162306a36Sopenharmony_ci
273262306a36Sopenharmony_cistatic void cxlr_dax_unregister(void *_cxlr_dax)
273362306a36Sopenharmony_ci{
273462306a36Sopenharmony_ci	struct cxl_dax_region *cxlr_dax = _cxlr_dax;
273562306a36Sopenharmony_ci
273662306a36Sopenharmony_ci	device_unregister(&cxlr_dax->dev);
273762306a36Sopenharmony_ci}
273862306a36Sopenharmony_ci
273962306a36Sopenharmony_cistatic int devm_cxl_add_dax_region(struct cxl_region *cxlr)
274062306a36Sopenharmony_ci{
274162306a36Sopenharmony_ci	struct cxl_dax_region *cxlr_dax;
274262306a36Sopenharmony_ci	struct device *dev;
274362306a36Sopenharmony_ci	int rc;
274462306a36Sopenharmony_ci
274562306a36Sopenharmony_ci	cxlr_dax = cxl_dax_region_alloc(cxlr);
274662306a36Sopenharmony_ci	if (IS_ERR(cxlr_dax))
274762306a36Sopenharmony_ci		return PTR_ERR(cxlr_dax);
274862306a36Sopenharmony_ci
274962306a36Sopenharmony_ci	dev = &cxlr_dax->dev;
275062306a36Sopenharmony_ci	rc = dev_set_name(dev, "dax_region%d", cxlr->id);
275162306a36Sopenharmony_ci	if (rc)
275262306a36Sopenharmony_ci		goto err;
275362306a36Sopenharmony_ci
275462306a36Sopenharmony_ci	rc = device_add(dev);
275562306a36Sopenharmony_ci	if (rc)
275662306a36Sopenharmony_ci		goto err;
275762306a36Sopenharmony_ci
275862306a36Sopenharmony_ci	dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent),
275962306a36Sopenharmony_ci		dev_name(dev));
276062306a36Sopenharmony_ci
276162306a36Sopenharmony_ci	return devm_add_action_or_reset(&cxlr->dev, cxlr_dax_unregister,
276262306a36Sopenharmony_ci					cxlr_dax);
276362306a36Sopenharmony_cierr:
276462306a36Sopenharmony_ci	put_device(dev);
276562306a36Sopenharmony_ci	return rc;
276662306a36Sopenharmony_ci}
276762306a36Sopenharmony_ci
276862306a36Sopenharmony_cistatic int match_root_decoder_by_range(struct device *dev, void *data)
276962306a36Sopenharmony_ci{
277062306a36Sopenharmony_ci	struct range *r1, *r2 = data;
277162306a36Sopenharmony_ci	struct cxl_root_decoder *cxlrd;
277262306a36Sopenharmony_ci
277362306a36Sopenharmony_ci	if (!is_root_decoder(dev))
277462306a36Sopenharmony_ci		return 0;
277562306a36Sopenharmony_ci
277662306a36Sopenharmony_ci	cxlrd = to_cxl_root_decoder(dev);
277762306a36Sopenharmony_ci	r1 = &cxlrd->cxlsd.cxld.hpa_range;
277862306a36Sopenharmony_ci	return range_contains(r1, r2);
277962306a36Sopenharmony_ci}
278062306a36Sopenharmony_ci
278162306a36Sopenharmony_cistatic int match_region_by_range(struct device *dev, void *data)
278262306a36Sopenharmony_ci{
278362306a36Sopenharmony_ci	struct cxl_region_params *p;
278462306a36Sopenharmony_ci	struct cxl_region *cxlr;
278562306a36Sopenharmony_ci	struct range *r = data;
278662306a36Sopenharmony_ci	int rc = 0;
278762306a36Sopenharmony_ci
278862306a36Sopenharmony_ci	if (!is_cxl_region(dev))
278962306a36Sopenharmony_ci		return 0;
279062306a36Sopenharmony_ci
279162306a36Sopenharmony_ci	cxlr = to_cxl_region(dev);
279262306a36Sopenharmony_ci	p = &cxlr->params;
279362306a36Sopenharmony_ci
279462306a36Sopenharmony_ci	down_read(&cxl_region_rwsem);
279562306a36Sopenharmony_ci	if (p->res && p->res->start == r->start && p->res->end == r->end)
279662306a36Sopenharmony_ci		rc = 1;
279762306a36Sopenharmony_ci	up_read(&cxl_region_rwsem);
279862306a36Sopenharmony_ci
279962306a36Sopenharmony_ci	return rc;
280062306a36Sopenharmony_ci}
280162306a36Sopenharmony_ci
280262306a36Sopenharmony_ci/* Establish an empty region covering the given HPA range */
280362306a36Sopenharmony_cistatic struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
280462306a36Sopenharmony_ci					   struct cxl_endpoint_decoder *cxled)
280562306a36Sopenharmony_ci{
280662306a36Sopenharmony_ci	struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
280762306a36Sopenharmony_ci	struct cxl_port *port = cxlrd_to_port(cxlrd);
280862306a36Sopenharmony_ci	struct range *hpa = &cxled->cxld.hpa_range;
280962306a36Sopenharmony_ci	struct cxl_region_params *p;
281062306a36Sopenharmony_ci	struct cxl_region *cxlr;
281162306a36Sopenharmony_ci	struct resource *res;
281262306a36Sopenharmony_ci	int rc;
281362306a36Sopenharmony_ci
281462306a36Sopenharmony_ci	do {
281562306a36Sopenharmony_ci		cxlr = __create_region(cxlrd, cxled->mode,
281662306a36Sopenharmony_ci				       atomic_read(&cxlrd->region_id));
281762306a36Sopenharmony_ci	} while (IS_ERR(cxlr) && PTR_ERR(cxlr) == -EBUSY);
281862306a36Sopenharmony_ci
281962306a36Sopenharmony_ci	if (IS_ERR(cxlr)) {
282062306a36Sopenharmony_ci		dev_err(cxlmd->dev.parent,
282162306a36Sopenharmony_ci			"%s:%s: %s failed assign region: %ld\n",
282262306a36Sopenharmony_ci			dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
282362306a36Sopenharmony_ci			__func__, PTR_ERR(cxlr));
282462306a36Sopenharmony_ci		return cxlr;
282562306a36Sopenharmony_ci	}
282662306a36Sopenharmony_ci
282762306a36Sopenharmony_ci	down_write(&cxl_region_rwsem);
282862306a36Sopenharmony_ci	p = &cxlr->params;
282962306a36Sopenharmony_ci	if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) {
283062306a36Sopenharmony_ci		dev_err(cxlmd->dev.parent,
283162306a36Sopenharmony_ci			"%s:%s: %s autodiscovery interrupted\n",
283262306a36Sopenharmony_ci			dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
283362306a36Sopenharmony_ci			__func__);
283462306a36Sopenharmony_ci		rc = -EBUSY;
283562306a36Sopenharmony_ci		goto err;
283662306a36Sopenharmony_ci	}
283762306a36Sopenharmony_ci
283862306a36Sopenharmony_ci	set_bit(CXL_REGION_F_AUTO, &cxlr->flags);
283962306a36Sopenharmony_ci
284062306a36Sopenharmony_ci	res = kmalloc(sizeof(*res), GFP_KERNEL);
284162306a36Sopenharmony_ci	if (!res) {
284262306a36Sopenharmony_ci		rc = -ENOMEM;
284362306a36Sopenharmony_ci		goto err;
284462306a36Sopenharmony_ci	}
284562306a36Sopenharmony_ci
284662306a36Sopenharmony_ci	*res = DEFINE_RES_MEM_NAMED(hpa->start, range_len(hpa),
284762306a36Sopenharmony_ci				    dev_name(&cxlr->dev));
284862306a36Sopenharmony_ci	rc = insert_resource(cxlrd->res, res);
284962306a36Sopenharmony_ci	if (rc) {
285062306a36Sopenharmony_ci		/*
285162306a36Sopenharmony_ci		 * Platform-firmware may not have split resources like "System
285262306a36Sopenharmony_ci		 * RAM" on CXL window boundaries see cxl_region_iomem_release()
285362306a36Sopenharmony_ci		 */
285462306a36Sopenharmony_ci		dev_warn(cxlmd->dev.parent,
285562306a36Sopenharmony_ci			 "%s:%s: %s %s cannot insert resource\n",
285662306a36Sopenharmony_ci			 dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
285762306a36Sopenharmony_ci			 __func__, dev_name(&cxlr->dev));
285862306a36Sopenharmony_ci	}
285962306a36Sopenharmony_ci
286062306a36Sopenharmony_ci	p->res = res;
286162306a36Sopenharmony_ci	p->interleave_ways = cxled->cxld.interleave_ways;
286262306a36Sopenharmony_ci	p->interleave_granularity = cxled->cxld.interleave_granularity;
286362306a36Sopenharmony_ci	p->state = CXL_CONFIG_INTERLEAVE_ACTIVE;
286462306a36Sopenharmony_ci
286562306a36Sopenharmony_ci	rc = sysfs_update_group(&cxlr->dev.kobj, get_cxl_region_target_group());
286662306a36Sopenharmony_ci	if (rc)
286762306a36Sopenharmony_ci		goto err;
286862306a36Sopenharmony_ci
286962306a36Sopenharmony_ci	dev_dbg(cxlmd->dev.parent, "%s:%s: %s %s res: %pr iw: %d ig: %d\n",
287062306a36Sopenharmony_ci		dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), __func__,
287162306a36Sopenharmony_ci		dev_name(&cxlr->dev), p->res, p->interleave_ways,
287262306a36Sopenharmony_ci		p->interleave_granularity);
287362306a36Sopenharmony_ci
287462306a36Sopenharmony_ci	/* ...to match put_device() in cxl_add_to_region() */
287562306a36Sopenharmony_ci	get_device(&cxlr->dev);
287662306a36Sopenharmony_ci	up_write(&cxl_region_rwsem);
287762306a36Sopenharmony_ci
287862306a36Sopenharmony_ci	return cxlr;
287962306a36Sopenharmony_ci
288062306a36Sopenharmony_cierr:
288162306a36Sopenharmony_ci	up_write(&cxl_region_rwsem);
288262306a36Sopenharmony_ci	devm_release_action(port->uport_dev, unregister_region, cxlr);
288362306a36Sopenharmony_ci	return ERR_PTR(rc);
288462306a36Sopenharmony_ci}
288562306a36Sopenharmony_ci
288662306a36Sopenharmony_ciint cxl_add_to_region(struct cxl_port *root, struct cxl_endpoint_decoder *cxled)
288762306a36Sopenharmony_ci{
288862306a36Sopenharmony_ci	struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
288962306a36Sopenharmony_ci	struct range *hpa = &cxled->cxld.hpa_range;
289062306a36Sopenharmony_ci	struct cxl_decoder *cxld = &cxled->cxld;
289162306a36Sopenharmony_ci	struct device *cxlrd_dev, *region_dev;
289262306a36Sopenharmony_ci	struct cxl_root_decoder *cxlrd;
289362306a36Sopenharmony_ci	struct cxl_region_params *p;
289462306a36Sopenharmony_ci	struct cxl_region *cxlr;
289562306a36Sopenharmony_ci	bool attach = false;
289662306a36Sopenharmony_ci	int rc;
289762306a36Sopenharmony_ci
289862306a36Sopenharmony_ci	cxlrd_dev = device_find_child(&root->dev, &cxld->hpa_range,
289962306a36Sopenharmony_ci				      match_root_decoder_by_range);
290062306a36Sopenharmony_ci	if (!cxlrd_dev) {
290162306a36Sopenharmony_ci		dev_err(cxlmd->dev.parent,
290262306a36Sopenharmony_ci			"%s:%s no CXL window for range %#llx:%#llx\n",
290362306a36Sopenharmony_ci			dev_name(&cxlmd->dev), dev_name(&cxld->dev),
290462306a36Sopenharmony_ci			cxld->hpa_range.start, cxld->hpa_range.end);
290562306a36Sopenharmony_ci		return -ENXIO;
290662306a36Sopenharmony_ci	}
290762306a36Sopenharmony_ci
290862306a36Sopenharmony_ci	cxlrd = to_cxl_root_decoder(cxlrd_dev);
290962306a36Sopenharmony_ci
291062306a36Sopenharmony_ci	/*
291162306a36Sopenharmony_ci	 * Ensure that if multiple threads race to construct_region() for @hpa
291262306a36Sopenharmony_ci	 * one does the construction and the others add to that.
291362306a36Sopenharmony_ci	 */
291462306a36Sopenharmony_ci	mutex_lock(&cxlrd->range_lock);
291562306a36Sopenharmony_ci	region_dev = device_find_child(&cxlrd->cxlsd.cxld.dev, hpa,
291662306a36Sopenharmony_ci				       match_region_by_range);
291762306a36Sopenharmony_ci	if (!region_dev) {
291862306a36Sopenharmony_ci		cxlr = construct_region(cxlrd, cxled);
291962306a36Sopenharmony_ci		region_dev = &cxlr->dev;
292062306a36Sopenharmony_ci	} else
292162306a36Sopenharmony_ci		cxlr = to_cxl_region(region_dev);
292262306a36Sopenharmony_ci	mutex_unlock(&cxlrd->range_lock);
292362306a36Sopenharmony_ci
292462306a36Sopenharmony_ci	rc = PTR_ERR_OR_ZERO(cxlr);
292562306a36Sopenharmony_ci	if (rc)
292662306a36Sopenharmony_ci		goto out;
292762306a36Sopenharmony_ci
292862306a36Sopenharmony_ci	attach_target(cxlr, cxled, -1, TASK_UNINTERRUPTIBLE);
292962306a36Sopenharmony_ci
293062306a36Sopenharmony_ci	down_read(&cxl_region_rwsem);
293162306a36Sopenharmony_ci	p = &cxlr->params;
293262306a36Sopenharmony_ci	attach = p->state == CXL_CONFIG_COMMIT;
293362306a36Sopenharmony_ci	up_read(&cxl_region_rwsem);
293462306a36Sopenharmony_ci
293562306a36Sopenharmony_ci	if (attach) {
293662306a36Sopenharmony_ci		/*
293762306a36Sopenharmony_ci		 * If device_attach() fails the range may still be active via
293862306a36Sopenharmony_ci		 * the platform-firmware memory map, otherwise the driver for
293962306a36Sopenharmony_ci		 * regions is local to this file, so driver matching can't fail.
294062306a36Sopenharmony_ci		 */
294162306a36Sopenharmony_ci		if (device_attach(&cxlr->dev) < 0)
294262306a36Sopenharmony_ci			dev_err(&cxlr->dev, "failed to enable, range: %pr\n",
294362306a36Sopenharmony_ci				p->res);
294462306a36Sopenharmony_ci	}
294562306a36Sopenharmony_ci
294662306a36Sopenharmony_ci	put_device(region_dev);
294762306a36Sopenharmony_ciout:
294862306a36Sopenharmony_ci	put_device(cxlrd_dev);
294962306a36Sopenharmony_ci	return rc;
295062306a36Sopenharmony_ci}
295162306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(cxl_add_to_region, CXL);
295262306a36Sopenharmony_ci
295362306a36Sopenharmony_cistatic int is_system_ram(struct resource *res, void *arg)
295462306a36Sopenharmony_ci{
295562306a36Sopenharmony_ci	struct cxl_region *cxlr = arg;
295662306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
295762306a36Sopenharmony_ci
295862306a36Sopenharmony_ci	dev_dbg(&cxlr->dev, "%pr has System RAM: %pr\n", p->res, res);
295962306a36Sopenharmony_ci	return 1;
296062306a36Sopenharmony_ci}
296162306a36Sopenharmony_ci
296262306a36Sopenharmony_cistatic int cxl_region_probe(struct device *dev)
296362306a36Sopenharmony_ci{
296462306a36Sopenharmony_ci	struct cxl_region *cxlr = to_cxl_region(dev);
296562306a36Sopenharmony_ci	struct cxl_region_params *p = &cxlr->params;
296662306a36Sopenharmony_ci	int rc;
296762306a36Sopenharmony_ci
296862306a36Sopenharmony_ci	rc = down_read_interruptible(&cxl_region_rwsem);
296962306a36Sopenharmony_ci	if (rc) {
297062306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "probe interrupted\n");
297162306a36Sopenharmony_ci		return rc;
297262306a36Sopenharmony_ci	}
297362306a36Sopenharmony_ci
297462306a36Sopenharmony_ci	if (p->state < CXL_CONFIG_COMMIT) {
297562306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "config state: %d\n", p->state);
297662306a36Sopenharmony_ci		rc = -ENXIO;
297762306a36Sopenharmony_ci		goto out;
297862306a36Sopenharmony_ci	}
297962306a36Sopenharmony_ci
298062306a36Sopenharmony_ci	if (test_bit(CXL_REGION_F_NEEDS_RESET, &cxlr->flags)) {
298162306a36Sopenharmony_ci		dev_err(&cxlr->dev,
298262306a36Sopenharmony_ci			"failed to activate, re-commit region and retry\n");
298362306a36Sopenharmony_ci		rc = -ENXIO;
298462306a36Sopenharmony_ci		goto out;
298562306a36Sopenharmony_ci	}
298662306a36Sopenharmony_ci
298762306a36Sopenharmony_ci	/*
298862306a36Sopenharmony_ci	 * From this point on any path that changes the region's state away from
298962306a36Sopenharmony_ci	 * CXL_CONFIG_COMMIT is also responsible for releasing the driver.
299062306a36Sopenharmony_ci	 */
299162306a36Sopenharmony_ciout:
299262306a36Sopenharmony_ci	up_read(&cxl_region_rwsem);
299362306a36Sopenharmony_ci
299462306a36Sopenharmony_ci	if (rc)
299562306a36Sopenharmony_ci		return rc;
299662306a36Sopenharmony_ci
299762306a36Sopenharmony_ci	switch (cxlr->mode) {
299862306a36Sopenharmony_ci	case CXL_DECODER_PMEM:
299962306a36Sopenharmony_ci		return devm_cxl_add_pmem_region(cxlr);
300062306a36Sopenharmony_ci	case CXL_DECODER_RAM:
300162306a36Sopenharmony_ci		/*
300262306a36Sopenharmony_ci		 * The region can not be manged by CXL if any portion of
300362306a36Sopenharmony_ci		 * it is already online as 'System RAM'
300462306a36Sopenharmony_ci		 */
300562306a36Sopenharmony_ci		if (walk_iomem_res_desc(IORES_DESC_NONE,
300662306a36Sopenharmony_ci					IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY,
300762306a36Sopenharmony_ci					p->res->start, p->res->end, cxlr,
300862306a36Sopenharmony_ci					is_system_ram) > 0)
300962306a36Sopenharmony_ci			return 0;
301062306a36Sopenharmony_ci		return devm_cxl_add_dax_region(cxlr);
301162306a36Sopenharmony_ci	default:
301262306a36Sopenharmony_ci		dev_dbg(&cxlr->dev, "unsupported region mode: %d\n",
301362306a36Sopenharmony_ci			cxlr->mode);
301462306a36Sopenharmony_ci		return -ENXIO;
301562306a36Sopenharmony_ci	}
301662306a36Sopenharmony_ci}
301762306a36Sopenharmony_ci
301862306a36Sopenharmony_cistatic struct cxl_driver cxl_region_driver = {
301962306a36Sopenharmony_ci	.name = "cxl_region",
302062306a36Sopenharmony_ci	.probe = cxl_region_probe,
302162306a36Sopenharmony_ci	.id = CXL_DEVICE_REGION,
302262306a36Sopenharmony_ci};
302362306a36Sopenharmony_ci
302462306a36Sopenharmony_ciint cxl_region_init(void)
302562306a36Sopenharmony_ci{
302662306a36Sopenharmony_ci	return cxl_driver_register(&cxl_region_driver);
302762306a36Sopenharmony_ci}
302862306a36Sopenharmony_ci
302962306a36Sopenharmony_civoid cxl_region_exit(void)
303062306a36Sopenharmony_ci{
303162306a36Sopenharmony_ci	cxl_driver_unregister(&cxl_region_driver);
303262306a36Sopenharmony_ci}
303362306a36Sopenharmony_ci
303462306a36Sopenharmony_ciMODULE_IMPORT_NS(CXL);
303562306a36Sopenharmony_ciMODULE_IMPORT_NS(DEVMEM);
303662306a36Sopenharmony_ciMODULE_ALIAS_CXL(CXL_DEVICE_REGION);
3037