xref: /openbmc/linux/drivers/cxl/core/port.c (revision 5f8b7d4b2e9604d03ae06f1a2dd5a1f34c33e533)
10ff0af18SDan Williams // SPDX-License-Identifier: GPL-2.0-only
20ff0af18SDan Williams /* Copyright(c) 2020 Intel Corporation. All rights reserved. */
3a76b6251SDan Williams #include <linux/platform_device.h>
4779dd20cSBen Widawsky #include <linux/memregion.h>
58dd2bc0fSBen Widawsky #include <linux/workqueue.h>
69b99ecf5SDan Williams #include <linux/debugfs.h>
70ff0af18SDan Williams #include <linux/device.h>
80ff0af18SDan Williams #include <linux/module.h>
90ff0af18SDan Williams #include <linux/pci.h>
100ff0af18SDan Williams #include <linux/slab.h>
110ff0af18SDan Williams #include <linux/idr.h>
120ff0af18SDan Williams #include <cxlmem.h>
132703c16cSDan Williams #include <cxlpci.h>
140ff0af18SDan Williams #include <cxl.h>
150ff0af18SDan Williams #include "core.h"
160ff0af18SDan Williams 
170ff0af18SDan Williams /**
180ff0af18SDan Williams  * DOC: cxl core
190ff0af18SDan Williams  *
200ff0af18SDan Williams  * The CXL core provides a set of interfaces that can be consumed by CXL aware
210ff0af18SDan Williams  * drivers. The interfaces allow for creation, modification, and destruction of
220ff0af18SDan Williams  * regions, memory devices, ports, and decoders. CXL aware drivers must register
230ff0af18SDan Williams  * with the CXL core via these interfaces in order to be able to participate in
240ff0af18SDan Williams  * cross-device interleave coordination. The CXL core also establishes and
250ff0af18SDan Williams  * maintains the bridge to the nvdimm subsystem.
260ff0af18SDan Williams  *
270ff0af18SDan Williams  * CXL core introduces sysfs hierarchy to control the devices that are
280ff0af18SDan Williams  * instantiated by the core.
290ff0af18SDan Williams  */
300ff0af18SDan Williams 
31d1d13a09SDan Williams /*
32d1d13a09SDan Williams  * All changes to the interleave configuration occur with this lock held
33d1d13a09SDan Williams  * for write.
34d1d13a09SDan Williams  */
35d1d13a09SDan Williams DECLARE_RWSEM(cxl_region_rwsem);
36d1d13a09SDan Williams 
370ff0af18SDan Williams static DEFINE_IDA(cxl_port_ida);
385ff7316fSDan Williams static DEFINE_XARRAY(cxl_root_buses);
390ff0af18SDan Williams 
cxl_num_decoders_committed(struct cxl_port * port)4007f9a20bSDave Jiang int cxl_num_decoders_committed(struct cxl_port *port)
4107f9a20bSDave Jiang {
4207f9a20bSDave Jiang 	lockdep_assert_held(&cxl_region_rwsem);
4307f9a20bSDave Jiang 
4407f9a20bSDave Jiang 	return port->commit_end + 1;
4507f9a20bSDave Jiang }
4607f9a20bSDave Jiang 
devtype_show(struct device * dev,struct device_attribute * attr,char * buf)470ff0af18SDan Williams static ssize_t devtype_show(struct device *dev, struct device_attribute *attr,
480ff0af18SDan Williams 			    char *buf)
490ff0af18SDan Williams {
500ff0af18SDan Williams 	return sysfs_emit(buf, "%s\n", dev->type->name);
510ff0af18SDan Williams }
520ff0af18SDan Williams static DEVICE_ATTR_RO(devtype);
530ff0af18SDan Williams 
cxl_device_id(const struct device * dev)542a81ada3SGreg Kroah-Hartman static int cxl_device_id(const struct device *dev)
5583fbdbe4SDan Williams {
5683fbdbe4SDan Williams 	if (dev->type == &cxl_nvdimm_bridge_type)
5783fbdbe4SDan Williams 		return CXL_DEVICE_NVDIMM_BRIDGE;
5883fbdbe4SDan Williams 	if (dev->type == &cxl_nvdimm_type)
5983fbdbe4SDan Williams 		return CXL_DEVICE_NVDIMM;
6004ad63f0SDan Williams 	if (dev->type == CXL_PMEM_REGION_TYPE())
6104ad63f0SDan Williams 		return CXL_DEVICE_PMEM_REGION;
6209d09e04SDan Williams 	if (dev->type == CXL_DAX_REGION_TYPE())
6309d09e04SDan Williams 		return CXL_DEVICE_DAX_REGION;
6454cdbf84SBen Widawsky 	if (is_cxl_port(dev)) {
6554cdbf84SBen Widawsky 		if (is_cxl_root(to_cxl_port(dev)))
6654cdbf84SBen Widawsky 			return CXL_DEVICE_ROOT;
6754cdbf84SBen Widawsky 		return CXL_DEVICE_PORT;
6854cdbf84SBen Widawsky 	}
698dd2bc0fSBen Widawsky 	if (is_cxl_memdev(dev))
708dd2bc0fSBen Widawsky 		return CXL_DEVICE_MEMORY_EXPANDER;
718d48817dSDan Williams 	if (dev->type == CXL_REGION_TYPE())
728d48817dSDan Williams 		return CXL_DEVICE_REGION;
731ad3f701SJonathan Cameron 	if (dev->type == &cxl_pmu_type)
741ad3f701SJonathan Cameron 		return CXL_DEVICE_PMU;
7583fbdbe4SDan Williams 	return 0;
7683fbdbe4SDan Williams }
7783fbdbe4SDan Williams 
modalias_show(struct device * dev,struct device_attribute * attr,char * buf)7883fbdbe4SDan Williams static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
7983fbdbe4SDan Williams 			     char *buf)
8083fbdbe4SDan Williams {
8183fbdbe4SDan Williams 	return sysfs_emit(buf, CXL_MODALIAS_FMT "\n", cxl_device_id(dev));
8283fbdbe4SDan Williams }
8383fbdbe4SDan Williams static DEVICE_ATTR_RO(modalias);
8483fbdbe4SDan Williams 
850ff0af18SDan Williams static struct attribute *cxl_base_attributes[] = {
860ff0af18SDan Williams 	&dev_attr_devtype.attr,
8783fbdbe4SDan Williams 	&dev_attr_modalias.attr,
880ff0af18SDan Williams 	NULL,
890ff0af18SDan Williams };
900ff0af18SDan Williams 
910ff0af18SDan Williams struct attribute_group cxl_base_attribute_group = {
920ff0af18SDan Williams 	.attrs = cxl_base_attributes,
930ff0af18SDan Williams };
940ff0af18SDan Williams 
start_show(struct device * dev,struct device_attribute * attr,char * buf)950ff0af18SDan Williams static ssize_t start_show(struct device *dev, struct device_attribute *attr,
960ff0af18SDan Williams 			  char *buf)
970ff0af18SDan Williams {
980ff0af18SDan Williams 	struct cxl_decoder *cxld = to_cxl_decoder(dev);
990ff0af18SDan Williams 
100e50fe01eSDan Williams 	return sysfs_emit(buf, "%#llx\n", cxld->hpa_range.start);
1010ff0af18SDan Williams }
102c3bca8d4SDan Williams static DEVICE_ATTR_ADMIN_RO(start);
1030ff0af18SDan Williams 
size_show(struct device * dev,struct device_attribute * attr,char * buf)1040ff0af18SDan Williams static ssize_t size_show(struct device *dev, struct device_attribute *attr,
1050ff0af18SDan Williams 			char *buf)
1060ff0af18SDan Williams {
1070ff0af18SDan Williams 	struct cxl_decoder *cxld = to_cxl_decoder(dev);
1080ff0af18SDan Williams 
109e50fe01eSDan Williams 	return sysfs_emit(buf, "%#llx\n", range_len(&cxld->hpa_range));
1100ff0af18SDan Williams }
1110ff0af18SDan Williams static DEVICE_ATTR_RO(size);
1120ff0af18SDan Williams 
1130ff0af18SDan Williams #define CXL_DECODER_FLAG_ATTR(name, flag)                            \
1140ff0af18SDan Williams static ssize_t name##_show(struct device *dev,                       \
1150ff0af18SDan Williams 			   struct device_attribute *attr, char *buf) \
1160ff0af18SDan Williams {                                                                    \
1170ff0af18SDan Williams 	struct cxl_decoder *cxld = to_cxl_decoder(dev);              \
1180ff0af18SDan Williams                                                                      \
1190ff0af18SDan Williams 	return sysfs_emit(buf, "%s\n",                               \
1200ff0af18SDan Williams 			  (cxld->flags & (flag)) ? "1" : "0");       \
1210ff0af18SDan Williams }                                                                    \
1220ff0af18SDan Williams static DEVICE_ATTR_RO(name)
1230ff0af18SDan Williams 
1240ff0af18SDan Williams CXL_DECODER_FLAG_ATTR(cap_pmem, CXL_DECODER_F_PMEM);
1250ff0af18SDan Williams CXL_DECODER_FLAG_ATTR(cap_ram, CXL_DECODER_F_RAM);
1260ff0af18SDan Williams CXL_DECODER_FLAG_ATTR(cap_type2, CXL_DECODER_F_TYPE2);
1270ff0af18SDan Williams CXL_DECODER_FLAG_ATTR(cap_type3, CXL_DECODER_F_TYPE3);
1280ff0af18SDan Williams CXL_DECODER_FLAG_ATTR(locked, CXL_DECODER_F_LOCK);
1290ff0af18SDan Williams 
target_type_show(struct device * dev,struct device_attribute * attr,char * buf)1300ff0af18SDan Williams static ssize_t target_type_show(struct device *dev,
1310ff0af18SDan Williams 				struct device_attribute *attr, char *buf)
1320ff0af18SDan Williams {
1330ff0af18SDan Williams 	struct cxl_decoder *cxld = to_cxl_decoder(dev);
1340ff0af18SDan Williams 
1350ff0af18SDan Williams 	switch (cxld->target_type) {
1365aa39a91SDan Williams 	case CXL_DECODER_DEVMEM:
1370ff0af18SDan Williams 		return sysfs_emit(buf, "accelerator\n");
1385aa39a91SDan Williams 	case CXL_DECODER_HOSTONLYMEM:
1390ff0af18SDan Williams 		return sysfs_emit(buf, "expander\n");
1400ff0af18SDan Williams 	}
1410ff0af18SDan Williams 	return -ENXIO;
1420ff0af18SDan Williams }
1430ff0af18SDan Williams static DEVICE_ATTR_RO(target_type);
1440ff0af18SDan Williams 
emit_target_list(struct cxl_switch_decoder * cxlsd,char * buf)145e636479eSDan Williams static ssize_t emit_target_list(struct cxl_switch_decoder *cxlsd, char *buf)
1460ff0af18SDan Williams {
147e636479eSDan Williams 	struct cxl_decoder *cxld = &cxlsd->cxld;
1480ff0af18SDan Williams 	ssize_t offset = 0;
1490ff0af18SDan Williams 	int i, rc = 0;
1500ff0af18SDan Williams 
1510ff0af18SDan Williams 	for (i = 0; i < cxld->interleave_ways; i++) {
152e636479eSDan Williams 		struct cxl_dport *dport = cxlsd->target[i];
1530ff0af18SDan Williams 		struct cxl_dport *next = NULL;
1540ff0af18SDan Williams 
1550ff0af18SDan Williams 		if (!dport)
1560ff0af18SDan Williams 			break;
1570ff0af18SDan Williams 
1580ff0af18SDan Williams 		if (i + 1 < cxld->interleave_ways)
159e636479eSDan Williams 			next = cxlsd->target[i + 1];
1600ff0af18SDan Williams 		rc = sysfs_emit_at(buf, offset, "%d%s", dport->port_id,
1610ff0af18SDan Williams 				   next ? "," : "");
1620ff0af18SDan Williams 		if (rc < 0)
16386c8ea0fSDan Williams 			return rc;
1640ff0af18SDan Williams 		offset += rc;
1650ff0af18SDan Williams 	}
16686c8ea0fSDan Williams 
16786c8ea0fSDan Williams 	return offset;
16886c8ea0fSDan Williams }
16986c8ea0fSDan Williams 
target_list_show(struct device * dev,struct device_attribute * attr,char * buf)17086c8ea0fSDan Williams static ssize_t target_list_show(struct device *dev,
17186c8ea0fSDan Williams 				struct device_attribute *attr, char *buf)
17286c8ea0fSDan Williams {
173e636479eSDan Williams 	struct cxl_switch_decoder *cxlsd = to_cxl_switch_decoder(dev);
17486c8ea0fSDan Williams 	ssize_t offset;
17586c8ea0fSDan Williams 	int rc;
17686c8ea0fSDan Williams 
1771a536972SDan Williams 	guard(rwsem_read)(&cxl_region_rwsem);
178e636479eSDan Williams 	rc = emit_target_list(cxlsd, buf);
1790ff0af18SDan Williams 	if (rc < 0)
1800ff0af18SDan Williams 		return rc;
18186c8ea0fSDan Williams 	offset = rc;
1820ff0af18SDan Williams 
1830ff0af18SDan Williams 	rc = sysfs_emit_at(buf, offset, "\n");
1840ff0af18SDan Williams 	if (rc < 0)
1850ff0af18SDan Williams 		return rc;
1860ff0af18SDan Williams 
1870ff0af18SDan Williams 	return offset + rc;
1880ff0af18SDan Williams }
1890ff0af18SDan Williams static DEVICE_ATTR_RO(target_list);
1900ff0af18SDan Williams 
mode_show(struct device * dev,struct device_attribute * attr,char * buf)1912c866903SDan Williams static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
1922c866903SDan Williams 			 char *buf)
1932c866903SDan Williams {
1942c866903SDan Williams 	struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(dev);
1952c866903SDan Williams 
1967d505f98SDan Williams 	return sysfs_emit(buf, "%s\n", cxl_decoder_mode_name(cxled->mode));
1972c866903SDan Williams }
198cf880423SDan Williams 
mode_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t len)199cf880423SDan Williams static ssize_t mode_store(struct device *dev, struct device_attribute *attr,
200cf880423SDan Williams 			  const char *buf, size_t len)
201cf880423SDan Williams {
202cf880423SDan Williams 	struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(dev);
203cf880423SDan Williams 	enum cxl_decoder_mode mode;
204cf880423SDan Williams 	ssize_t rc;
205cf880423SDan Williams 
206cf880423SDan Williams 	if (sysfs_streq(buf, "pmem"))
207cf880423SDan Williams 		mode = CXL_DECODER_PMEM;
208cf880423SDan Williams 	else if (sysfs_streq(buf, "ram"))
209cf880423SDan Williams 		mode = CXL_DECODER_RAM;
210cf880423SDan Williams 	else
211cf880423SDan Williams 		return -EINVAL;
212cf880423SDan Williams 
213cf880423SDan Williams 	rc = cxl_dpa_set_mode(cxled, mode);
214cf880423SDan Williams 	if (rc)
215cf880423SDan Williams 		return rc;
216cf880423SDan Williams 
217cf880423SDan Williams 	return len;
218cf880423SDan Williams }
219cf880423SDan Williams static DEVICE_ATTR_RW(mode);
220cf880423SDan Williams 
dpa_resource_show(struct device * dev,struct device_attribute * attr,char * buf)221cf880423SDan Williams static ssize_t dpa_resource_show(struct device *dev, struct device_attribute *attr,
222cf880423SDan Williams 			    char *buf)
223cf880423SDan Williams {
224cf880423SDan Williams 	struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(dev);
225cf880423SDan Williams 
226c1d2d084SDan Williams 	guard(rwsem_read)(&cxl_dpa_rwsem);
227c1d2d084SDan Williams 	return sysfs_emit(buf, "%#llx\n", (u64)cxl_dpa_resource_start(cxled));
228cf880423SDan Williams }
229cf880423SDan Williams static DEVICE_ATTR_RO(dpa_resource);
230cf880423SDan Williams 
dpa_size_show(struct device * dev,struct device_attribute * attr,char * buf)231cf880423SDan Williams static ssize_t dpa_size_show(struct device *dev, struct device_attribute *attr,
232cf880423SDan Williams 			     char *buf)
233cf880423SDan Williams {
234cf880423SDan Williams 	struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(dev);
235cf880423SDan Williams 	resource_size_t size = cxl_dpa_size(cxled);
236cf880423SDan Williams 
237cf880423SDan Williams 	return sysfs_emit(buf, "%pa\n", &size);
238cf880423SDan Williams }
239cf880423SDan Williams 
dpa_size_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t len)240cf880423SDan Williams static ssize_t dpa_size_store(struct device *dev, struct device_attribute *attr,
241cf880423SDan Williams 			      const char *buf, size_t len)
242cf880423SDan Williams {
243cf880423SDan Williams 	struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(dev);
244cf880423SDan Williams 	unsigned long long size;
245cf880423SDan Williams 	ssize_t rc;
246cf880423SDan Williams 
247cf880423SDan Williams 	rc = kstrtoull(buf, 0, &size);
248cf880423SDan Williams 	if (rc)
249cf880423SDan Williams 		return rc;
250cf880423SDan Williams 
251cf880423SDan Williams 	if (!IS_ALIGNED(size, SZ_256M))
252cf880423SDan Williams 		return -EINVAL;
253cf880423SDan Williams 
254cf880423SDan Williams 	rc = cxl_dpa_free(cxled);
255cf880423SDan Williams 	if (rc)
256cf880423SDan Williams 		return rc;
257cf880423SDan Williams 
258cf880423SDan Williams 	if (size == 0)
259cf880423SDan Williams 		return len;
260cf880423SDan Williams 
261cf880423SDan Williams 	rc = cxl_dpa_alloc(cxled, size);
262cf880423SDan Williams 	if (rc)
263cf880423SDan Williams 		return rc;
264cf880423SDan Williams 
265cf880423SDan Williams 	return len;
266cf880423SDan Williams }
267cf880423SDan Williams static DEVICE_ATTR_RW(dpa_size);
2682c866903SDan Williams 
interleave_granularity_show(struct device * dev,struct device_attribute * attr,char * buf)269538831f1SBen Widawsky static ssize_t interleave_granularity_show(struct device *dev,
270538831f1SBen Widawsky 					   struct device_attribute *attr,
271538831f1SBen Widawsky 					   char *buf)
272538831f1SBen Widawsky {
273538831f1SBen Widawsky 	struct cxl_decoder *cxld = to_cxl_decoder(dev);
274538831f1SBen Widawsky 
275538831f1SBen Widawsky 	return sysfs_emit(buf, "%d\n", cxld->interleave_granularity);
276538831f1SBen Widawsky }
277538831f1SBen Widawsky 
278538831f1SBen Widawsky static DEVICE_ATTR_RO(interleave_granularity);
279538831f1SBen Widawsky 
interleave_ways_show(struct device * dev,struct device_attribute * attr,char * buf)280538831f1SBen Widawsky static ssize_t interleave_ways_show(struct device *dev,
281538831f1SBen Widawsky 				    struct device_attribute *attr, char *buf)
282538831f1SBen Widawsky {
283538831f1SBen Widawsky 	struct cxl_decoder *cxld = to_cxl_decoder(dev);
284538831f1SBen Widawsky 
285538831f1SBen Widawsky 	return sysfs_emit(buf, "%d\n", cxld->interleave_ways);
286538831f1SBen Widawsky }
287538831f1SBen Widawsky 
288538831f1SBen Widawsky static DEVICE_ATTR_RO(interleave_ways);
289538831f1SBen Widawsky 
2900ff0af18SDan Williams static struct attribute *cxl_decoder_base_attrs[] = {
2910ff0af18SDan Williams 	&dev_attr_start.attr,
2920ff0af18SDan Williams 	&dev_attr_size.attr,
2930ff0af18SDan Williams 	&dev_attr_locked.attr,
294538831f1SBen Widawsky 	&dev_attr_interleave_granularity.attr,
295538831f1SBen Widawsky 	&dev_attr_interleave_ways.attr,
2960ff0af18SDan Williams 	NULL,
2970ff0af18SDan Williams };
2980ff0af18SDan Williams 
2990ff0af18SDan Williams static struct attribute_group cxl_decoder_base_attribute_group = {
3000ff0af18SDan Williams 	.attrs = cxl_decoder_base_attrs,
3010ff0af18SDan Williams };
3020ff0af18SDan Williams 
3030ff0af18SDan Williams static struct attribute *cxl_decoder_root_attrs[] = {
3040ff0af18SDan Williams 	&dev_attr_cap_pmem.attr,
3050ff0af18SDan Williams 	&dev_attr_cap_ram.attr,
3060ff0af18SDan Williams 	&dev_attr_cap_type2.attr,
3070ff0af18SDan Williams 	&dev_attr_cap_type3.attr,
3088aea0ef1SDan Williams 	&dev_attr_target_list.attr,
309779dd20cSBen Widawsky 	SET_CXL_REGION_ATTR(create_pmem_region)
3106e099264SDan Williams 	SET_CXL_REGION_ATTR(create_ram_region)
311779dd20cSBen Widawsky 	SET_CXL_REGION_ATTR(delete_region)
3120ff0af18SDan Williams 	NULL,
3130ff0af18SDan Williams };
3140ff0af18SDan Williams 
can_create_pmem(struct cxl_root_decoder * cxlrd)315779dd20cSBen Widawsky static bool can_create_pmem(struct cxl_root_decoder *cxlrd)
316779dd20cSBen Widawsky {
317779dd20cSBen Widawsky 	unsigned long flags = CXL_DECODER_F_TYPE3 | CXL_DECODER_F_PMEM;
318779dd20cSBen Widawsky 
319779dd20cSBen Widawsky 	return (cxlrd->cxlsd.cxld.flags & flags) == flags;
320779dd20cSBen Widawsky }
321779dd20cSBen Widawsky 
can_create_ram(struct cxl_root_decoder * cxlrd)3226e099264SDan Williams static bool can_create_ram(struct cxl_root_decoder *cxlrd)
3236e099264SDan Williams {
3246e099264SDan Williams 	unsigned long flags = CXL_DECODER_F_TYPE3 | CXL_DECODER_F_RAM;
3256e099264SDan Williams 
3266e099264SDan Williams 	return (cxlrd->cxlsd.cxld.flags & flags) == flags;
3276e099264SDan Williams }
3286e099264SDan Williams 
cxl_root_decoder_visible(struct kobject * kobj,struct attribute * a,int n)329779dd20cSBen Widawsky static umode_t cxl_root_decoder_visible(struct kobject *kobj, struct attribute *a, int n)
330779dd20cSBen Widawsky {
331779dd20cSBen Widawsky 	struct device *dev = kobj_to_dev(kobj);
332779dd20cSBen Widawsky 	struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev);
333779dd20cSBen Widawsky 
334779dd20cSBen Widawsky 	if (a == CXL_REGION_ATTR(create_pmem_region) && !can_create_pmem(cxlrd))
335779dd20cSBen Widawsky 		return 0;
336779dd20cSBen Widawsky 
3376e099264SDan Williams 	if (a == CXL_REGION_ATTR(create_ram_region) && !can_create_ram(cxlrd))
3386e099264SDan Williams 		return 0;
3396e099264SDan Williams 
3406e099264SDan Williams 	if (a == CXL_REGION_ATTR(delete_region) &&
3416e099264SDan Williams 	    !(can_create_pmem(cxlrd) || can_create_ram(cxlrd)))
342779dd20cSBen Widawsky 		return 0;
343779dd20cSBen Widawsky 
344779dd20cSBen Widawsky 	return a->mode;
345779dd20cSBen Widawsky }
346779dd20cSBen Widawsky 
3470ff0af18SDan Williams static struct attribute_group cxl_decoder_root_attribute_group = {
3480ff0af18SDan Williams 	.attrs = cxl_decoder_root_attrs,
349779dd20cSBen Widawsky 	.is_visible = cxl_root_decoder_visible,
3500ff0af18SDan Williams };
3510ff0af18SDan Williams 
3520ff0af18SDan Williams static const struct attribute_group *cxl_decoder_root_attribute_groups[] = {
3530ff0af18SDan Williams 	&cxl_decoder_root_attribute_group,
3540ff0af18SDan Williams 	&cxl_decoder_base_attribute_group,
3550ff0af18SDan Williams 	&cxl_base_attribute_group,
3560ff0af18SDan Williams 	NULL,
3570ff0af18SDan Williams };
3580ff0af18SDan Williams 
3590ff0af18SDan Williams static struct attribute *cxl_decoder_switch_attrs[] = {
3600ff0af18SDan Williams 	&dev_attr_target_type.attr,
3618aea0ef1SDan Williams 	&dev_attr_target_list.attr,
3622bde6dbeSDan Williams 	SET_CXL_REGION_ATTR(region)
3630ff0af18SDan Williams 	NULL,
3640ff0af18SDan Williams };
3650ff0af18SDan Williams 
3660ff0af18SDan Williams static struct attribute_group cxl_decoder_switch_attribute_group = {
3670ff0af18SDan Williams 	.attrs = cxl_decoder_switch_attrs,
3680ff0af18SDan Williams };
3690ff0af18SDan Williams 
3700ff0af18SDan Williams static const struct attribute_group *cxl_decoder_switch_attribute_groups[] = {
3710ff0af18SDan Williams 	&cxl_decoder_switch_attribute_group,
3720ff0af18SDan Williams 	&cxl_decoder_base_attribute_group,
3730ff0af18SDan Williams 	&cxl_base_attribute_group,
3740ff0af18SDan Williams 	NULL,
3750ff0af18SDan Williams };
3760ff0af18SDan Williams 
3779b71e1c9SBen Widawsky static struct attribute *cxl_decoder_endpoint_attrs[] = {
3789b71e1c9SBen Widawsky 	&dev_attr_target_type.attr,
3792c866903SDan Williams 	&dev_attr_mode.attr,
380cf880423SDan Williams 	&dev_attr_dpa_size.attr,
381cf880423SDan Williams 	&dev_attr_dpa_resource.attr,
3822bde6dbeSDan Williams 	SET_CXL_REGION_ATTR(region)
3839b71e1c9SBen Widawsky 	NULL,
3849b71e1c9SBen Widawsky };
3859b71e1c9SBen Widawsky 
3869b71e1c9SBen Widawsky static struct attribute_group cxl_decoder_endpoint_attribute_group = {
3879b71e1c9SBen Widawsky 	.attrs = cxl_decoder_endpoint_attrs,
3889b71e1c9SBen Widawsky };
3899b71e1c9SBen Widawsky 
3909b71e1c9SBen Widawsky static const struct attribute_group *cxl_decoder_endpoint_attribute_groups[] = {
3919b71e1c9SBen Widawsky 	&cxl_decoder_base_attribute_group,
3929b71e1c9SBen Widawsky 	&cxl_decoder_endpoint_attribute_group,
3939b71e1c9SBen Widawsky 	&cxl_base_attribute_group,
3949b71e1c9SBen Widawsky 	NULL,
3959b71e1c9SBen Widawsky };
3969b71e1c9SBen Widawsky 
__cxl_decoder_release(struct cxl_decoder * cxld)397e636479eSDan Williams static void __cxl_decoder_release(struct cxl_decoder *cxld)
398e636479eSDan Williams {
399e636479eSDan Williams 	struct cxl_port *port = to_cxl_port(cxld->dev.parent);
400e636479eSDan Williams 
401e636479eSDan Williams 	ida_free(&port->decoder_ida, cxld->id);
402e636479eSDan Williams 	put_device(&port->dev);
403e636479eSDan Williams }
404e636479eSDan Williams 
cxl_endpoint_decoder_release(struct device * dev)4053bf65915SDan Williams static void cxl_endpoint_decoder_release(struct device *dev)
4060ff0af18SDan Williams {
4073bf65915SDan Williams 	struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(dev);
4080ff0af18SDan Williams 
4093bf65915SDan Williams 	__cxl_decoder_release(&cxled->cxld);
4103bf65915SDan Williams 	kfree(cxled);
411e636479eSDan Williams }
412e636479eSDan Williams 
cxl_switch_decoder_release(struct device * dev)413e636479eSDan Williams static void cxl_switch_decoder_release(struct device *dev)
414e636479eSDan Williams {
415e636479eSDan Williams 	struct cxl_switch_decoder *cxlsd = to_cxl_switch_decoder(dev);
416e636479eSDan Williams 
417e636479eSDan Williams 	__cxl_decoder_release(&cxlsd->cxld);
418e636479eSDan Williams 	kfree(cxlsd);
4190ff0af18SDan Williams }
4200ff0af18SDan Williams 
to_cxl_root_decoder(struct device * dev)4210f157c7fSDan Williams struct cxl_root_decoder *to_cxl_root_decoder(struct device *dev)
4220f157c7fSDan Williams {
4230f157c7fSDan Williams 	if (dev_WARN_ONCE(dev, !is_root_decoder(dev),
4240f157c7fSDan Williams 			  "not a cxl_root_decoder device\n"))
4250f157c7fSDan Williams 		return NULL;
4260f157c7fSDan Williams 	return container_of(dev, struct cxl_root_decoder, cxlsd.cxld.dev);
4270f157c7fSDan Williams }
4280f157c7fSDan Williams EXPORT_SYMBOL_NS_GPL(to_cxl_root_decoder, CXL);
4290f157c7fSDan Williams 
cxl_root_decoder_release(struct device * dev)4300f157c7fSDan Williams static void cxl_root_decoder_release(struct device *dev)
4310f157c7fSDan Williams {
4320f157c7fSDan Williams 	struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev);
4330f157c7fSDan Williams 
434779dd20cSBen Widawsky 	if (atomic_read(&cxlrd->region_id) >= 0)
435779dd20cSBen Widawsky 		memregion_free(atomic_read(&cxlrd->region_id));
4360f157c7fSDan Williams 	__cxl_decoder_release(&cxlrd->cxlsd.cxld);
4370f157c7fSDan Williams 	kfree(cxlrd);
4380f157c7fSDan Williams }
4390f157c7fSDan Williams 
4409b71e1c9SBen Widawsky static const struct device_type cxl_decoder_endpoint_type = {
4419b71e1c9SBen Widawsky 	.name = "cxl_decoder_endpoint",
4423bf65915SDan Williams 	.release = cxl_endpoint_decoder_release,
4439b71e1c9SBen Widawsky 	.groups = cxl_decoder_endpoint_attribute_groups,
4449b71e1c9SBen Widawsky };
4459b71e1c9SBen Widawsky 
4460ff0af18SDan Williams static const struct device_type cxl_decoder_switch_type = {
4470ff0af18SDan Williams 	.name = "cxl_decoder_switch",
448e636479eSDan Williams 	.release = cxl_switch_decoder_release,
4490ff0af18SDan Williams 	.groups = cxl_decoder_switch_attribute_groups,
4500ff0af18SDan Williams };
4510ff0af18SDan Williams 
4520ff0af18SDan Williams static const struct device_type cxl_decoder_root_type = {
4530ff0af18SDan Williams 	.name = "cxl_decoder_root",
4540f157c7fSDan Williams 	.release = cxl_root_decoder_release,
4550ff0af18SDan Williams 	.groups = cxl_decoder_root_attribute_groups,
4560ff0af18SDan Williams };
4570ff0af18SDan Williams 
is_endpoint_decoder(struct device * dev)4588ae3cebcSBen Widawsky bool is_endpoint_decoder(struct device *dev)
4599b71e1c9SBen Widawsky {
4609b71e1c9SBen Widawsky 	return dev->type == &cxl_decoder_endpoint_type;
4619b71e1c9SBen Widawsky }
462a32320b7SDan Williams EXPORT_SYMBOL_NS_GPL(is_endpoint_decoder, CXL);
4639b71e1c9SBen Widawsky 
is_root_decoder(struct device * dev)4640ff0af18SDan Williams bool is_root_decoder(struct device *dev)
4650ff0af18SDan Williams {
4660ff0af18SDan Williams 	return dev->type == &cxl_decoder_root_type;
4670ff0af18SDan Williams }
4680ff0af18SDan Williams EXPORT_SYMBOL_NS_GPL(is_root_decoder, CXL);
4690ff0af18SDan Williams 
is_switch_decoder(struct device * dev)470384e624bSDan Williams bool is_switch_decoder(struct device *dev)
471e636479eSDan Williams {
472e636479eSDan Williams 	return is_root_decoder(dev) || dev->type == &cxl_decoder_switch_type;
473e636479eSDan Williams }
4743d8f7ccaSDan Williams EXPORT_SYMBOL_NS_GPL(is_switch_decoder, CXL);
475e636479eSDan Williams 
to_cxl_decoder(struct device * dev)4760ff0af18SDan Williams struct cxl_decoder *to_cxl_decoder(struct device *dev)
4770ff0af18SDan Williams {
478e636479eSDan Williams 	if (dev_WARN_ONCE(dev,
479e636479eSDan Williams 			  !is_switch_decoder(dev) && !is_endpoint_decoder(dev),
4800ff0af18SDan Williams 			  "not a cxl_decoder device\n"))
4810ff0af18SDan Williams 		return NULL;
4820ff0af18SDan Williams 	return container_of(dev, struct cxl_decoder, dev);
4830ff0af18SDan Williams }
4840ff0af18SDan Williams EXPORT_SYMBOL_NS_GPL(to_cxl_decoder, CXL);
4850ff0af18SDan Williams 
to_cxl_endpoint_decoder(struct device * dev)4863bf65915SDan Williams struct cxl_endpoint_decoder *to_cxl_endpoint_decoder(struct device *dev)
4873bf65915SDan Williams {
4883bf65915SDan Williams 	if (dev_WARN_ONCE(dev, !is_endpoint_decoder(dev),
4893bf65915SDan Williams 			  "not a cxl_endpoint_decoder device\n"))
4903bf65915SDan Williams 		return NULL;
4913bf65915SDan Williams 	return container_of(dev, struct cxl_endpoint_decoder, cxld.dev);
4923bf65915SDan Williams }
4933bf65915SDan Williams EXPORT_SYMBOL_NS_GPL(to_cxl_endpoint_decoder, CXL);
4943bf65915SDan Williams 
to_cxl_switch_decoder(struct device * dev)49527b3f8d1SDan Williams struct cxl_switch_decoder *to_cxl_switch_decoder(struct device *dev)
496e636479eSDan Williams {
497e636479eSDan Williams 	if (dev_WARN_ONCE(dev, !is_switch_decoder(dev),
498e636479eSDan Williams 			  "not a cxl_switch_decoder device\n"))
499e636479eSDan Williams 		return NULL;
500e636479eSDan Williams 	return container_of(dev, struct cxl_switch_decoder, cxld.dev);
501e636479eSDan Williams }
5023d8f7ccaSDan Williams EXPORT_SYMBOL_NS_GPL(to_cxl_switch_decoder, CXL);
503e636479eSDan Williams 
cxl_ep_release(struct cxl_ep * ep)5042703c16cSDan Williams static void cxl_ep_release(struct cxl_ep *ep)
5052703c16cSDan Williams {
5062703c16cSDan Williams 	put_device(ep->ep);
5072703c16cSDan Williams 	kfree(ep);
5082703c16cSDan Williams }
5092703c16cSDan Williams 
cxl_ep_remove(struct cxl_port * port,struct cxl_ep * ep)510256d0e9eSDan Williams static void cxl_ep_remove(struct cxl_port *port, struct cxl_ep *ep)
511256d0e9eSDan Williams {
512256d0e9eSDan Williams 	if (!ep)
513256d0e9eSDan Williams 		return;
514256d0e9eSDan Williams 	xa_erase(&port->endpoints, (unsigned long) ep->ep);
515256d0e9eSDan Williams 	cxl_ep_release(ep);
516256d0e9eSDan Williams }
517256d0e9eSDan Williams 
cxl_port_release(struct device * dev)5180ff0af18SDan Williams static void cxl_port_release(struct device *dev)
5190ff0af18SDan Williams {
5200ff0af18SDan Williams 	struct cxl_port *port = to_cxl_port(dev);
521256d0e9eSDan Williams 	unsigned long index;
522256d0e9eSDan Williams 	struct cxl_ep *ep;
5230ff0af18SDan Williams 
524256d0e9eSDan Williams 	xa_for_each(&port->endpoints, index, ep)
525256d0e9eSDan Williams 		cxl_ep_remove(port, ep);
526256d0e9eSDan Williams 	xa_destroy(&port->endpoints);
52739178585SDan Williams 	xa_destroy(&port->dports);
528384e624bSDan Williams 	xa_destroy(&port->regions);
5290ff0af18SDan Williams 	ida_free(&cxl_port_ida, port->id);
5300ff0af18SDan Williams 	kfree(port);
5310ff0af18SDan Williams }
5320ff0af18SDan Williams 
5330ff0af18SDan Williams static const struct attribute_group *cxl_port_attribute_groups[] = {
5340ff0af18SDan Williams 	&cxl_base_attribute_group,
5350ff0af18SDan Williams 	NULL,
5360ff0af18SDan Williams };
5370ff0af18SDan Williams 
5380ff0af18SDan Williams static const struct device_type cxl_port_type = {
5390ff0af18SDan Williams 	.name = "cxl_port",
5400ff0af18SDan Williams 	.release = cxl_port_release,
5410ff0af18SDan Williams 	.groups = cxl_port_attribute_groups,
5420ff0af18SDan Williams };
5430ff0af18SDan Williams 
is_cxl_port(const struct device * dev)5442a81ada3SGreg Kroah-Hartman bool is_cxl_port(const struct device *dev)
5453c5b9039SDan Williams {
5463c5b9039SDan Williams 	return dev->type == &cxl_port_type;
5473c5b9039SDan Williams }
5483c5b9039SDan Williams EXPORT_SYMBOL_NS_GPL(is_cxl_port, CXL);
5493c5b9039SDan Williams 
to_cxl_port(const struct device * dev)5502a81ada3SGreg Kroah-Hartman struct cxl_port *to_cxl_port(const struct device *dev)
5510ff0af18SDan Williams {
5520ff0af18SDan Williams 	if (dev_WARN_ONCE(dev, dev->type != &cxl_port_type,
5530ff0af18SDan Williams 			  "not a cxl_port device\n"))
5540ff0af18SDan Williams 		return NULL;
5550ff0af18SDan Williams 	return container_of(dev, struct cxl_port, dev);
5560ff0af18SDan Williams }
5573c5b9039SDan Williams EXPORT_SYMBOL_NS_GPL(to_cxl_port, CXL);
5580ff0af18SDan Williams 
unregister_port(void * _port)5590ff0af18SDan Williams static void unregister_port(void *_port)
5600ff0af18SDan Williams {
5610ff0af18SDan Williams 	struct cxl_port *port = _port;
5625c3c067bSDan Williams 	struct cxl_port *parent;
5635c3c067bSDan Williams 	struct device *lock_dev;
5640ff0af18SDan Williams 
5655c3c067bSDan Williams 	if (is_cxl_root(port))
5665c3c067bSDan Williams 		parent = NULL;
5675c3c067bSDan Williams 	else
5685c3c067bSDan Williams 		parent = to_cxl_port(port->dev.parent);
5695c3c067bSDan Williams 
5705c3c067bSDan Williams 	/*
5715c3c067bSDan Williams 	 * CXL root port's and the first level of ports are unregistered
5725c3c067bSDan Williams 	 * under the platform firmware device lock, all other ports are
5735c3c067bSDan Williams 	 * unregistered while holding their parent port lock.
5745c3c067bSDan Williams 	 */
5755c3c067bSDan Williams 	if (!parent)
5767481653dSDan Williams 		lock_dev = port->uport_dev;
5775c3c067bSDan Williams 	else if (is_cxl_root(parent))
5787481653dSDan Williams 		lock_dev = parent->uport_dev;
5795c3c067bSDan Williams 	else
5805c3c067bSDan Williams 		lock_dev = &parent->dev;
5815c3c067bSDan Williams 
5825c3c067bSDan Williams 	device_lock_assert(lock_dev);
583fe80f1adSDan Williams 	port->dead = true;
5840ff0af18SDan Williams 	device_unregister(&port->dev);
5850ff0af18SDan Williams }
5860ff0af18SDan Williams 
cxl_unlink_uport(void * _port)5870ff0af18SDan Williams static void cxl_unlink_uport(void *_port)
5880ff0af18SDan Williams {
5890ff0af18SDan Williams 	struct cxl_port *port = _port;
5900ff0af18SDan Williams 
5910ff0af18SDan Williams 	sysfs_remove_link(&port->dev.kobj, "uport");
5920ff0af18SDan Williams }
5930ff0af18SDan Williams 
devm_cxl_link_uport(struct device * host,struct cxl_port * port)5940ff0af18SDan Williams static int devm_cxl_link_uport(struct device *host, struct cxl_port *port)
5950ff0af18SDan Williams {
5960ff0af18SDan Williams 	int rc;
5970ff0af18SDan Williams 
5987481653dSDan Williams 	rc = sysfs_create_link(&port->dev.kobj, &port->uport_dev->kobj,
5997481653dSDan Williams 			       "uport");
6000ff0af18SDan Williams 	if (rc)
6010ff0af18SDan Williams 		return rc;
6020ff0af18SDan Williams 	return devm_add_action_or_reset(host, cxl_unlink_uport, port);
6030ff0af18SDan Williams }
6040ff0af18SDan Williams 
cxl_unlink_parent_dport(void * _port)605172738bbSDan Williams static void cxl_unlink_parent_dport(void *_port)
606172738bbSDan Williams {
607172738bbSDan Williams 	struct cxl_port *port = _port;
608172738bbSDan Williams 
609172738bbSDan Williams 	sysfs_remove_link(&port->dev.kobj, "parent_dport");
610172738bbSDan Williams }
611172738bbSDan Williams 
devm_cxl_link_parent_dport(struct device * host,struct cxl_port * port,struct cxl_dport * parent_dport)612172738bbSDan Williams static int devm_cxl_link_parent_dport(struct device *host,
613172738bbSDan Williams 				      struct cxl_port *port,
614172738bbSDan Williams 				      struct cxl_dport *parent_dport)
615172738bbSDan Williams {
616172738bbSDan Williams 	int rc;
617172738bbSDan Williams 
618172738bbSDan Williams 	if (!parent_dport)
619172738bbSDan Williams 		return 0;
620172738bbSDan Williams 
621227db574SRobert Richter 	rc = sysfs_create_link(&port->dev.kobj, &parent_dport->dport_dev->kobj,
622172738bbSDan Williams 			       "parent_dport");
623172738bbSDan Williams 	if (rc)
624172738bbSDan Williams 		return rc;
625172738bbSDan Williams 	return devm_add_action_or_reset(host, cxl_unlink_parent_dport, port);
626172738bbSDan Williams }
627172738bbSDan Williams 
6283750d013SDan Williams static struct lock_class_key cxl_port_key;
6293750d013SDan Williams 
cxl_port_alloc(struct device * uport_dev,resource_size_t component_reg_phys,struct cxl_dport * parent_dport)6307481653dSDan Williams static struct cxl_port *cxl_port_alloc(struct device *uport_dev,
6310ff0af18SDan Williams 				       resource_size_t component_reg_phys,
6321b58b4caSDan Williams 				       struct cxl_dport *parent_dport)
6330ff0af18SDan Williams {
6340ff0af18SDan Williams 	struct cxl_port *port;
6350ff0af18SDan Williams 	struct device *dev;
6360ff0af18SDan Williams 	int rc;
6370ff0af18SDan Williams 
6380ff0af18SDan Williams 	port = kzalloc(sizeof(*port), GFP_KERNEL);
6390ff0af18SDan Williams 	if (!port)
6400ff0af18SDan Williams 		return ERR_PTR(-ENOMEM);
6410ff0af18SDan Williams 
6420ff0af18SDan Williams 	rc = ida_alloc(&cxl_port_ida, GFP_KERNEL);
6430ff0af18SDan Williams 	if (rc < 0)
6440ff0af18SDan Williams 		goto err;
6450ff0af18SDan Williams 	port->id = rc;
6467481653dSDan Williams 	port->uport_dev = uport_dev;
6470ff0af18SDan Williams 
6480ff0af18SDan Williams 	/*
6490ff0af18SDan Williams 	 * The top-level cxl_port "cxl_root" does not have a cxl_port as
6500ff0af18SDan Williams 	 * its parent and it does not have any corresponding component
6510ff0af18SDan Williams 	 * registers as its decode is described by a fixed platform
6520ff0af18SDan Williams 	 * description.
6530ff0af18SDan Williams 	 */
6540ff0af18SDan Williams 	dev = &port->dev;
6551b58b4caSDan Williams 	if (parent_dport) {
6561b58b4caSDan Williams 		struct cxl_port *parent_port = parent_dport->port;
657ee800010SDan Williams 		struct cxl_port *iter;
658ee800010SDan Williams 
6590ff0af18SDan Williams 		dev->parent = &parent_port->dev;
6603750d013SDan Williams 		port->depth = parent_port->depth + 1;
6611b58b4caSDan Williams 		port->parent_dport = parent_dport;
662ee800010SDan Williams 
663ee800010SDan Williams 		/*
664ee800010SDan Williams 		 * walk to the host bridge, or the first ancestor that knows
665ee800010SDan Williams 		 * the host bridge
666ee800010SDan Williams 		 */
667ee800010SDan Williams 		iter = port;
668ee800010SDan Williams 		while (!iter->host_bridge &&
669ee800010SDan Williams 		       !is_cxl_root(to_cxl_port(iter->dev.parent)))
670ee800010SDan Williams 			iter = to_cxl_port(iter->dev.parent);
671ee800010SDan Williams 		if (iter->host_bridge)
672ee800010SDan Williams 			port->host_bridge = iter->host_bridge;
673d5b1a271SRobert Richter 		else if (parent_dport->rch)
674227db574SRobert Richter 			port->host_bridge = parent_dport->dport_dev;
675ee800010SDan Williams 		else
6767481653dSDan Williams 			port->host_bridge = iter->uport_dev;
6777481653dSDan Williams 		dev_dbg(uport_dev, "host-bridge: %s\n",
6787481653dSDan Williams 			dev_name(port->host_bridge));
6793750d013SDan Williams 	} else
6807481653dSDan Williams 		dev->parent = uport_dev;
6810ff0af18SDan Williams 
6820ff0af18SDan Williams 	port->component_reg_phys = component_reg_phys;
6830ff0af18SDan Williams 	ida_init(&port->decoder_ida);
6840c33b393SDan Williams 	port->hdm_end = -1;
685176baefbSDan Williams 	port->commit_end = -1;
68639178585SDan Williams 	xa_init(&port->dports);
687256d0e9eSDan Williams 	xa_init(&port->endpoints);
688384e624bSDan Williams 	xa_init(&port->regions);
6890ff0af18SDan Williams 
6900ff0af18SDan Williams 	device_initialize(dev);
6913750d013SDan Williams 	lockdep_set_class_and_subclass(&dev->mutex, &cxl_port_key, port->depth);
6920ff0af18SDan Williams 	device_set_pm_not_required(dev);
6930ff0af18SDan Williams 	dev->bus = &cxl_bus_type;
6940ff0af18SDan Williams 	dev->type = &cxl_port_type;
6950ff0af18SDan Williams 
6960ff0af18SDan Williams 	return port;
6970ff0af18SDan Williams 
6980ff0af18SDan Williams err:
6990ff0af18SDan Williams 	kfree(port);
7000ff0af18SDan Williams 	return ERR_PTR(rc);
7010ff0af18SDan Williams }
7020ff0af18SDan Williams 
cxl_setup_comp_regs(struct device * host,struct cxl_register_map * map,resource_size_t component_reg_phys)7030fc37ec1SRobert Richter static int cxl_setup_comp_regs(struct device *host, struct cxl_register_map *map,
70419ab69a6SRobert Richter 			       resource_size_t component_reg_phys)
70519ab69a6SRobert Richter {
70619ab69a6SRobert Richter 	if (component_reg_phys == CXL_RESOURCE_NONE)
70719ab69a6SRobert Richter 		return 0;
70819ab69a6SRobert Richter 
70919ab69a6SRobert Richter 	*map = (struct cxl_register_map) {
7100fc37ec1SRobert Richter 		.host = host,
71119ab69a6SRobert Richter 		.reg_type = CXL_REGLOC_RBI_COMPONENT,
71219ab69a6SRobert Richter 		.resource = component_reg_phys,
71319ab69a6SRobert Richter 		.max_size = CXL_COMPONENT_REG_BLOCK_SIZE,
71419ab69a6SRobert Richter 	};
71519ab69a6SRobert Richter 
71619ab69a6SRobert Richter 	return cxl_setup_regs(map);
71719ab69a6SRobert Richter }
71819ab69a6SRobert Richter 
cxl_port_setup_regs(struct cxl_port * port,resource_size_t component_reg_phys)719a76b6251SDan Williams static int cxl_port_setup_regs(struct cxl_port *port,
72019ab69a6SRobert Richter 			resource_size_t component_reg_phys)
72119ab69a6SRobert Richter {
722a76b6251SDan Williams 	if (dev_is_platform(port->uport_dev))
723a76b6251SDan Williams 		return 0;
72419ab69a6SRobert Richter 	return cxl_setup_comp_regs(&port->dev, &port->comp_map,
72519ab69a6SRobert Richter 				   component_reg_phys);
72619ab69a6SRobert Richter }
72719ab69a6SRobert Richter 
cxl_dport_setup_regs(struct device * host,struct cxl_dport * dport,resource_size_t component_reg_phys)7288b527963SDan Williams static int cxl_dport_setup_regs(struct device *host, struct cxl_dport *dport,
7295d2ffbe4SRobert Richter 				resource_size_t component_reg_phys)
7305d2ffbe4SRobert Richter {
7318b527963SDan Williams 	int rc;
7328b527963SDan Williams 
733a76b6251SDan Williams 	if (dev_is_platform(dport->dport_dev))
734a76b6251SDan Williams 		return 0;
7358b527963SDan Williams 
7368b527963SDan Williams 	/*
7378b527963SDan Williams 	 * use @dport->dport_dev for the context for error messages during
7388b527963SDan Williams 	 * register probing, and fixup @host after the fact, since @host may be
7398b527963SDan Williams 	 * NULL.
7408b527963SDan Williams 	 */
7418b527963SDan Williams 	rc = cxl_setup_comp_regs(dport->dport_dev, &dport->comp_map,
7425d2ffbe4SRobert Richter 				 component_reg_phys);
7438b527963SDan Williams 	dport->comp_map.host = host;
7448b527963SDan Williams 	return rc;
7455d2ffbe4SRobert Richter }
7465d2ffbe4SRobert Richter 
__devm_cxl_add_port(struct device * host,struct device * uport_dev,resource_size_t component_reg_phys,struct cxl_dport * parent_dport)747f3cd264cSRobert Richter static struct cxl_port *__devm_cxl_add_port(struct device *host,
7487481653dSDan Williams 					    struct device *uport_dev,
7490ff0af18SDan Williams 					    resource_size_t component_reg_phys,
7501b58b4caSDan Williams 					    struct cxl_dport *parent_dport)
7510ff0af18SDan Williams {
7520ff0af18SDan Williams 	struct cxl_port *port;
7530ff0af18SDan Williams 	struct device *dev;
7540ff0af18SDan Williams 	int rc;
7550ff0af18SDan Williams 
7567481653dSDan Williams 	port = cxl_port_alloc(uport_dev, component_reg_phys, parent_dport);
7570ff0af18SDan Williams 	if (IS_ERR(port))
7580ff0af18SDan Williams 		return port;
7590ff0af18SDan Williams 
7600ff0af18SDan Williams 	dev = &port->dev;
7617481653dSDan Williams 	if (is_cxl_memdev(uport_dev))
7628dd2bc0fSBen Widawsky 		rc = dev_set_name(dev, "endpoint%d", port->id);
7631b58b4caSDan Williams 	else if (parent_dport)
7640ff0af18SDan Williams 		rc = dev_set_name(dev, "port%d", port->id);
7650ff0af18SDan Williams 	else
7660ff0af18SDan Williams 		rc = dev_set_name(dev, "root%d", port->id);
7670ff0af18SDan Williams 	if (rc)
7680ff0af18SDan Williams 		goto err;
7690ff0af18SDan Williams 
77019ab69a6SRobert Richter 	rc = cxl_port_setup_regs(port, component_reg_phys);
77119ab69a6SRobert Richter 	if (rc)
77219ab69a6SRobert Richter 		goto err;
77319ab69a6SRobert Richter 
7740ff0af18SDan Williams 	rc = device_add(dev);
7750ff0af18SDan Williams 	if (rc)
7760ff0af18SDan Williams 		goto err;
7770ff0af18SDan Williams 
7780ff0af18SDan Williams 	rc = devm_add_action_or_reset(host, unregister_port, port);
7790ff0af18SDan Williams 	if (rc)
7800ff0af18SDan Williams 		return ERR_PTR(rc);
7810ff0af18SDan Williams 
7820ff0af18SDan Williams 	rc = devm_cxl_link_uport(host, port);
7830ff0af18SDan Williams 	if (rc)
7840ff0af18SDan Williams 		return ERR_PTR(rc);
7850ff0af18SDan Williams 
786172738bbSDan Williams 	rc = devm_cxl_link_parent_dport(host, port, parent_dport);
787172738bbSDan Williams 	if (rc)
788172738bbSDan Williams 		return ERR_PTR(rc);
789172738bbSDan Williams 
7900ff0af18SDan Williams 	return port;
7910ff0af18SDan Williams 
7920ff0af18SDan Williams err:
7930ff0af18SDan Williams 	put_device(dev);
7940ff0af18SDan Williams 	return ERR_PTR(rc);
7950ff0af18SDan Williams }
796f3cd264cSRobert Richter 
797f3cd264cSRobert Richter /**
798f3cd264cSRobert Richter  * devm_cxl_add_port - register a cxl_port in CXL memory decode hierarchy
799f3cd264cSRobert Richter  * @host: host device for devm operations
8007481653dSDan Williams  * @uport_dev: "physical" device implementing this upstream port
801f3cd264cSRobert Richter  * @component_reg_phys: (optional) for configurable cxl_port instances
802f3cd264cSRobert Richter  * @parent_dport: next hop up in the CXL memory decode hierarchy
803f3cd264cSRobert Richter  */
devm_cxl_add_port(struct device * host,struct device * uport_dev,resource_size_t component_reg_phys,struct cxl_dport * parent_dport)8047481653dSDan Williams struct cxl_port *devm_cxl_add_port(struct device *host,
8057481653dSDan Williams 				   struct device *uport_dev,
806f3cd264cSRobert Richter 				   resource_size_t component_reg_phys,
807f3cd264cSRobert Richter 				   struct cxl_dport *parent_dport)
808f3cd264cSRobert Richter {
809f3cd264cSRobert Richter 	struct cxl_port *port, *parent_port;
810f3cd264cSRobert Richter 
8117481653dSDan Williams 	port = __devm_cxl_add_port(host, uport_dev, component_reg_phys,
812f3cd264cSRobert Richter 				   parent_dport);
813f3cd264cSRobert Richter 
814f3cd264cSRobert Richter 	parent_port = parent_dport ? parent_dport->port : NULL;
815f3cd264cSRobert Richter 	if (IS_ERR(port)) {
8167481653dSDan Williams 		dev_dbg(uport_dev, "Failed to add%s%s%s: %ld\n",
817a70fc4edSRobert Richter 			parent_port ? " port to " : "",
818f3cd264cSRobert Richter 			parent_port ? dev_name(&parent_port->dev) : "",
819a70fc4edSRobert Richter 			parent_port ? "" : " root port",
820f3cd264cSRobert Richter 			PTR_ERR(port));
821f3cd264cSRobert Richter 	} else {
8227481653dSDan Williams 		dev_dbg(uport_dev, "%s added%s%s%s\n",
823f3cd264cSRobert Richter 			dev_name(&port->dev),
824f3cd264cSRobert Richter 			parent_port ? " to " : "",
825f3cd264cSRobert Richter 			parent_port ? dev_name(&parent_port->dev) : "",
826f3cd264cSRobert Richter 			parent_port ? "" : " (root port)");
827f3cd264cSRobert Richter 	}
828f3cd264cSRobert Richter 
829f3cd264cSRobert Richter 	return port;
830f3cd264cSRobert Richter }
8310ff0af18SDan Williams EXPORT_SYMBOL_NS_GPL(devm_cxl_add_port, CXL);
8320ff0af18SDan Williams 
cxl_port_to_pci_bus(struct cxl_port * port)8335ff7316fSDan Williams struct pci_bus *cxl_port_to_pci_bus(struct cxl_port *port)
8345ff7316fSDan Williams {
8355ff7316fSDan Williams 	/* There is no pci_bus associated with a CXL platform-root port */
8365ff7316fSDan Williams 	if (is_cxl_root(port))
8375ff7316fSDan Williams 		return NULL;
8385ff7316fSDan Williams 
8397481653dSDan Williams 	if (dev_is_pci(port->uport_dev)) {
8407481653dSDan Williams 		struct pci_dev *pdev = to_pci_dev(port->uport_dev);
8415ff7316fSDan Williams 
8425ff7316fSDan Williams 		return pdev->subordinate;
8435ff7316fSDan Williams 	}
8445ff7316fSDan Williams 
8457481653dSDan Williams 	return xa_load(&cxl_root_buses, (unsigned long)port->uport_dev);
8465ff7316fSDan Williams }
8475ff7316fSDan Williams EXPORT_SYMBOL_NS_GPL(cxl_port_to_pci_bus, CXL);
8485ff7316fSDan Williams 
unregister_pci_bus(void * uport_dev)8497481653dSDan Williams static void unregister_pci_bus(void *uport_dev)
8505ff7316fSDan Williams {
8517481653dSDan Williams 	xa_erase(&cxl_root_buses, (unsigned long)uport_dev);
8525ff7316fSDan Williams }
8535ff7316fSDan Williams 
devm_cxl_register_pci_bus(struct device * host,struct device * uport_dev,struct pci_bus * bus)8547481653dSDan Williams int devm_cxl_register_pci_bus(struct device *host, struct device *uport_dev,
8555ff7316fSDan Williams 			      struct pci_bus *bus)
8565ff7316fSDan Williams {
8575ff7316fSDan Williams 	int rc;
8585ff7316fSDan Williams 
8597481653dSDan Williams 	if (dev_is_pci(uport_dev))
8605ff7316fSDan Williams 		return -EINVAL;
8615ff7316fSDan Williams 
8627481653dSDan Williams 	rc = xa_insert(&cxl_root_buses, (unsigned long)uport_dev, bus,
8637481653dSDan Williams 		       GFP_KERNEL);
8645ff7316fSDan Williams 	if (rc)
8655ff7316fSDan Williams 		return rc;
8667481653dSDan Williams 	return devm_add_action_or_reset(host, unregister_pci_bus, uport_dev);
8675ff7316fSDan Williams }
8685ff7316fSDan Williams EXPORT_SYMBOL_NS_GPL(devm_cxl_register_pci_bus, CXL);
8695ff7316fSDan Williams 
dev_is_cxl_root_child(struct device * dev)8702703c16cSDan Williams static bool dev_is_cxl_root_child(struct device *dev)
8712703c16cSDan Williams {
8722703c16cSDan Williams 	struct cxl_port *port, *parent;
8732703c16cSDan Williams 
8742703c16cSDan Williams 	if (!is_cxl_port(dev))
8752703c16cSDan Williams 		return false;
8762703c16cSDan Williams 
8772703c16cSDan Williams 	port = to_cxl_port(dev);
8782703c16cSDan Williams 	if (is_cxl_root(port))
8792703c16cSDan Williams 		return false;
8802703c16cSDan Williams 
8812703c16cSDan Williams 	parent = to_cxl_port(port->dev.parent);
8822703c16cSDan Williams 	if (is_cxl_root(parent))
8832703c16cSDan Williams 		return true;
8842703c16cSDan Williams 
8852703c16cSDan Williams 	return false;
8862703c16cSDan Williams }
8872703c16cSDan Williams 
find_cxl_root(struct cxl_port * port)888d35b495dSDan Williams struct cxl_port *find_cxl_root(struct cxl_port *port)
889a46cfc0fSDan Williams {
890d35b495dSDan Williams 	struct cxl_port *iter = port;
891a46cfc0fSDan Williams 
892d35b495dSDan Williams 	while (iter && !is_cxl_root(iter))
893d35b495dSDan Williams 		iter = to_cxl_port(iter->dev.parent);
894a46cfc0fSDan Williams 
895d35b495dSDan Williams 	if (!iter)
896a46cfc0fSDan Williams 		return NULL;
897d35b495dSDan Williams 	get_device(&iter->dev);
898d35b495dSDan Williams 	return iter;
899a46cfc0fSDan Williams }
900a46cfc0fSDan Williams EXPORT_SYMBOL_NS_GPL(find_cxl_root, CXL);
901a46cfc0fSDan Williams 
find_dport(struct cxl_port * port,int id)9020ff0af18SDan Williams static struct cxl_dport *find_dport(struct cxl_port *port, int id)
9030ff0af18SDan Williams {
9040ff0af18SDan Williams 	struct cxl_dport *dport;
90539178585SDan Williams 	unsigned long index;
9060ff0af18SDan Williams 
9070ff0af18SDan Williams 	device_lock_assert(&port->dev);
90839178585SDan Williams 	xa_for_each(&port->dports, index, dport)
9090ff0af18SDan Williams 		if (dport->port_id == id)
9100ff0af18SDan Williams 			return dport;
9110ff0af18SDan Williams 	return NULL;
9120ff0af18SDan Williams }
9130ff0af18SDan Williams 
add_dport(struct cxl_port * port,struct cxl_dport * dport)914227db574SRobert Richter static int add_dport(struct cxl_port *port, struct cxl_dport *dport)
9150ff0af18SDan Williams {
9160ff0af18SDan Williams 	struct cxl_dport *dup;
917e4f6dfa9SDan Williams 	int rc;
9180ff0af18SDan Williams 
919c978f1b1SDan Williams 	device_lock_assert(&port->dev);
920227db574SRobert Richter 	dup = find_dport(port, dport->port_id);
92139178585SDan Williams 	if (dup) {
9220ff0af18SDan Williams 		dev_err(&port->dev,
9230ff0af18SDan Williams 			"unable to add dport%d-%s non-unique port id (%s)\n",
924227db574SRobert Richter 			dport->port_id, dev_name(dport->dport_dev),
925227db574SRobert Richter 			dev_name(dup->dport_dev));
92639178585SDan Williams 		return -EBUSY;
92739178585SDan Williams 	}
928e4f6dfa9SDan Williams 
929227db574SRobert Richter 	rc = xa_insert(&port->dports, (unsigned long)dport->dport_dev, dport,
93039178585SDan Williams 		       GFP_KERNEL);
931e4f6dfa9SDan Williams 	if (rc)
932e4f6dfa9SDan Williams 		return rc;
933e4f6dfa9SDan Williams 
934e4f6dfa9SDan Williams 	port->nr_dports++;
935e4f6dfa9SDan Williams 	return 0;
9360ff0af18SDan Williams }
9370ff0af18SDan Williams 
93854cdbf84SBen Widawsky /*
93954cdbf84SBen Widawsky  * Since root-level CXL dports cannot be enumerated by PCI they are not
94054cdbf84SBen Widawsky  * enumerated by the common port driver that acquires the port lock over
94154cdbf84SBen Widawsky  * dport add/remove. Instead, root dports are manually added by a
94254cdbf84SBen Widawsky  * platform driver and cond_cxl_root_lock() is used to take the missing
94354cdbf84SBen Widawsky  * port lock in that case.
94454cdbf84SBen Widawsky  */
cond_cxl_root_lock(struct cxl_port * port)94554cdbf84SBen Widawsky static void cond_cxl_root_lock(struct cxl_port *port)
94654cdbf84SBen Widawsky {
94754cdbf84SBen Widawsky 	if (is_cxl_root(port))
94838a34e10SDan Williams 		device_lock(&port->dev);
94954cdbf84SBen Widawsky }
95054cdbf84SBen Widawsky 
cond_cxl_root_unlock(struct cxl_port * port)95154cdbf84SBen Widawsky static void cond_cxl_root_unlock(struct cxl_port *port)
95254cdbf84SBen Widawsky {
95354cdbf84SBen Widawsky 	if (is_cxl_root(port))
95438a34e10SDan Williams 		device_unlock(&port->dev);
95554cdbf84SBen Widawsky }
95654cdbf84SBen Widawsky 
cxl_dport_remove(void * data)95798d2d3a2SDan Williams static void cxl_dport_remove(void *data)
95898d2d3a2SDan Williams {
95998d2d3a2SDan Williams 	struct cxl_dport *dport = data;
96098d2d3a2SDan Williams 	struct cxl_port *port = dport->port;
96198d2d3a2SDan Williams 
962227db574SRobert Richter 	xa_erase(&port->dports, (unsigned long) dport->dport_dev);
963227db574SRobert Richter 	put_device(dport->dport_dev);
96498d2d3a2SDan Williams }
96598d2d3a2SDan Williams 
cxl_dport_unlink(void * data)96698d2d3a2SDan Williams static void cxl_dport_unlink(void *data)
96798d2d3a2SDan Williams {
96898d2d3a2SDan Williams 	struct cxl_dport *dport = data;
96998d2d3a2SDan Williams 	struct cxl_port *port = dport->port;
97098d2d3a2SDan Williams 	char link_name[CXL_TARGET_STRLEN];
97198d2d3a2SDan Williams 
97298d2d3a2SDan Williams 	sprintf(link_name, "dport%d", dport->port_id);
97398d2d3a2SDan Williams 	sysfs_remove_link(&port->dev.kobj, link_name);
97498d2d3a2SDan Williams }
97598d2d3a2SDan Williams 
976d5b1a271SRobert Richter static struct cxl_dport *
__devm_cxl_add_dport(struct cxl_port * port,struct device * dport_dev,int port_id,resource_size_t component_reg_phys,resource_size_t rcrb)977d5b1a271SRobert Richter __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
978d5b1a271SRobert Richter 		     int port_id, resource_size_t component_reg_phys,
979d5b1a271SRobert Richter 		     resource_size_t rcrb)
9800ff0af18SDan Williams {
9810ff0af18SDan Williams 	char link_name[CXL_TARGET_STRLEN];
9820ff0af18SDan Williams 	struct cxl_dport *dport;
983664bf115SDan Williams 	struct device *host;
9840ff0af18SDan Williams 	int rc;
9850ff0af18SDan Williams 
986664bf115SDan Williams 	if (is_cxl_root(port))
9877481653dSDan Williams 		host = port->uport_dev;
988664bf115SDan Williams 	else
989664bf115SDan Williams 		host = &port->dev;
990664bf115SDan Williams 
99198d2d3a2SDan Williams 	if (!host->driver) {
99298d2d3a2SDan Williams 		dev_WARN_ONCE(&port->dev, 1, "dport:%s bad devm context\n",
99398d2d3a2SDan Williams 			      dev_name(dport_dev));
99498d2d3a2SDan Williams 		return ERR_PTR(-ENXIO);
99598d2d3a2SDan Williams 	}
99698d2d3a2SDan Williams 
9970ff0af18SDan Williams 	if (snprintf(link_name, CXL_TARGET_STRLEN, "dport%d", port_id) >=
9980ff0af18SDan Williams 	    CXL_TARGET_STRLEN)
99998d2d3a2SDan Williams 		return ERR_PTR(-EINVAL);
10000ff0af18SDan Williams 
100198d2d3a2SDan Williams 	dport = devm_kzalloc(host, sizeof(*dport), GFP_KERNEL);
10020ff0af18SDan Williams 	if (!dport)
100398d2d3a2SDan Williams 		return ERR_PTR(-ENOMEM);
10040ff0af18SDan Williams 
10058b527963SDan Williams 	dport->dport_dev = dport_dev;
10068b527963SDan Williams 	dport->port_id = port_id;
10078b527963SDan Williams 	dport->port = port;
10088b527963SDan Williams 
10098b527963SDan Williams 	if (rcrb == CXL_RESOURCE_NONE) {
10108b527963SDan Williams 		rc = cxl_dport_setup_regs(&port->dev, dport,
10118b527963SDan Williams 					  component_reg_phys);
10128b527963SDan Williams 		if (rc)
10138b527963SDan Williams 			return ERR_PTR(rc);
10148b527963SDan Williams 	} else {
101506193378SDan Williams 		dport->rcrb.base = rcrb;
101606193378SDan Williams 		component_reg_phys = __rcrb_to_component(dport_dev, &dport->rcrb,
1017eb4663b0SRobert Richter 							 CXL_RCRB_DOWNSTREAM);
1018eb4663b0SRobert Richter 		if (component_reg_phys == CXL_RESOURCE_NONE) {
1019eb4663b0SRobert Richter 			dev_warn(dport_dev, "Invalid Component Registers in RCRB");
1020eb4663b0SRobert Richter 			return ERR_PTR(-ENXIO);
1021eb4663b0SRobert Richter 		}
1022eb4663b0SRobert Richter 
10238b527963SDan Williams 		/*
10248b527963SDan Williams 		 * RCH @dport is not ready to map until associated with its
10258b527963SDan Williams 		 * memdev
10268b527963SDan Williams 		 */
10278b527963SDan Williams 		rc = cxl_dport_setup_regs(NULL, dport, component_reg_phys);
10288b527963SDan Williams 		if (rc)
10298b527963SDan Williams 			return ERR_PTR(rc);
10308b527963SDan Williams 
1031d5b1a271SRobert Richter 		dport->rch = true;
1032eb4663b0SRobert Richter 	}
1033eb4663b0SRobert Richter 
1034eb4663b0SRobert Richter 	if (component_reg_phys != CXL_RESOURCE_NONE)
1035eb4663b0SRobert Richter 		dev_dbg(dport_dev, "Component Registers found for dport: %pa\n",
1036eb4663b0SRobert Richter 			&component_reg_phys);
1037eb4663b0SRobert Richter 
103854cdbf84SBen Widawsky 	cond_cxl_root_lock(port);
10390ff0af18SDan Williams 	rc = add_dport(port, dport);
104054cdbf84SBen Widawsky 	cond_cxl_root_unlock(port);
10410ff0af18SDan Williams 	if (rc)
104298d2d3a2SDan Williams 		return ERR_PTR(rc);
104398d2d3a2SDan Williams 
104498d2d3a2SDan Williams 	get_device(dport_dev);
104598d2d3a2SDan Williams 	rc = devm_add_action_or_reset(host, cxl_dport_remove, dport);
104698d2d3a2SDan Williams 	if (rc)
104798d2d3a2SDan Williams 		return ERR_PTR(rc);
10480ff0af18SDan Williams 
10490ff0af18SDan Williams 	rc = sysfs_create_link(&port->dev.kobj, &dport_dev->kobj, link_name);
10500ff0af18SDan Williams 	if (rc)
105198d2d3a2SDan Williams 		return ERR_PTR(rc);
10520ff0af18SDan Williams 
105398d2d3a2SDan Williams 	rc = devm_add_action_or_reset(host, cxl_dport_unlink, dport);
105498d2d3a2SDan Williams 	if (rc)
105598d2d3a2SDan Williams 		return ERR_PTR(rc);
105698d2d3a2SDan Williams 
105798d2d3a2SDan Williams 	return dport;
10580ff0af18SDan Williams }
105958eef878SRobert Richter 
106058eef878SRobert Richter /**
1061d5b1a271SRobert Richter  * devm_cxl_add_dport - append VH downstream port data to a cxl_port
106258eef878SRobert Richter  * @port: the cxl_port that references this dport
106358eef878SRobert Richter  * @dport_dev: firmware or PCI device representing the dport
106458eef878SRobert Richter  * @port_id: identifier for this dport in a decoder's target list
106558eef878SRobert Richter  * @component_reg_phys: optional location of CXL component registers
106658eef878SRobert Richter  *
106758eef878SRobert Richter  * Note that dports are appended to the devm release action's of the
106858eef878SRobert Richter  * either the port's host (for root ports), or the port itself (for
106958eef878SRobert Richter  * switch ports)
107058eef878SRobert Richter  */
devm_cxl_add_dport(struct cxl_port * port,struct device * dport_dev,int port_id,resource_size_t component_reg_phys)107158eef878SRobert Richter struct cxl_dport *devm_cxl_add_dport(struct cxl_port *port,
107258eef878SRobert Richter 				     struct device *dport_dev, int port_id,
107358eef878SRobert Richter 				     resource_size_t component_reg_phys)
107458eef878SRobert Richter {
107558eef878SRobert Richter 	struct cxl_dport *dport;
107658eef878SRobert Richter 
107758eef878SRobert Richter 	dport = __devm_cxl_add_dport(port, dport_dev, port_id,
1078d5b1a271SRobert Richter 				     component_reg_phys, CXL_RESOURCE_NONE);
107958eef878SRobert Richter 	if (IS_ERR(dport)) {
108058eef878SRobert Richter 		dev_dbg(dport_dev, "failed to add dport to %s: %ld\n",
108158eef878SRobert Richter 			dev_name(&port->dev), PTR_ERR(dport));
108258eef878SRobert Richter 	} else {
108358eef878SRobert Richter 		dev_dbg(dport_dev, "dport added to %s\n",
108458eef878SRobert Richter 			dev_name(&port->dev));
108558eef878SRobert Richter 	}
108658eef878SRobert Richter 
108758eef878SRobert Richter 	return dport;
108858eef878SRobert Richter }
108998d2d3a2SDan Williams EXPORT_SYMBOL_NS_GPL(devm_cxl_add_dport, CXL);
10900ff0af18SDan Williams 
1091d5b1a271SRobert Richter /**
1092d5b1a271SRobert Richter  * devm_cxl_add_rch_dport - append RCH downstream port data to a cxl_port
1093d5b1a271SRobert Richter  * @port: the cxl_port that references this dport
1094d5b1a271SRobert Richter  * @dport_dev: firmware or PCI device representing the dport
1095d5b1a271SRobert Richter  * @port_id: identifier for this dport in a decoder's target list
1096d5b1a271SRobert Richter  * @rcrb: mandatory location of a Root Complex Register Block
1097d5b1a271SRobert Richter  *
1098d5b1a271SRobert Richter  * See CXL 3.0 9.11.8 CXL Devices Attached to an RCH
1099d5b1a271SRobert Richter  */
devm_cxl_add_rch_dport(struct cxl_port * port,struct device * dport_dev,int port_id,resource_size_t rcrb)1100d5b1a271SRobert Richter struct cxl_dport *devm_cxl_add_rch_dport(struct cxl_port *port,
1101d5b1a271SRobert Richter 					 struct device *dport_dev, int port_id,
1102d5b1a271SRobert Richter 					 resource_size_t rcrb)
1103d5b1a271SRobert Richter {
1104d5b1a271SRobert Richter 	struct cxl_dport *dport;
1105d5b1a271SRobert Richter 
1106d5b1a271SRobert Richter 	if (rcrb == CXL_RESOURCE_NONE) {
1107d5b1a271SRobert Richter 		dev_dbg(&port->dev, "failed to add RCH dport, missing RCRB\n");
1108d5b1a271SRobert Richter 		return ERR_PTR(-EINVAL);
1109d5b1a271SRobert Richter 	}
1110d5b1a271SRobert Richter 
1111d5b1a271SRobert Richter 	dport = __devm_cxl_add_dport(port, dport_dev, port_id,
1112eb4663b0SRobert Richter 				     CXL_RESOURCE_NONE, rcrb);
1113d5b1a271SRobert Richter 	if (IS_ERR(dport)) {
1114d5b1a271SRobert Richter 		dev_dbg(dport_dev, "failed to add RCH dport to %s: %ld\n",
1115d5b1a271SRobert Richter 			dev_name(&port->dev), PTR_ERR(dport));
1116d5b1a271SRobert Richter 	} else {
1117d5b1a271SRobert Richter 		dev_dbg(dport_dev, "RCH dport added to %s\n",
1118d5b1a271SRobert Richter 			dev_name(&port->dev));
1119d5b1a271SRobert Richter 	}
1120d5b1a271SRobert Richter 
1121d5b1a271SRobert Richter 	return dport;
1122d5b1a271SRobert Richter }
1123d5b1a271SRobert Richter EXPORT_SYMBOL_NS_GPL(devm_cxl_add_rch_dport, CXL);
1124d5b1a271SRobert Richter 
add_ep(struct cxl_ep * new)1125de516b40SDan Williams static int add_ep(struct cxl_ep *new)
11262703c16cSDan Williams {
1127de516b40SDan Williams 	struct cxl_port *port = new->dport->port;
1128256d0e9eSDan Williams 	int rc;
11292703c16cSDan Williams 
113038a34e10SDan Williams 	device_lock(&port->dev);
11312703c16cSDan Williams 	if (port->dead) {
113238a34e10SDan Williams 		device_unlock(&port->dev);
11332703c16cSDan Williams 		return -ENXIO;
11342703c16cSDan Williams 	}
1135256d0e9eSDan Williams 	rc = xa_insert(&port->endpoints, (unsigned long)new->ep, new,
1136256d0e9eSDan Williams 		       GFP_KERNEL);
113738a34e10SDan Williams 	device_unlock(&port->dev);
11382703c16cSDan Williams 
1139256d0e9eSDan Williams 	return rc;
11402703c16cSDan Williams }
11412703c16cSDan Williams 
11422703c16cSDan Williams /**
11432703c16cSDan Williams  * cxl_add_ep - register an endpoint's interest in a port
1144de516b40SDan Williams  * @dport: the dport that routes to @ep_dev
11452703c16cSDan Williams  * @ep_dev: device representing the endpoint
11462703c16cSDan Williams  *
11472703c16cSDan Williams  * Intermediate CXL ports are scanned based on the arrival of endpoints.
11482703c16cSDan Williams  * When those endpoints depart the port can be destroyed once all
11492703c16cSDan Williams  * endpoints that care about that port have been removed.
11502703c16cSDan Williams  */
cxl_add_ep(struct cxl_dport * dport,struct device * ep_dev)1151de516b40SDan Williams static int cxl_add_ep(struct cxl_dport *dport, struct device *ep_dev)
11522703c16cSDan Williams {
11532703c16cSDan Williams 	struct cxl_ep *ep;
11542703c16cSDan Williams 	int rc;
11552703c16cSDan Williams 
11562703c16cSDan Williams 	ep = kzalloc(sizeof(*ep), GFP_KERNEL);
11572703c16cSDan Williams 	if (!ep)
11582703c16cSDan Williams 		return -ENOMEM;
11592703c16cSDan Williams 
11602703c16cSDan Williams 	ep->ep = get_device(ep_dev);
1161de516b40SDan Williams 	ep->dport = dport;
11622703c16cSDan Williams 
1163de516b40SDan Williams 	rc = add_ep(ep);
11642703c16cSDan Williams 	if (rc)
11652703c16cSDan Williams 		cxl_ep_release(ep);
11662703c16cSDan Williams 	return rc;
11672703c16cSDan Williams }
11682703c16cSDan Williams 
11692703c16cSDan Williams struct cxl_find_port_ctx {
11702703c16cSDan Williams 	const struct device *dport_dev;
11712703c16cSDan Williams 	const struct cxl_port *parent_port;
1172de516b40SDan Williams 	struct cxl_dport **dport;
11732703c16cSDan Williams };
11742703c16cSDan Williams 
match_port_by_dport(struct device * dev,const void * data)11752703c16cSDan Williams static int match_port_by_dport(struct device *dev, const void *data)
11762703c16cSDan Williams {
11772703c16cSDan Williams 	const struct cxl_find_port_ctx *ctx = data;
1178de516b40SDan Williams 	struct cxl_dport *dport;
11792703c16cSDan Williams 	struct cxl_port *port;
11802703c16cSDan Williams 
11812703c16cSDan Williams 	if (!is_cxl_port(dev))
11822703c16cSDan Williams 		return 0;
11832703c16cSDan Williams 	if (ctx->parent_port && dev->parent != &ctx->parent_port->dev)
11842703c16cSDan Williams 		return 0;
11852703c16cSDan Williams 
11862703c16cSDan Williams 	port = to_cxl_port(dev);
1187de516b40SDan Williams 	dport = cxl_find_dport_by_dev(port, ctx->dport_dev);
1188de516b40SDan Williams 	if (ctx->dport)
1189de516b40SDan Williams 		*ctx->dport = dport;
1190de516b40SDan Williams 	return dport != NULL;
11912703c16cSDan Williams }
11922703c16cSDan Williams 
__find_cxl_port(struct cxl_find_port_ctx * ctx)11932703c16cSDan Williams static struct cxl_port *__find_cxl_port(struct cxl_find_port_ctx *ctx)
11942703c16cSDan Williams {
11952703c16cSDan Williams 	struct device *dev;
11962703c16cSDan Williams 
11972703c16cSDan Williams 	if (!ctx->dport_dev)
11982703c16cSDan Williams 		return NULL;
11992703c16cSDan Williams 
12002703c16cSDan Williams 	dev = bus_find_device(&cxl_bus_type, NULL, ctx, match_port_by_dport);
12012703c16cSDan Williams 	if (dev)
12022703c16cSDan Williams 		return to_cxl_port(dev);
12032703c16cSDan Williams 	return NULL;
12042703c16cSDan Williams }
12052703c16cSDan Williams 
find_cxl_port(struct device * dport_dev,struct cxl_dport ** dport)1206de516b40SDan Williams static struct cxl_port *find_cxl_port(struct device *dport_dev,
1207de516b40SDan Williams 				      struct cxl_dport **dport)
12082703c16cSDan Williams {
12092703c16cSDan Williams 	struct cxl_find_port_ctx ctx = {
12102703c16cSDan Williams 		.dport_dev = dport_dev,
1211de516b40SDan Williams 		.dport = dport,
12122703c16cSDan Williams 	};
1213de516b40SDan Williams 	struct cxl_port *port;
12142703c16cSDan Williams 
1215de516b40SDan Williams 	port = __find_cxl_port(&ctx);
1216de516b40SDan Williams 	return port;
12172703c16cSDan Williams }
12182703c16cSDan Williams 
find_cxl_port_at(struct cxl_port * parent_port,struct device * dport_dev,struct cxl_dport ** dport)12192703c16cSDan Williams static struct cxl_port *find_cxl_port_at(struct cxl_port *parent_port,
1220de516b40SDan Williams 					 struct device *dport_dev,
1221de516b40SDan Williams 					 struct cxl_dport **dport)
12222703c16cSDan Williams {
12232703c16cSDan Williams 	struct cxl_find_port_ctx ctx = {
12242703c16cSDan Williams 		.dport_dev = dport_dev,
12252703c16cSDan Williams 		.parent_port = parent_port,
1226de516b40SDan Williams 		.dport = dport,
12272703c16cSDan Williams 	};
1228de516b40SDan Williams 	struct cxl_port *port;
12292703c16cSDan Williams 
1230de516b40SDan Williams 	port = __find_cxl_port(&ctx);
1231de516b40SDan Williams 	return port;
12322703c16cSDan Williams }
12332703c16cSDan Williams 
12342703c16cSDan Williams /*
1235cbbd05d0SRandy Dunlap  * All users of grandparent() are using it to walk PCIe-like switch port
12362703c16cSDan Williams  * hierarchy. A PCIe switch is comprised of a bridge device representing the
12372703c16cSDan Williams  * upstream switch port and N bridges representing downstream switch ports. When
12382703c16cSDan Williams  * bridges stack the grand-parent of a downstream switch port is another
12392703c16cSDan Williams  * downstream switch port in the immediate ancestor switch.
12402703c16cSDan Williams  */
grandparent(struct device * dev)12412703c16cSDan Williams static struct device *grandparent(struct device *dev)
12422703c16cSDan Williams {
12432703c16cSDan Williams 	if (dev && dev->parent)
12442703c16cSDan Williams 		return dev->parent->parent;
12452703c16cSDan Williams 	return NULL;
12462703c16cSDan Williams }
12472703c16cSDan Williams 
endpoint_host(struct cxl_port * endpoint)12486b2e428eSDan Williams static struct device *endpoint_host(struct cxl_port *endpoint)
12496b2e428eSDan Williams {
12506b2e428eSDan Williams 	struct cxl_port *port = to_cxl_port(endpoint->dev.parent);
12516b2e428eSDan Williams 
12526b2e428eSDan Williams 	if (is_cxl_root(port))
12536b2e428eSDan Williams 		return port->uport_dev;
12546b2e428eSDan Williams 	return &port->dev;
12556b2e428eSDan Williams }
12566b2e428eSDan Williams 
delete_endpoint(void * data)12578dd2bc0fSBen Widawsky static void delete_endpoint(void *data)
12588dd2bc0fSBen Widawsky {
12598dd2bc0fSBen Widawsky 	struct cxl_memdev *cxlmd = data;
1260516b300cSDan Williams 	struct cxl_port *endpoint = cxlmd->endpoint;
12616b2e428eSDan Williams 	struct device *host = endpoint_host(endpoint);
12628dd2bc0fSBen Widawsky 
12636b2e428eSDan Williams 	device_lock(host);
12646b2e428eSDan Williams 	if (host->driver && !endpoint->dead) {
12656b2e428eSDan Williams 		devm_release_action(host, cxl_unlink_parent_dport, endpoint);
12666b2e428eSDan Williams 		devm_release_action(host, cxl_unlink_uport, endpoint);
12676b2e428eSDan Williams 		devm_release_action(host, unregister_port, endpoint);
12688dd2bc0fSBen Widawsky 	}
1269516b300cSDan Williams 	cxlmd->endpoint = NULL;
12706b2e428eSDan Williams 	device_unlock(host);
12718dd2bc0fSBen Widawsky 	put_device(&endpoint->dev);
12726b2e428eSDan Williams 	put_device(host);
12738dd2bc0fSBen Widawsky }
12748dd2bc0fSBen Widawsky 
cxl_endpoint_autoremove(struct cxl_memdev * cxlmd,struct cxl_port * endpoint)12758dd2bc0fSBen Widawsky int cxl_endpoint_autoremove(struct cxl_memdev *cxlmd, struct cxl_port *endpoint)
12768dd2bc0fSBen Widawsky {
12776b2e428eSDan Williams 	struct device *host = endpoint_host(endpoint);
12788dd2bc0fSBen Widawsky 	struct device *dev = &cxlmd->dev;
12798dd2bc0fSBen Widawsky 
12806b2e428eSDan Williams 	get_device(host);
12818dd2bc0fSBen Widawsky 	get_device(&endpoint->dev);
1282516b300cSDan Williams 	cxlmd->endpoint = endpoint;
12832345df54SDan Williams 	cxlmd->depth = endpoint->depth;
12848dd2bc0fSBen Widawsky 	return devm_add_action_or_reset(dev, delete_endpoint, cxlmd);
12858dd2bc0fSBen Widawsky }
12868dd2bc0fSBen Widawsky EXPORT_SYMBOL_NS_GPL(cxl_endpoint_autoremove, CXL);
12878dd2bc0fSBen Widawsky 
12882703c16cSDan Williams /*
12892703c16cSDan Williams  * The natural end of life of a non-root 'cxl_port' is when its parent port goes
12902703c16cSDan Williams  * through a ->remove() event ("top-down" unregistration). The unnatural trigger
12912703c16cSDan Williams  * for a port to be unregistered is when all memdevs beneath that port have gone
12922703c16cSDan Williams  * through ->remove(). This "bottom-up" removal selectively removes individual
12932703c16cSDan Williams  * child ports manually. This depends on devm_cxl_add_port() to not change is
129439178585SDan Williams  * devm action registration order, and for dports to have already been
129539178585SDan Williams  * destroyed by reap_dports().
12962703c16cSDan Williams  */
delete_switch_port(struct cxl_port * port)129739178585SDan Williams static void delete_switch_port(struct cxl_port *port)
12982703c16cSDan Williams {
1299172738bbSDan Williams 	devm_release_action(port->dev.parent, cxl_unlink_parent_dport, port);
130039178585SDan Williams 	devm_release_action(port->dev.parent, cxl_unlink_uport, port);
130139178585SDan Williams 	devm_release_action(port->dev.parent, unregister_port, port);
130239178585SDan Williams }
13032703c16cSDan Williams 
reap_dports(struct cxl_port * port)130439178585SDan Williams static void reap_dports(struct cxl_port *port)
130539178585SDan Williams {
130639178585SDan Williams 	struct cxl_dport *dport;
130739178585SDan Williams 	unsigned long index;
130839178585SDan Williams 
130939178585SDan Williams 	device_lock_assert(&port->dev);
131039178585SDan Williams 
131139178585SDan Williams 	xa_for_each(&port->dports, index, dport) {
13122703c16cSDan Williams 		devm_release_action(&port->dev, cxl_dport_unlink, dport);
13132703c16cSDan Williams 		devm_release_action(&port->dev, cxl_dport_remove, dport);
13142703c16cSDan Williams 		devm_kfree(&port->dev, dport);
13152703c16cSDan Williams 	}
13162703c16cSDan Williams }
13172703c16cSDan Williams 
13182345df54SDan Williams struct detach_ctx {
13192345df54SDan Williams 	struct cxl_memdev *cxlmd;
13202345df54SDan Williams 	int depth;
13212345df54SDan Williams };
13222345df54SDan Williams 
port_has_memdev(struct device * dev,const void * data)13232345df54SDan Williams static int port_has_memdev(struct device *dev, const void *data)
13242345df54SDan Williams {
13252345df54SDan Williams 	const struct detach_ctx *ctx = data;
13262345df54SDan Williams 	struct cxl_port *port;
13272345df54SDan Williams 
13282345df54SDan Williams 	if (!is_cxl_port(dev))
13292345df54SDan Williams 		return 0;
13302345df54SDan Williams 
13312345df54SDan Williams 	port = to_cxl_port(dev);
13322345df54SDan Williams 	if (port->depth != ctx->depth)
13332345df54SDan Williams 		return 0;
13342345df54SDan Williams 
13352345df54SDan Williams 	return !!cxl_ep_load(port, ctx->cxlmd);
13362345df54SDan Williams }
13372345df54SDan Williams 
cxl_detach_ep(void * data)13382703c16cSDan Williams static void cxl_detach_ep(void *data)
13392703c16cSDan Williams {
13402703c16cSDan Williams 	struct cxl_memdev *cxlmd = data;
13412703c16cSDan Williams 
13422345df54SDan Williams 	for (int i = cxlmd->depth - 1; i >= 1; i--) {
13432703c16cSDan Williams 		struct cxl_port *port, *parent_port;
13442345df54SDan Williams 		struct detach_ctx ctx = {
13452345df54SDan Williams 			.cxlmd = cxlmd,
13462345df54SDan Williams 			.depth = i,
13472345df54SDan Williams 		};
13482345df54SDan Williams 		struct device *dev;
13492703c16cSDan Williams 		struct cxl_ep *ep;
135039178585SDan Williams 		bool died = false;
13512703c16cSDan Williams 
13522345df54SDan Williams 		dev = bus_find_device(&cxl_bus_type, NULL, &ctx,
13532345df54SDan Williams 				      port_has_memdev);
13542345df54SDan Williams 		if (!dev)
135505e81553SWan Jiabing 			continue;
13562345df54SDan Williams 		port = to_cxl_port(dev);
13572703c16cSDan Williams 
13582703c16cSDan Williams 		parent_port = to_cxl_port(port->dev.parent);
135938a34e10SDan Williams 		device_lock(&parent_port->dev);
136038a34e10SDan Williams 		device_lock(&port->dev);
1361256d0e9eSDan Williams 		ep = cxl_ep_load(port, cxlmd);
13622703c16cSDan Williams 		dev_dbg(&cxlmd->dev, "disconnect %s from %s\n",
13632703c16cSDan Williams 			ep ? dev_name(ep->ep) : "", dev_name(&port->dev));
1364256d0e9eSDan Williams 		cxl_ep_remove(port, ep);
1365256d0e9eSDan Williams 		if (ep && !port->dead && xa_empty(&port->endpoints) &&
13662345df54SDan Williams 		    !is_cxl_root(parent_port) && parent_port->dev.driver) {
13672703c16cSDan Williams 			/*
13682703c16cSDan Williams 			 * This was the last ep attached to a dynamically
13692703c16cSDan Williams 			 * enumerated port. Block new cxl_add_ep() and garbage
13702703c16cSDan Williams 			 * collect the port.
13712703c16cSDan Williams 			 */
137239178585SDan Williams 			died = true;
13732703c16cSDan Williams 			port->dead = true;
137439178585SDan Williams 			reap_dports(port);
13752703c16cSDan Williams 		}
137638a34e10SDan Williams 		device_unlock(&port->dev);
13772703c16cSDan Williams 
137839178585SDan Williams 		if (died) {
13792703c16cSDan Williams 			dev_dbg(&cxlmd->dev, "delete %s\n",
13802703c16cSDan Williams 				dev_name(&port->dev));
138139178585SDan Williams 			delete_switch_port(port);
13822703c16cSDan Williams 		}
13832703c16cSDan Williams 		put_device(&port->dev);
138438a34e10SDan Williams 		device_unlock(&parent_port->dev);
13852703c16cSDan Williams 	}
13862703c16cSDan Williams }
13872703c16cSDan Williams 
find_component_registers(struct device * dev)13882703c16cSDan Williams static resource_size_t find_component_registers(struct device *dev)
13892703c16cSDan Williams {
13902703c16cSDan Williams 	struct cxl_register_map map;
13912703c16cSDan Williams 	struct pci_dev *pdev;
13922703c16cSDan Williams 
13932703c16cSDan Williams 	/*
13942703c16cSDan Williams 	 * Theoretically, CXL component registers can be hosted on a
13952703c16cSDan Williams 	 * non-PCI device, in practice, only cxl_test hits this case.
13962703c16cSDan Williams 	 */
13972703c16cSDan Williams 	if (!dev_is_pci(dev))
13982703c16cSDan Williams 		return CXL_RESOURCE_NONE;
13992703c16cSDan Williams 
14002703c16cSDan Williams 	pdev = to_pci_dev(dev);
14012703c16cSDan Williams 
14022703c16cSDan Williams 	cxl_find_regblock(pdev, CXL_REGLOC_RBI_COMPONENT, &map);
14036c7f4f1eSDan Williams 	return map.resource;
14042703c16cSDan Williams }
14052703c16cSDan Williams 
add_port_attach_ep(struct cxl_memdev * cxlmd,struct device * uport_dev,struct device * dport_dev)14062703c16cSDan Williams static int add_port_attach_ep(struct cxl_memdev *cxlmd,
14072703c16cSDan Williams 			      struct device *uport_dev,
14082703c16cSDan Williams 			      struct device *dport_dev)
14092703c16cSDan Williams {
14102703c16cSDan Williams 	struct device *dparent = grandparent(dport_dev);
14112703c16cSDan Williams 	struct cxl_port *port, *parent_port = NULL;
14121b58b4caSDan Williams 	struct cxl_dport *dport, *parent_dport;
14132703c16cSDan Williams 	resource_size_t component_reg_phys;
14142703c16cSDan Williams 	int rc;
14152703c16cSDan Williams 
14162703c16cSDan Williams 	if (!dparent) {
14172703c16cSDan Williams 		/*
14182703c16cSDan Williams 		 * The iteration reached the topology root without finding the
14192703c16cSDan Williams 		 * CXL-root 'cxl_port' on a previous iteration, fail for now to
14202703c16cSDan Williams 		 * be re-probed after platform driver attaches.
14212703c16cSDan Williams 		 */
14222703c16cSDan Williams 		dev_dbg(&cxlmd->dev, "%s is a root dport\n",
14232703c16cSDan Williams 			dev_name(dport_dev));
14242703c16cSDan Williams 		return -ENXIO;
14252703c16cSDan Williams 	}
14262703c16cSDan Williams 
14271b58b4caSDan Williams 	parent_port = find_cxl_port(dparent, &parent_dport);
14282703c16cSDan Williams 	if (!parent_port) {
14292703c16cSDan Williams 		/* iterate to create this parent_port */
14302703c16cSDan Williams 		return -EAGAIN;
14312703c16cSDan Williams 	}
14322703c16cSDan Williams 
143338a34e10SDan Williams 	device_lock(&parent_port->dev);
14342703c16cSDan Williams 	if (!parent_port->dev.driver) {
14352703c16cSDan Williams 		dev_warn(&cxlmd->dev,
14362703c16cSDan Williams 			 "port %s:%s disabled, failed to enumerate CXL.mem\n",
14372703c16cSDan Williams 			 dev_name(&parent_port->dev), dev_name(uport_dev));
14382703c16cSDan Williams 		port = ERR_PTR(-ENXIO);
14392703c16cSDan Williams 		goto out;
14402703c16cSDan Williams 	}
14412703c16cSDan Williams 
1442de516b40SDan Williams 	port = find_cxl_port_at(parent_port, dport_dev, &dport);
14432703c16cSDan Williams 	if (!port) {
14442703c16cSDan Williams 		component_reg_phys = find_component_registers(uport_dev);
14452703c16cSDan Williams 		port = devm_cxl_add_port(&parent_port->dev, uport_dev,
14461b58b4caSDan Williams 					 component_reg_phys, parent_dport);
1447de516b40SDan Williams 		/* retry find to pick up the new dport information */
14482703c16cSDan Williams 		if (!IS_ERR(port))
1449de516b40SDan Williams 			port = find_cxl_port_at(parent_port, dport_dev, &dport);
14502703c16cSDan Williams 	}
14512703c16cSDan Williams out:
145238a34e10SDan Williams 	device_unlock(&parent_port->dev);
14532703c16cSDan Williams 
14542703c16cSDan Williams 	if (IS_ERR(port))
14552703c16cSDan Williams 		rc = PTR_ERR(port);
14562703c16cSDan Williams 	else {
14572703c16cSDan Williams 		dev_dbg(&cxlmd->dev, "add to new port %s:%s\n",
14587481653dSDan Williams 			dev_name(&port->dev), dev_name(port->uport_dev));
1459de516b40SDan Williams 		rc = cxl_add_ep(dport, &cxlmd->dev);
1460256d0e9eSDan Williams 		if (rc == -EBUSY) {
14612703c16cSDan Williams 			/*
14622703c16cSDan Williams 			 * "can't" happen, but this error code means
14632703c16cSDan Williams 			 * something to the caller, so translate it.
14642703c16cSDan Williams 			 */
14652703c16cSDan Williams 			rc = -ENXIO;
14662703c16cSDan Williams 		}
14672703c16cSDan Williams 		put_device(&port->dev);
14682703c16cSDan Williams 	}
14692703c16cSDan Williams 
14702703c16cSDan Williams 	put_device(&parent_port->dev);
14712703c16cSDan Williams 	return rc;
14722703c16cSDan Williams }
14732703c16cSDan Williams 
devm_cxl_enumerate_ports(struct cxl_memdev * cxlmd)14742703c16cSDan Williams int devm_cxl_enumerate_ports(struct cxl_memdev *cxlmd)
14752703c16cSDan Williams {
14762703c16cSDan Williams 	struct device *dev = &cxlmd->dev;
14772703c16cSDan Williams 	struct device *iter;
14782703c16cSDan Williams 	int rc;
14792703c16cSDan Williams 
14800a19bfc8SDan Williams 	/*
14810a19bfc8SDan Williams 	 * Skip intermediate port enumeration in the RCH case, there
14820a19bfc8SDan Williams 	 * are no ports in between a host bridge and an endpoint.
14830a19bfc8SDan Williams 	 */
14840a19bfc8SDan Williams 	if (cxlmd->cxlds->rcd)
14850a19bfc8SDan Williams 		return 0;
14860a19bfc8SDan Williams 
14872703c16cSDan Williams 	rc = devm_add_action_or_reset(&cxlmd->dev, cxl_detach_ep, cxlmd);
14882703c16cSDan Williams 	if (rc)
14892703c16cSDan Williams 		return rc;
14902703c16cSDan Williams 
14912703c16cSDan Williams 	/*
14922703c16cSDan Williams 	 * Scan for and add all cxl_ports in this device's ancestry.
14932703c16cSDan Williams 	 * Repeat until no more ports are added. Abort if a port add
14942703c16cSDan Williams 	 * attempt fails.
14952703c16cSDan Williams 	 */
14962703c16cSDan Williams retry:
14972703c16cSDan Williams 	for (iter = dev; iter; iter = grandparent(iter)) {
14982703c16cSDan Williams 		struct device *dport_dev = grandparent(iter);
14992703c16cSDan Williams 		struct device *uport_dev;
1500de516b40SDan Williams 		struct cxl_dport *dport;
15012703c16cSDan Williams 		struct cxl_port *port;
15022703c16cSDan Williams 
15032703c16cSDan Williams 		if (!dport_dev)
15042703c16cSDan Williams 			return 0;
15052703c16cSDan Williams 
15062703c16cSDan Williams 		uport_dev = dport_dev->parent;
15072703c16cSDan Williams 		if (!uport_dev) {
15082703c16cSDan Williams 			dev_warn(dev, "at %s no parent for dport: %s\n",
15092703c16cSDan Williams 				 dev_name(iter), dev_name(dport_dev));
15102703c16cSDan Williams 			return -ENXIO;
15112703c16cSDan Williams 		}
15122703c16cSDan Williams 
15132703c16cSDan Williams 		dev_dbg(dev, "scan: iter: %s dport_dev: %s parent: %s\n",
15142703c16cSDan Williams 			dev_name(iter), dev_name(dport_dev),
15152703c16cSDan Williams 			dev_name(uport_dev));
1516de516b40SDan Williams 		port = find_cxl_port(dport_dev, &dport);
15172703c16cSDan Williams 		if (port) {
15182703c16cSDan Williams 			dev_dbg(&cxlmd->dev,
15192703c16cSDan Williams 				"found already registered port %s:%s\n",
15207481653dSDan Williams 				dev_name(&port->dev),
15217481653dSDan Williams 				dev_name(port->uport_dev));
1522de516b40SDan Williams 			rc = cxl_add_ep(dport, &cxlmd->dev);
15232703c16cSDan Williams 
15242703c16cSDan Williams 			/*
15252703c16cSDan Williams 			 * If the endpoint already exists in the port's list,
15262703c16cSDan Williams 			 * that's ok, it was added on a previous pass.
15272703c16cSDan Williams 			 * Otherwise, retry in add_port_attach_ep() after taking
15282703c16cSDan Williams 			 * the parent_port lock as the current port may be being
15292703c16cSDan Williams 			 * reaped.
15302703c16cSDan Williams 			 */
1531256d0e9eSDan Williams 			if (rc && rc != -EBUSY) {
15322703c16cSDan Williams 				put_device(&port->dev);
15332703c16cSDan Williams 				return rc;
15342703c16cSDan Williams 			}
15352703c16cSDan Williams 
15362703c16cSDan Williams 			/* Any more ports to add between this one and the root? */
15372703c16cSDan Williams 			if (!dev_is_cxl_root_child(&port->dev)) {
15382703c16cSDan Williams 				put_device(&port->dev);
15392703c16cSDan Williams 				continue;
15402703c16cSDan Williams 			}
15412703c16cSDan Williams 
15422703c16cSDan Williams 			put_device(&port->dev);
15432703c16cSDan Williams 			return 0;
15442703c16cSDan Williams 		}
15452703c16cSDan Williams 
15462703c16cSDan Williams 		rc = add_port_attach_ep(cxlmd, uport_dev, dport_dev);
15472703c16cSDan Williams 		/* port missing, try to add parent */
15482703c16cSDan Williams 		if (rc == -EAGAIN)
15492703c16cSDan Williams 			continue;
15502703c16cSDan Williams 		/* failed to add ep or port */
15512703c16cSDan Williams 		if (rc)
15522703c16cSDan Williams 			return rc;
15532703c16cSDan Williams 		/* port added, new descendants possible, start over */
15542703c16cSDan Williams 		goto retry;
15552703c16cSDan Williams 	}
15562703c16cSDan Williams 
15572703c16cSDan Williams 	return 0;
15582703c16cSDan Williams }
15592703c16cSDan Williams EXPORT_SYMBOL_NS_GPL(devm_cxl_enumerate_ports, CXL);
15602703c16cSDan Williams 
cxl_pci_find_port(struct pci_dev * pdev,struct cxl_dport ** dport)1561733b57f2SRobert Richter struct cxl_port *cxl_pci_find_port(struct pci_dev *pdev,
1562733b57f2SRobert Richter 				   struct cxl_dport **dport)
1563733b57f2SRobert Richter {
1564733b57f2SRobert Richter 	return find_cxl_port(pdev->dev.parent, dport);
1565733b57f2SRobert Richter }
1566733b57f2SRobert Richter EXPORT_SYMBOL_NS_GPL(cxl_pci_find_port, CXL);
1567733b57f2SRobert Richter 
cxl_mem_find_port(struct cxl_memdev * cxlmd,struct cxl_dport ** dport)15681b58b4caSDan Williams struct cxl_port *cxl_mem_find_port(struct cxl_memdev *cxlmd,
15691b58b4caSDan Williams 				   struct cxl_dport **dport)
15708dd2bc0fSBen Widawsky {
15711b58b4caSDan Williams 	return find_cxl_port(grandparent(&cxlmd->dev), dport);
15728dd2bc0fSBen Widawsky }
15738dd2bc0fSBen Widawsky EXPORT_SYMBOL_NS_GPL(cxl_mem_find_port, CXL);
15748dd2bc0fSBen Widawsky 
decoder_populate_targets(struct cxl_switch_decoder * cxlsd,struct cxl_port * port,int * target_map)1575e636479eSDan Williams static int decoder_populate_targets(struct cxl_switch_decoder *cxlsd,
15760ff0af18SDan Williams 				    struct cxl_port *port, int *target_map)
15770ff0af18SDan Williams {
15781a536972SDan Williams 	int i;
15790ff0af18SDan Williams 
15800ff0af18SDan Williams 	if (!target_map)
15810ff0af18SDan Williams 		return 0;
15820ff0af18SDan Williams 
1583d17d0540SDan Williams 	device_lock_assert(&port->dev);
1584d17d0540SDan Williams 
158539178585SDan Williams 	if (xa_empty(&port->dports))
1586d17d0540SDan Williams 		return -EINVAL;
15870ff0af18SDan Williams 
15881a536972SDan Williams 	guard(rwsem_write)(&cxl_region_rwsem);
15895a473e32SHuang Ying 	for (i = 0; i < cxlsd->cxld.interleave_ways; i++) {
15900ff0af18SDan Williams 		struct cxl_dport *dport = find_dport(port, target_map[i]);
15910ff0af18SDan Williams 
15921a536972SDan Williams 		if (!dport)
15931a536972SDan Williams 			return -ENXIO;
1594e636479eSDan Williams 		cxlsd->target[i] = dport;
15950ff0af18SDan Williams 	}
15960ff0af18SDan Williams 
15971a536972SDan Williams 	return 0;
15980ff0af18SDan Williams }
15990ff0af18SDan Williams 
cxl_hb_modulo(struct cxl_root_decoder * cxlrd,int pos)1600f9db85bfSAlison Schofield struct cxl_dport *cxl_hb_modulo(struct cxl_root_decoder *cxlrd, int pos)
16016aa41144SDan Williams {
16026aa41144SDan Williams 	struct cxl_switch_decoder *cxlsd = &cxlrd->cxlsd;
16036aa41144SDan Williams 	struct cxl_decoder *cxld = &cxlsd->cxld;
16046aa41144SDan Williams 	int iw;
16056aa41144SDan Williams 
16066aa41144SDan Williams 	iw = cxld->interleave_ways;
16076aa41144SDan Williams 	if (dev_WARN_ONCE(&cxld->dev, iw != cxlsd->nr_targets,
16086aa41144SDan Williams 			  "misconfigured root decoder\n"))
16096aa41144SDan Williams 		return NULL;
16106aa41144SDan Williams 
16116aa41144SDan Williams 	return cxlrd->cxlsd.target[pos % iw];
16126aa41144SDan Williams }
1613f9db85bfSAlison Schofield EXPORT_SYMBOL_NS_GPL(cxl_hb_modulo, CXL);
16146aa41144SDan Williams 
16153750d013SDan Williams static struct lock_class_key cxl_decoder_key;
16163750d013SDan Williams 
1617d54c1bbeSBen Widawsky /**
1618e636479eSDan Williams  * cxl_decoder_init - Common decoder setup / initialization
1619d54c1bbeSBen Widawsky  * @port: owning port of this decoder
1620e636479eSDan Williams  * @cxld: common decoder properties to initialize
1621d54c1bbeSBen Widawsky  *
1622e636479eSDan Williams  * A port may contain one or more decoders. Each of those decoders
1623e636479eSDan Williams  * enable some address space for CXL.mem utilization. A decoder is
1624e636479eSDan Williams  * expected to be configured by the caller before registering via
1625e636479eSDan Williams  * cxl_decoder_add()
1626d54c1bbeSBen Widawsky  */
cxl_decoder_init(struct cxl_port * port,struct cxl_decoder * cxld)1627e636479eSDan Williams static int cxl_decoder_init(struct cxl_port *port, struct cxl_decoder *cxld)
16280ff0af18SDan Williams {
16290ff0af18SDan Williams 	struct device *dev;
1630e636479eSDan Williams 	int rc;
16310ff0af18SDan Williams 
16320ff0af18SDan Williams 	rc = ida_alloc(&port->decoder_ida, GFP_KERNEL);
16330ff0af18SDan Williams 	if (rc < 0)
1634e636479eSDan Williams 		return rc;
16350ff0af18SDan Williams 
163674be9877SDan Williams 	/* need parent to stick around to release the id */
163774be9877SDan Williams 	get_device(&port->dev);
16380ff0af18SDan Williams 	cxld->id = rc;
163974be9877SDan Williams 
16400ff0af18SDan Williams 	dev = &cxld->dev;
16410ff0af18SDan Williams 	device_initialize(dev);
16423750d013SDan Williams 	lockdep_set_class(&dev->mutex, &cxl_decoder_key);
16430ff0af18SDan Williams 	device_set_pm_not_required(dev);
16440ff0af18SDan Williams 	dev->parent = &port->dev;
16450ff0af18SDan Williams 	dev->bus = &cxl_bus_type;
16460ff0af18SDan Williams 
1647d2b61ed2SBen Widawsky 	/* Pre initialize an "empty" decoder */
1648d2b61ed2SBen Widawsky 	cxld->interleave_ways = 1;
1649d2b61ed2SBen Widawsky 	cxld->interleave_granularity = PAGE_SIZE;
16505aa39a91SDan Williams 	cxld->target_type = CXL_DECODER_HOSTONLYMEM;
1651e50fe01eSDan Williams 	cxld->hpa_range = (struct range) {
1652e50fe01eSDan Williams 		.start = 0,
1653e50fe01eSDan Williams 		.end = -1,
1654e50fe01eSDan Williams 	};
1655d2b61ed2SBen Widawsky 
1656e636479eSDan Williams 	return 0;
1657e636479eSDan Williams }
1658e636479eSDan Williams 
cxl_switch_decoder_init(struct cxl_port * port,struct cxl_switch_decoder * cxlsd,int nr_targets)1659e636479eSDan Williams static int cxl_switch_decoder_init(struct cxl_port *port,
1660e636479eSDan Williams 				   struct cxl_switch_decoder *cxlsd,
1661e636479eSDan Williams 				   int nr_targets)
1662e636479eSDan Williams {
1663e636479eSDan Williams 	if (nr_targets > CXL_DECODER_MAX_INTERLEAVE)
1664e636479eSDan Williams 		return -EINVAL;
1665e636479eSDan Williams 
1666e636479eSDan Williams 	cxlsd->nr_targets = nr_targets;
1667e636479eSDan Williams 	return cxl_decoder_init(port, &cxlsd->cxld);
16680ff0af18SDan Williams }
16690ff0af18SDan Williams 
1670d54c1bbeSBen Widawsky /**
1671d54c1bbeSBen Widawsky  * cxl_root_decoder_alloc - Allocate a root level decoder
1672d54c1bbeSBen Widawsky  * @port: owning CXL root of this decoder
1673d54c1bbeSBen Widawsky  * @nr_targets: static number of downstream targets
1674f9db85bfSAlison Schofield  * @calc_hb: which host bridge covers the n'th position by granularity
1675d54c1bbeSBen Widawsky  *
1676d54c1bbeSBen Widawsky  * Return: A new cxl decoder to be registered by cxl_decoder_add(). A
1677d54c1bbeSBen Widawsky  * 'CXL root' decoder is one that decodes from a top-level / static platform
1678d54c1bbeSBen Widawsky  * firmware description of CXL resources into a CXL standard decode
1679d54c1bbeSBen Widawsky  * topology.
1680d54c1bbeSBen Widawsky  */
cxl_root_decoder_alloc(struct cxl_port * port,unsigned int nr_targets,cxl_calc_hb_fn calc_hb)16810f157c7fSDan Williams struct cxl_root_decoder *cxl_root_decoder_alloc(struct cxl_port *port,
1682f9db85bfSAlison Schofield 						unsigned int nr_targets,
1683f9db85bfSAlison Schofield 						cxl_calc_hb_fn calc_hb)
1684d54c1bbeSBen Widawsky {
16850f157c7fSDan Williams 	struct cxl_root_decoder *cxlrd;
1686e636479eSDan Williams 	struct cxl_switch_decoder *cxlsd;
1687e636479eSDan Williams 	struct cxl_decoder *cxld;
1688e636479eSDan Williams 	int rc;
1689e636479eSDan Williams 
1690d54c1bbeSBen Widawsky 	if (!is_cxl_root(port))
1691d54c1bbeSBen Widawsky 		return ERR_PTR(-EINVAL);
1692d54c1bbeSBen Widawsky 
16930f157c7fSDan Williams 	cxlrd = kzalloc(struct_size(cxlrd, cxlsd.target, nr_targets),
16940f157c7fSDan Williams 			GFP_KERNEL);
16950f157c7fSDan Williams 	if (!cxlrd)
1696e636479eSDan Williams 		return ERR_PTR(-ENOMEM);
1697e636479eSDan Williams 
16980f157c7fSDan Williams 	cxlsd = &cxlrd->cxlsd;
1699e636479eSDan Williams 	rc = cxl_switch_decoder_init(port, cxlsd, nr_targets);
1700e636479eSDan Williams 	if (rc) {
17010f157c7fSDan Williams 		kfree(cxlrd);
1702e636479eSDan Williams 		return ERR_PTR(rc);
1703e636479eSDan Williams 	}
1704e636479eSDan Williams 
1705f9db85bfSAlison Schofield 	cxlrd->calc_hb = calc_hb;
1706a32320b7SDan Williams 	mutex_init(&cxlrd->range_lock);
17076aa41144SDan Williams 
1708e636479eSDan Williams 	cxld = &cxlsd->cxld;
1709e636479eSDan Williams 	cxld->dev.type = &cxl_decoder_root_type;
1710779dd20cSBen Widawsky 	/*
1711779dd20cSBen Widawsky 	 * cxl_root_decoder_release() special cases negative ids to
1712779dd20cSBen Widawsky 	 * detect memregion_alloc() failures.
1713779dd20cSBen Widawsky 	 */
1714779dd20cSBen Widawsky 	atomic_set(&cxlrd->region_id, -1);
1715779dd20cSBen Widawsky 	rc = memregion_alloc(GFP_KERNEL);
1716779dd20cSBen Widawsky 	if (rc < 0) {
1717779dd20cSBen Widawsky 		put_device(&cxld->dev);
1718779dd20cSBen Widawsky 		return ERR_PTR(rc);
1719779dd20cSBen Widawsky 	}
1720779dd20cSBen Widawsky 
1721779dd20cSBen Widawsky 	atomic_set(&cxlrd->region_id, rc);
17220f157c7fSDan Williams 	return cxlrd;
1723d54c1bbeSBen Widawsky }
1724d54c1bbeSBen Widawsky EXPORT_SYMBOL_NS_GPL(cxl_root_decoder_alloc, CXL);
1725d54c1bbeSBen Widawsky 
1726d54c1bbeSBen Widawsky /**
1727d54c1bbeSBen Widawsky  * cxl_switch_decoder_alloc - Allocate a switch level decoder
1728d54c1bbeSBen Widawsky  * @port: owning CXL switch port of this decoder
1729d54c1bbeSBen Widawsky  * @nr_targets: max number of dynamically addressable downstream targets
1730d54c1bbeSBen Widawsky  *
1731d54c1bbeSBen Widawsky  * Return: A new cxl decoder to be registered by cxl_decoder_add(). A
1732d54c1bbeSBen Widawsky  * 'switch' decoder is any decoder that can be enumerated by PCIe
1733d54c1bbeSBen Widawsky  * topology and the HDM Decoder Capability. This includes the decoders
1734d54c1bbeSBen Widawsky  * that sit between Switch Upstream Ports / Switch Downstream Ports and
1735d54c1bbeSBen Widawsky  * Host Bridges / Root Ports.
1736d54c1bbeSBen Widawsky  */
cxl_switch_decoder_alloc(struct cxl_port * port,unsigned int nr_targets)1737e636479eSDan Williams struct cxl_switch_decoder *cxl_switch_decoder_alloc(struct cxl_port *port,
1738d54c1bbeSBen Widawsky 						    unsigned int nr_targets)
1739d54c1bbeSBen Widawsky {
1740e636479eSDan Williams 	struct cxl_switch_decoder *cxlsd;
1741e636479eSDan Williams 	struct cxl_decoder *cxld;
1742e636479eSDan Williams 	int rc;
1743e636479eSDan Williams 
17449b71e1c9SBen Widawsky 	if (is_cxl_root(port) || is_cxl_endpoint(port))
1745d54c1bbeSBen Widawsky 		return ERR_PTR(-EINVAL);
1746d54c1bbeSBen Widawsky 
1747e636479eSDan Williams 	cxlsd = kzalloc(struct_size(cxlsd, target, nr_targets), GFP_KERNEL);
1748e636479eSDan Williams 	if (!cxlsd)
1749e636479eSDan Williams 		return ERR_PTR(-ENOMEM);
1750e636479eSDan Williams 
1751e636479eSDan Williams 	rc = cxl_switch_decoder_init(port, cxlsd, nr_targets);
1752e636479eSDan Williams 	if (rc) {
1753e636479eSDan Williams 		kfree(cxlsd);
1754e636479eSDan Williams 		return ERR_PTR(rc);
1755e636479eSDan Williams 	}
1756e636479eSDan Williams 
1757e636479eSDan Williams 	cxld = &cxlsd->cxld;
1758e636479eSDan Williams 	cxld->dev.type = &cxl_decoder_switch_type;
1759e636479eSDan Williams 	return cxlsd;
1760d54c1bbeSBen Widawsky }
1761d54c1bbeSBen Widawsky EXPORT_SYMBOL_NS_GPL(cxl_switch_decoder_alloc, CXL);
1762d54c1bbeSBen Widawsky 
1763d54c1bbeSBen Widawsky /**
17649b71e1c9SBen Widawsky  * cxl_endpoint_decoder_alloc - Allocate an endpoint decoder
17659b71e1c9SBen Widawsky  * @port: owning port of this decoder
17669b71e1c9SBen Widawsky  *
17679b71e1c9SBen Widawsky  * Return: A new cxl decoder to be registered by cxl_decoder_add()
17689b71e1c9SBen Widawsky  */
cxl_endpoint_decoder_alloc(struct cxl_port * port)17693bf65915SDan Williams struct cxl_endpoint_decoder *cxl_endpoint_decoder_alloc(struct cxl_port *port)
17709b71e1c9SBen Widawsky {
17713bf65915SDan Williams 	struct cxl_endpoint_decoder *cxled;
1772e636479eSDan Williams 	struct cxl_decoder *cxld;
1773e636479eSDan Williams 	int rc;
1774e636479eSDan Williams 
17759b71e1c9SBen Widawsky 	if (!is_cxl_endpoint(port))
17769b71e1c9SBen Widawsky 		return ERR_PTR(-EINVAL);
17779b71e1c9SBen Widawsky 
17783bf65915SDan Williams 	cxled = kzalloc(sizeof(*cxled), GFP_KERNEL);
17793bf65915SDan Williams 	if (!cxled)
1780e636479eSDan Williams 		return ERR_PTR(-ENOMEM);
1781e636479eSDan Williams 
1782b9686e8cSDan Williams 	cxled->pos = -1;
17833bf65915SDan Williams 	cxld = &cxled->cxld;
1784e636479eSDan Williams 	rc = cxl_decoder_init(port, cxld);
1785e636479eSDan Williams 	if (rc)	 {
17863bf65915SDan Williams 		kfree(cxled);
1787e636479eSDan Williams 		return ERR_PTR(rc);
1788e636479eSDan Williams 	}
1789e636479eSDan Williams 
1790e636479eSDan Williams 	cxld->dev.type = &cxl_decoder_endpoint_type;
17913bf65915SDan Williams 	return cxled;
17929b71e1c9SBen Widawsky }
17939b71e1c9SBen Widawsky EXPORT_SYMBOL_NS_GPL(cxl_endpoint_decoder_alloc, CXL);
17949b71e1c9SBen Widawsky 
17959b71e1c9SBen Widawsky /**
1796d17d0540SDan Williams  * cxl_decoder_add_locked - Add a decoder with targets
1797e636479eSDan Williams  * @cxld: The cxl decoder allocated by cxl_<type>_decoder_alloc()
1798d54c1bbeSBen Widawsky  * @target_map: A list of downstream ports that this decoder can direct memory
1799d54c1bbeSBen Widawsky  *              traffic to. These numbers should correspond with the port number
1800d54c1bbeSBen Widawsky  *              in the PCIe Link Capabilities structure.
1801d54c1bbeSBen Widawsky  *
1802d54c1bbeSBen Widawsky  * Certain types of decoders may not have any targets. The main example of this
1803d54c1bbeSBen Widawsky  * is an endpoint device. A more awkward example is a hostbridge whose root
1804d54c1bbeSBen Widawsky  * ports get hot added (technically possible, though unlikely).
1805d54c1bbeSBen Widawsky  *
1806d17d0540SDan Williams  * This is the locked variant of cxl_decoder_add().
1807d17d0540SDan Williams  *
1808d17d0540SDan Williams  * Context: Process context. Expects the device lock of the port that owns the
1809d17d0540SDan Williams  *	    @cxld to be held.
1810d54c1bbeSBen Widawsky  *
1811d54c1bbeSBen Widawsky  * Return: Negative error code if the decoder wasn't properly configured; else
1812d54c1bbeSBen Widawsky  *	   returns 0.
1813d54c1bbeSBen Widawsky  */
cxl_decoder_add_locked(struct cxl_decoder * cxld,int * target_map)1814d17d0540SDan Williams int cxl_decoder_add_locked(struct cxl_decoder *cxld, int *target_map)
18150ff0af18SDan Williams {
18160ff0af18SDan Williams 	struct cxl_port *port;
18170ff0af18SDan Williams 	struct device *dev;
18180ff0af18SDan Williams 	int rc;
18190ff0af18SDan Williams 
18200ff0af18SDan Williams 	if (WARN_ON_ONCE(!cxld))
18210ff0af18SDan Williams 		return -EINVAL;
18220ff0af18SDan Williams 
18230ff0af18SDan Williams 	if (WARN_ON_ONCE(IS_ERR(cxld)))
18240ff0af18SDan Williams 		return PTR_ERR(cxld);
18250ff0af18SDan Williams 
18260ff0af18SDan Williams 	if (cxld->interleave_ways < 1)
18270ff0af18SDan Williams 		return -EINVAL;
18280ff0af18SDan Williams 
18299b71e1c9SBen Widawsky 	dev = &cxld->dev;
18309b71e1c9SBen Widawsky 
18310ff0af18SDan Williams 	port = to_cxl_port(cxld->dev.parent);
18329b71e1c9SBen Widawsky 	if (!is_endpoint_decoder(dev)) {
1833e636479eSDan Williams 		struct cxl_switch_decoder *cxlsd = to_cxl_switch_decoder(dev);
1834e636479eSDan Williams 
1835e636479eSDan Williams 		rc = decoder_populate_targets(cxlsd, port, target_map);
18360909b4e5SDan Williams 		if (rc && (cxld->flags & CXL_DECODER_F_ENABLE)) {
18370909b4e5SDan Williams 			dev_err(&port->dev,
18380909b4e5SDan Williams 				"Failed to populate active decoder targets\n");
18390ff0af18SDan Williams 			return rc;
18409b71e1c9SBen Widawsky 		}
18410909b4e5SDan Williams 	}
18420ff0af18SDan Williams 
18430ff0af18SDan Williams 	rc = dev_set_name(dev, "decoder%d.%d", port->id, cxld->id);
18440ff0af18SDan Williams 	if (rc)
18450ff0af18SDan Williams 		return rc;
18460ff0af18SDan Williams 
18470ff0af18SDan Williams 	return device_add(dev);
18480ff0af18SDan Williams }
1849d17d0540SDan Williams EXPORT_SYMBOL_NS_GPL(cxl_decoder_add_locked, CXL);
1850d17d0540SDan Williams 
1851d17d0540SDan Williams /**
1852d17d0540SDan Williams  * cxl_decoder_add - Add a decoder with targets
1853e636479eSDan Williams  * @cxld: The cxl decoder allocated by cxl_<type>_decoder_alloc()
1854d17d0540SDan Williams  * @target_map: A list of downstream ports that this decoder can direct memory
1855d17d0540SDan Williams  *              traffic to. These numbers should correspond with the port number
1856d17d0540SDan Williams  *              in the PCIe Link Capabilities structure.
1857d17d0540SDan Williams  *
1858d17d0540SDan Williams  * This is the unlocked variant of cxl_decoder_add_locked().
1859d17d0540SDan Williams  * See cxl_decoder_add_locked().
1860d17d0540SDan Williams  *
1861d17d0540SDan Williams  * Context: Process context. Takes and releases the device lock of the port that
1862d17d0540SDan Williams  *	    owns the @cxld.
1863d17d0540SDan Williams  */
cxl_decoder_add(struct cxl_decoder * cxld,int * target_map)1864d17d0540SDan Williams int cxl_decoder_add(struct cxl_decoder *cxld, int *target_map)
1865d17d0540SDan Williams {
1866d17d0540SDan Williams 	struct cxl_port *port;
1867d17d0540SDan Williams 	int rc;
1868d17d0540SDan Williams 
1869d17d0540SDan Williams 	if (WARN_ON_ONCE(!cxld))
1870d17d0540SDan Williams 		return -EINVAL;
1871d17d0540SDan Williams 
1872d17d0540SDan Williams 	if (WARN_ON_ONCE(IS_ERR(cxld)))
1873d17d0540SDan Williams 		return PTR_ERR(cxld);
1874d17d0540SDan Williams 
1875d17d0540SDan Williams 	port = to_cxl_port(cxld->dev.parent);
1876d17d0540SDan Williams 
187738a34e10SDan Williams 	device_lock(&port->dev);
1878d17d0540SDan Williams 	rc = cxl_decoder_add_locked(cxld, target_map);
187938a34e10SDan Williams 	device_unlock(&port->dev);
1880d17d0540SDan Williams 
1881d17d0540SDan Williams 	return rc;
1882d17d0540SDan Williams }
18830ff0af18SDan Williams EXPORT_SYMBOL_NS_GPL(cxl_decoder_add, CXL);
18840ff0af18SDan Williams 
cxld_unregister(void * dev)18850ff0af18SDan Williams static void cxld_unregister(void *dev)
18860ff0af18SDan Williams {
1887b9686e8cSDan Williams 	struct cxl_endpoint_decoder *cxled;
1888b9686e8cSDan Williams 
1889b9686e8cSDan Williams 	if (is_endpoint_decoder(dev)) {
1890b9686e8cSDan Williams 		cxled = to_cxl_endpoint_decoder(dev);
1891b9686e8cSDan Williams 		cxl_decoder_kill_region(cxled);
1892b9686e8cSDan Williams 	}
1893b9686e8cSDan Williams 
18940ff0af18SDan Williams 	device_unregister(dev);
18950ff0af18SDan Williams }
18960ff0af18SDan Williams 
cxl_decoder_autoremove(struct device * host,struct cxl_decoder * cxld)18970ff0af18SDan Williams int cxl_decoder_autoremove(struct device *host, struct cxl_decoder *cxld)
18980ff0af18SDan Williams {
18990ff0af18SDan Williams 	return devm_add_action_or_reset(host, cxld_unregister, &cxld->dev);
19000ff0af18SDan Williams }
19010ff0af18SDan Williams EXPORT_SYMBOL_NS_GPL(cxl_decoder_autoremove, CXL);
19020ff0af18SDan Williams 
19030ff0af18SDan Williams /**
19040ff0af18SDan Williams  * __cxl_driver_register - register a driver for the cxl bus
19050ff0af18SDan Williams  * @cxl_drv: cxl driver structure to attach
19060ff0af18SDan Williams  * @owner: owning module/driver
19070ff0af18SDan Williams  * @modname: KBUILD_MODNAME for parent driver
19080ff0af18SDan Williams  */
__cxl_driver_register(struct cxl_driver * cxl_drv,struct module * owner,const char * modname)19090ff0af18SDan Williams int __cxl_driver_register(struct cxl_driver *cxl_drv, struct module *owner,
19100ff0af18SDan Williams 			  const char *modname)
19110ff0af18SDan Williams {
19120ff0af18SDan Williams 	if (!cxl_drv->probe) {
19130ff0af18SDan Williams 		pr_debug("%s ->probe() must be specified\n", modname);
19140ff0af18SDan Williams 		return -EINVAL;
19150ff0af18SDan Williams 	}
19160ff0af18SDan Williams 
19170ff0af18SDan Williams 	if (!cxl_drv->name) {
19180ff0af18SDan Williams 		pr_debug("%s ->name must be specified\n", modname);
19190ff0af18SDan Williams 		return -EINVAL;
19200ff0af18SDan Williams 	}
19210ff0af18SDan Williams 
19220ff0af18SDan Williams 	if (!cxl_drv->id) {
19230ff0af18SDan Williams 		pr_debug("%s ->id must be specified\n", modname);
19240ff0af18SDan Williams 		return -EINVAL;
19250ff0af18SDan Williams 	}
19260ff0af18SDan Williams 
19270ff0af18SDan Williams 	cxl_drv->drv.bus = &cxl_bus_type;
19280ff0af18SDan Williams 	cxl_drv->drv.owner = owner;
19290ff0af18SDan Williams 	cxl_drv->drv.mod_name = modname;
19300ff0af18SDan Williams 	cxl_drv->drv.name = cxl_drv->name;
19310ff0af18SDan Williams 
19320ff0af18SDan Williams 	return driver_register(&cxl_drv->drv);
19330ff0af18SDan Williams }
19340ff0af18SDan Williams EXPORT_SYMBOL_NS_GPL(__cxl_driver_register, CXL);
19350ff0af18SDan Williams 
cxl_driver_unregister(struct cxl_driver * cxl_drv)19360ff0af18SDan Williams void cxl_driver_unregister(struct cxl_driver *cxl_drv)
19370ff0af18SDan Williams {
19380ff0af18SDan Williams 	driver_unregister(&cxl_drv->drv);
19390ff0af18SDan Williams }
19400ff0af18SDan Williams EXPORT_SYMBOL_NS_GPL(cxl_driver_unregister, CXL);
19410ff0af18SDan Williams 
cxl_bus_uevent(const struct device * dev,struct kobj_uevent_env * env)19422a81ada3SGreg Kroah-Hartman static int cxl_bus_uevent(const struct device *dev, struct kobj_uevent_env *env)
19430ff0af18SDan Williams {
19440ff0af18SDan Williams 	return add_uevent_var(env, "MODALIAS=" CXL_MODALIAS_FMT,
19450ff0af18SDan Williams 			      cxl_device_id(dev));
19460ff0af18SDan Williams }
19470ff0af18SDan Williams 
cxl_bus_match(struct device * dev,struct device_driver * drv)19480ff0af18SDan Williams static int cxl_bus_match(struct device *dev, struct device_driver *drv)
19490ff0af18SDan Williams {
19500ff0af18SDan Williams 	return cxl_device_id(dev) == to_cxl_drv(drv)->id;
19510ff0af18SDan Williams }
19520ff0af18SDan Williams 
cxl_bus_probe(struct device * dev)19530ff0af18SDan Williams static int cxl_bus_probe(struct device *dev)
19540ff0af18SDan Williams {
19553c5b9039SDan Williams 	int rc;
19563c5b9039SDan Williams 
19573c5b9039SDan Williams 	rc = to_cxl_drv(dev->driver)->probe(dev);
195854cdbf84SBen Widawsky 	dev_dbg(dev, "probe: %d\n", rc);
19593c5b9039SDan Williams 	return rc;
19600ff0af18SDan Williams }
19610ff0af18SDan Williams 
cxl_bus_remove(struct device * dev)19620ff0af18SDan Williams static void cxl_bus_remove(struct device *dev)
19630ff0af18SDan Williams {
19640ff0af18SDan Williams 	struct cxl_driver *cxl_drv = to_cxl_drv(dev->driver);
19650ff0af18SDan Williams 
19660ff0af18SDan Williams 	if (cxl_drv->remove)
19670ff0af18SDan Williams 		cxl_drv->remove(dev);
19680ff0af18SDan Williams }
19690ff0af18SDan Williams 
19708dd2bc0fSBen Widawsky static struct workqueue_struct *cxl_bus_wq;
19718dd2bc0fSBen Widawsky 
cxl_rescan_attach(struct device * dev,void * data)1972*a9ed67f3SDan Williams static int cxl_rescan_attach(struct device *dev, void *data)
1973*a9ed67f3SDan Williams {
1974*a9ed67f3SDan Williams 	int rc = device_attach(dev);
1975*a9ed67f3SDan Williams 
1976*a9ed67f3SDan Williams 	dev_vdbg(dev, "rescan: %s\n", rc ? "attach" : "detached");
1977*a9ed67f3SDan Williams 
1978*a9ed67f3SDan Williams 	return 0;
1979*a9ed67f3SDan Williams }
1980*a9ed67f3SDan Williams 
cxl_bus_rescan_queue(struct work_struct * w)19814029c32fSDan Williams static void cxl_bus_rescan_queue(struct work_struct *w)
19828dd2bc0fSBen Widawsky {
1983*a9ed67f3SDan Williams 	bus_for_each_dev(&cxl_bus_type, NULL, NULL, cxl_rescan_attach);
19844029c32fSDan Williams }
19854029c32fSDan Williams 
cxl_bus_rescan(void)19864029c32fSDan Williams void cxl_bus_rescan(void)
19874029c32fSDan Williams {
19884029c32fSDan Williams 	static DECLARE_WORK(rescan_work, cxl_bus_rescan_queue);
19894029c32fSDan Williams 
19904029c32fSDan Williams 	queue_work(cxl_bus_wq, &rescan_work);
19918dd2bc0fSBen Widawsky }
19928dd2bc0fSBen Widawsky EXPORT_SYMBOL_NS_GPL(cxl_bus_rescan, CXL);
19938dd2bc0fSBen Widawsky 
cxl_bus_drain(void)19944029c32fSDan Williams void cxl_bus_drain(void)
19954029c32fSDan Williams {
19964029c32fSDan Williams 	drain_workqueue(cxl_bus_wq);
19974029c32fSDan Williams }
19984029c32fSDan Williams EXPORT_SYMBOL_NS_GPL(cxl_bus_drain, CXL);
19994029c32fSDan Williams 
schedule_cxl_memdev_detach(struct cxl_memdev * cxlmd)20008dd2bc0fSBen Widawsky bool schedule_cxl_memdev_detach(struct cxl_memdev *cxlmd)
20018dd2bc0fSBen Widawsky {
20028dd2bc0fSBen Widawsky 	return queue_work(cxl_bus_wq, &cxlmd->detach_work);
20038dd2bc0fSBen Widawsky }
20048dd2bc0fSBen Widawsky EXPORT_SYMBOL_NS_GPL(schedule_cxl_memdev_detach, CXL);
20058dd2bc0fSBen Widawsky 
20068dd2bc0fSBen Widawsky /* for user tooling to ensure port disable work has completed */
flush_store(const struct bus_type * bus,const char * buf,size_t count)200775cff725SGreg Kroah-Hartman static ssize_t flush_store(const struct bus_type *bus, const char *buf, size_t count)
20088dd2bc0fSBen Widawsky {
20098dd2bc0fSBen Widawsky 	if (sysfs_streq(buf, "1")) {
20108dd2bc0fSBen Widawsky 		flush_workqueue(cxl_bus_wq);
20118dd2bc0fSBen Widawsky 		return count;
20128dd2bc0fSBen Widawsky 	}
20138dd2bc0fSBen Widawsky 
20148dd2bc0fSBen Widawsky 	return -EINVAL;
20158dd2bc0fSBen Widawsky }
20168dd2bc0fSBen Widawsky 
20178dd2bc0fSBen Widawsky static BUS_ATTR_WO(flush);
20188dd2bc0fSBen Widawsky 
20198dd2bc0fSBen Widawsky static struct attribute *cxl_bus_attributes[] = {
20208dd2bc0fSBen Widawsky 	&bus_attr_flush.attr,
20218dd2bc0fSBen Widawsky 	NULL,
20228dd2bc0fSBen Widawsky };
20238dd2bc0fSBen Widawsky 
20248dd2bc0fSBen Widawsky static struct attribute_group cxl_bus_attribute_group = {
20258dd2bc0fSBen Widawsky 	.attrs = cxl_bus_attributes,
20268dd2bc0fSBen Widawsky };
20278dd2bc0fSBen Widawsky 
20288dd2bc0fSBen Widawsky static const struct attribute_group *cxl_bus_attribute_groups[] = {
20298dd2bc0fSBen Widawsky 	&cxl_bus_attribute_group,
20308dd2bc0fSBen Widawsky 	NULL,
20318dd2bc0fSBen Widawsky };
20328dd2bc0fSBen Widawsky 
20330ff0af18SDan Williams struct bus_type cxl_bus_type = {
20340ff0af18SDan Williams 	.name = "cxl",
20350ff0af18SDan Williams 	.uevent = cxl_bus_uevent,
20360ff0af18SDan Williams 	.match = cxl_bus_match,
20370ff0af18SDan Williams 	.probe = cxl_bus_probe,
20380ff0af18SDan Williams 	.remove = cxl_bus_remove,
20398dd2bc0fSBen Widawsky 	.bus_groups = cxl_bus_attribute_groups,
20400ff0af18SDan Williams };
20410ff0af18SDan Williams EXPORT_SYMBOL_NS_GPL(cxl_bus_type, CXL);
20420ff0af18SDan Williams 
20439b99ecf5SDan Williams static struct dentry *cxl_debugfs;
20449b99ecf5SDan Williams 
cxl_debugfs_create_dir(const char * dir)20459b99ecf5SDan Williams struct dentry *cxl_debugfs_create_dir(const char *dir)
20469b99ecf5SDan Williams {
20479b99ecf5SDan Williams 	return debugfs_create_dir(dir, cxl_debugfs);
20489b99ecf5SDan Williams }
2049cc2a4878SDan Williams EXPORT_SYMBOL_NS_GPL(cxl_debugfs_create_dir, CXL);
20509b99ecf5SDan Williams 
cxl_core_init(void)20510ff0af18SDan Williams static __init int cxl_core_init(void)
20520ff0af18SDan Williams {
20530ff0af18SDan Williams 	int rc;
20540ff0af18SDan Williams 
20559b99ecf5SDan Williams 	cxl_debugfs = debugfs_create_dir("cxl", NULL);
20569b99ecf5SDan Williams 
20570ff0af18SDan Williams 	cxl_mbox_init();
20580ff0af18SDan Williams 
20590ff0af18SDan Williams 	rc = cxl_memdev_init();
20600ff0af18SDan Williams 	if (rc)
20610ff0af18SDan Williams 		return rc;
20620ff0af18SDan Williams 
20638dd2bc0fSBen Widawsky 	cxl_bus_wq = alloc_ordered_workqueue("cxl_port", 0);
20648dd2bc0fSBen Widawsky 	if (!cxl_bus_wq) {
20658dd2bc0fSBen Widawsky 		rc = -ENOMEM;
20668dd2bc0fSBen Widawsky 		goto err_wq;
20678dd2bc0fSBen Widawsky 	}
20688dd2bc0fSBen Widawsky 
20690ff0af18SDan Williams 	rc = bus_register(&cxl_bus_type);
20700ff0af18SDan Williams 	if (rc)
20718dd2bc0fSBen Widawsky 		goto err_bus;
20728dd2bc0fSBen Widawsky 
20738d48817dSDan Williams 	rc = cxl_region_init();
20748d48817dSDan Williams 	if (rc)
20758d48817dSDan Williams 		goto err_region;
20768d48817dSDan Williams 
20770ff0af18SDan Williams 	return 0;
20780ff0af18SDan Williams 
20798d48817dSDan Williams err_region:
20808d48817dSDan Williams 	bus_unregister(&cxl_bus_type);
20818dd2bc0fSBen Widawsky err_bus:
20828dd2bc0fSBen Widawsky 	destroy_workqueue(cxl_bus_wq);
20838dd2bc0fSBen Widawsky err_wq:
20840ff0af18SDan Williams 	cxl_memdev_exit();
20850ff0af18SDan Williams 	return rc;
20860ff0af18SDan Williams }
20870ff0af18SDan Williams 
cxl_core_exit(void)20880ff0af18SDan Williams static void cxl_core_exit(void)
20890ff0af18SDan Williams {
20908d48817dSDan Williams 	cxl_region_exit();
20910ff0af18SDan Williams 	bus_unregister(&cxl_bus_type);
20928dd2bc0fSBen Widawsky 	destroy_workqueue(cxl_bus_wq);
20930ff0af18SDan Williams 	cxl_memdev_exit();
20949b99ecf5SDan Williams 	debugfs_remove_recursive(cxl_debugfs);
20950ff0af18SDan Williams }
20960ff0af18SDan Williams 
209709d09e04SDan Williams subsys_initcall(cxl_core_init);
20980ff0af18SDan Williams module_exit(cxl_core_exit);
20990ff0af18SDan Williams MODULE_LICENSE("GPL v2");
2100