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