1779dd20cSBen Widawsky // SPDX-License-Identifier: GPL-2.0-only 2779dd20cSBen Widawsky /* Copyright(c) 2022 Intel Corporation. All rights reserved. */ 3779dd20cSBen Widawsky #include <linux/memregion.h> 4779dd20cSBen Widawsky #include <linux/genalloc.h> 5779dd20cSBen Widawsky #include <linux/device.h> 6779dd20cSBen Widawsky #include <linux/module.h> 7779dd20cSBen Widawsky #include <linux/slab.h> 8dd5ba0ebSBen Widawsky #include <linux/uuid.h> 9779dd20cSBen Widawsky #include <linux/idr.h> 1080d10a6cSBen Widawsky #include <cxlmem.h> 11779dd20cSBen Widawsky #include <cxl.h> 12779dd20cSBen Widawsky #include "core.h" 13779dd20cSBen Widawsky 14779dd20cSBen Widawsky /** 15779dd20cSBen Widawsky * DOC: cxl core region 16779dd20cSBen Widawsky * 17779dd20cSBen Widawsky * CXL Regions represent mapped memory capacity in system physical address 18779dd20cSBen Widawsky * space. Whereas the CXL Root Decoders identify the bounds of potential CXL 19779dd20cSBen Widawsky * Memory ranges, Regions represent the active mapped capacity by the HDM 20779dd20cSBen Widawsky * Decoder Capability structures throughout the Host Bridges, Switches, and 21779dd20cSBen Widawsky * Endpoints in the topology. 22dd5ba0ebSBen Widawsky * 23dd5ba0ebSBen Widawsky * Region configuration has ordering constraints. UUID may be set at any time 24dd5ba0ebSBen Widawsky * but is only visible for persistent regions. 2580d10a6cSBen Widawsky * 1. Interleave granularity 2680d10a6cSBen Widawsky * 2. Interleave size 27b9686e8cSDan Williams * 3. Decoder targets 28779dd20cSBen Widawsky */ 29779dd20cSBen Widawsky 30dd5ba0ebSBen Widawsky /* 31dd5ba0ebSBen Widawsky * All changes to the interleave configuration occur with this lock held 32dd5ba0ebSBen Widawsky * for write. 33dd5ba0ebSBen Widawsky */ 34dd5ba0ebSBen Widawsky static DECLARE_RWSEM(cxl_region_rwsem); 35dd5ba0ebSBen Widawsky 36779dd20cSBen Widawsky static struct cxl_region *to_cxl_region(struct device *dev); 37779dd20cSBen Widawsky 38dd5ba0ebSBen Widawsky static ssize_t uuid_show(struct device *dev, struct device_attribute *attr, 39dd5ba0ebSBen Widawsky char *buf) 40dd5ba0ebSBen Widawsky { 41dd5ba0ebSBen Widawsky struct cxl_region *cxlr = to_cxl_region(dev); 42dd5ba0ebSBen Widawsky struct cxl_region_params *p = &cxlr->params; 43dd5ba0ebSBen Widawsky ssize_t rc; 44dd5ba0ebSBen Widawsky 45dd5ba0ebSBen Widawsky rc = down_read_interruptible(&cxl_region_rwsem); 46dd5ba0ebSBen Widawsky if (rc) 47dd5ba0ebSBen Widawsky return rc; 48dd5ba0ebSBen Widawsky rc = sysfs_emit(buf, "%pUb\n", &p->uuid); 49dd5ba0ebSBen Widawsky up_read(&cxl_region_rwsem); 50dd5ba0ebSBen Widawsky 51dd5ba0ebSBen Widawsky return rc; 52dd5ba0ebSBen Widawsky } 53dd5ba0ebSBen Widawsky 54dd5ba0ebSBen Widawsky static int is_dup(struct device *match, void *data) 55dd5ba0ebSBen Widawsky { 56dd5ba0ebSBen Widawsky struct cxl_region_params *p; 57dd5ba0ebSBen Widawsky struct cxl_region *cxlr; 58dd5ba0ebSBen Widawsky uuid_t *uuid = data; 59dd5ba0ebSBen Widawsky 60dd5ba0ebSBen Widawsky if (!is_cxl_region(match)) 61dd5ba0ebSBen Widawsky return 0; 62dd5ba0ebSBen Widawsky 63dd5ba0ebSBen Widawsky lockdep_assert_held(&cxl_region_rwsem); 64dd5ba0ebSBen Widawsky cxlr = to_cxl_region(match); 65dd5ba0ebSBen Widawsky p = &cxlr->params; 66dd5ba0ebSBen Widawsky 67dd5ba0ebSBen Widawsky if (uuid_equal(&p->uuid, uuid)) { 68dd5ba0ebSBen Widawsky dev_dbg(match, "already has uuid: %pUb\n", uuid); 69dd5ba0ebSBen Widawsky return -EBUSY; 70dd5ba0ebSBen Widawsky } 71dd5ba0ebSBen Widawsky 72dd5ba0ebSBen Widawsky return 0; 73dd5ba0ebSBen Widawsky } 74dd5ba0ebSBen Widawsky 75dd5ba0ebSBen Widawsky static ssize_t uuid_store(struct device *dev, struct device_attribute *attr, 76dd5ba0ebSBen Widawsky const char *buf, size_t len) 77dd5ba0ebSBen Widawsky { 78dd5ba0ebSBen Widawsky struct cxl_region *cxlr = to_cxl_region(dev); 79dd5ba0ebSBen Widawsky struct cxl_region_params *p = &cxlr->params; 80dd5ba0ebSBen Widawsky uuid_t temp; 81dd5ba0ebSBen Widawsky ssize_t rc; 82dd5ba0ebSBen Widawsky 83dd5ba0ebSBen Widawsky if (len != UUID_STRING_LEN + 1) 84dd5ba0ebSBen Widawsky return -EINVAL; 85dd5ba0ebSBen Widawsky 86dd5ba0ebSBen Widawsky rc = uuid_parse(buf, &temp); 87dd5ba0ebSBen Widawsky if (rc) 88dd5ba0ebSBen Widawsky return rc; 89dd5ba0ebSBen Widawsky 90dd5ba0ebSBen Widawsky if (uuid_is_null(&temp)) 91dd5ba0ebSBen Widawsky return -EINVAL; 92dd5ba0ebSBen Widawsky 93dd5ba0ebSBen Widawsky rc = down_write_killable(&cxl_region_rwsem); 94dd5ba0ebSBen Widawsky if (rc) 95dd5ba0ebSBen Widawsky return rc; 96dd5ba0ebSBen Widawsky 97dd5ba0ebSBen Widawsky if (uuid_equal(&p->uuid, &temp)) 98dd5ba0ebSBen Widawsky goto out; 99dd5ba0ebSBen Widawsky 100dd5ba0ebSBen Widawsky rc = -EBUSY; 101dd5ba0ebSBen Widawsky if (p->state >= CXL_CONFIG_ACTIVE) 102dd5ba0ebSBen Widawsky goto out; 103dd5ba0ebSBen Widawsky 104dd5ba0ebSBen Widawsky rc = bus_for_each_dev(&cxl_bus_type, NULL, &temp, is_dup); 105dd5ba0ebSBen Widawsky if (rc < 0) 106dd5ba0ebSBen Widawsky goto out; 107dd5ba0ebSBen Widawsky 108dd5ba0ebSBen Widawsky uuid_copy(&p->uuid, &temp); 109dd5ba0ebSBen Widawsky out: 110dd5ba0ebSBen Widawsky up_write(&cxl_region_rwsem); 111dd5ba0ebSBen Widawsky 112dd5ba0ebSBen Widawsky if (rc) 113dd5ba0ebSBen Widawsky return rc; 114dd5ba0ebSBen Widawsky return len; 115dd5ba0ebSBen Widawsky } 116dd5ba0ebSBen Widawsky static DEVICE_ATTR_RW(uuid); 117dd5ba0ebSBen Widawsky 118176baefbSDan Williams static struct cxl_region_ref *cxl_rr_load(struct cxl_port *port, 119176baefbSDan Williams struct cxl_region *cxlr) 120176baefbSDan Williams { 121176baefbSDan Williams return xa_load(&port->regions, (unsigned long)cxlr); 122176baefbSDan Williams } 123176baefbSDan Williams 124176baefbSDan Williams static int cxl_region_decode_reset(struct cxl_region *cxlr, int count) 125176baefbSDan Williams { 126176baefbSDan Williams struct cxl_region_params *p = &cxlr->params; 127176baefbSDan Williams int i; 128176baefbSDan Williams 129176baefbSDan Williams for (i = count - 1; i >= 0; i--) { 130176baefbSDan Williams struct cxl_endpoint_decoder *cxled = p->targets[i]; 131176baefbSDan Williams struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); 132176baefbSDan Williams struct cxl_port *iter = cxled_to_port(cxled); 133176baefbSDan Williams struct cxl_ep *ep; 134176baefbSDan Williams int rc; 135176baefbSDan Williams 136176baefbSDan Williams while (!is_cxl_root(to_cxl_port(iter->dev.parent))) 137176baefbSDan Williams iter = to_cxl_port(iter->dev.parent); 138176baefbSDan Williams 139176baefbSDan Williams for (ep = cxl_ep_load(iter, cxlmd); iter; 140176baefbSDan Williams iter = ep->next, ep = cxl_ep_load(iter, cxlmd)) { 141176baefbSDan Williams struct cxl_region_ref *cxl_rr; 142176baefbSDan Williams struct cxl_decoder *cxld; 143176baefbSDan Williams 144176baefbSDan Williams cxl_rr = cxl_rr_load(iter, cxlr); 145176baefbSDan Williams cxld = cxl_rr->decoder; 146176baefbSDan Williams rc = cxld->reset(cxld); 147176baefbSDan Williams if (rc) 148176baefbSDan Williams return rc; 149176baefbSDan Williams } 150176baefbSDan Williams 151176baefbSDan Williams rc = cxled->cxld.reset(&cxled->cxld); 152176baefbSDan Williams if (rc) 153176baefbSDan Williams return rc; 154176baefbSDan Williams } 155176baefbSDan Williams 156176baefbSDan Williams return 0; 157176baefbSDan Williams } 158176baefbSDan Williams 159176baefbSDan Williams static int cxl_region_decode_commit(struct cxl_region *cxlr) 160176baefbSDan Williams { 161176baefbSDan Williams struct cxl_region_params *p = &cxlr->params; 162176baefbSDan Williams int i, rc; 163176baefbSDan Williams 164176baefbSDan Williams for (i = 0; i < p->nr_targets; i++) { 165176baefbSDan Williams struct cxl_endpoint_decoder *cxled = p->targets[i]; 166176baefbSDan Williams struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); 167176baefbSDan Williams struct cxl_region_ref *cxl_rr; 168176baefbSDan Williams struct cxl_decoder *cxld; 169176baefbSDan Williams struct cxl_port *iter; 170176baefbSDan Williams struct cxl_ep *ep; 171176baefbSDan Williams 172176baefbSDan Williams /* commit bottom up */ 173176baefbSDan Williams for (iter = cxled_to_port(cxled); !is_cxl_root(iter); 174176baefbSDan Williams iter = to_cxl_port(iter->dev.parent)) { 175176baefbSDan Williams cxl_rr = cxl_rr_load(iter, cxlr); 176176baefbSDan Williams cxld = cxl_rr->decoder; 177176baefbSDan Williams rc = cxld->commit(cxld); 178176baefbSDan Williams if (rc) 179176baefbSDan Williams break; 180176baefbSDan Williams } 181176baefbSDan Williams 182176baefbSDan Williams /* success, all decoders up to the root are programmed */ 183176baefbSDan Williams if (is_cxl_root(iter)) 184176baefbSDan Williams continue; 185176baefbSDan Williams 186176baefbSDan Williams /* programming @iter failed, teardown */ 187176baefbSDan Williams for (ep = cxl_ep_load(iter, cxlmd); ep && iter; 188176baefbSDan Williams iter = ep->next, ep = cxl_ep_load(iter, cxlmd)) { 189176baefbSDan Williams cxl_rr = cxl_rr_load(iter, cxlr); 190176baefbSDan Williams cxld = cxl_rr->decoder; 191176baefbSDan Williams cxld->reset(cxld); 192176baefbSDan Williams } 193176baefbSDan Williams 194176baefbSDan Williams cxled->cxld.reset(&cxled->cxld); 195176baefbSDan Williams if (i == 0) 196176baefbSDan Williams return rc; 197176baefbSDan Williams break; 198176baefbSDan Williams } 199176baefbSDan Williams 200176baefbSDan Williams if (i >= p->nr_targets) 201176baefbSDan Williams return 0; 202176baefbSDan Williams 203176baefbSDan Williams /* undo the targets that were successfully committed */ 204176baefbSDan Williams cxl_region_decode_reset(cxlr, i); 205176baefbSDan Williams return rc; 206176baefbSDan Williams } 207176baefbSDan Williams 208176baefbSDan Williams static ssize_t commit_store(struct device *dev, struct device_attribute *attr, 209176baefbSDan Williams const char *buf, size_t len) 210176baefbSDan Williams { 211176baefbSDan Williams struct cxl_region *cxlr = to_cxl_region(dev); 212176baefbSDan Williams struct cxl_region_params *p = &cxlr->params; 213176baefbSDan Williams bool commit; 214176baefbSDan Williams ssize_t rc; 215176baefbSDan Williams 216176baefbSDan Williams rc = kstrtobool(buf, &commit); 217176baefbSDan Williams if (rc) 218176baefbSDan Williams return rc; 219176baefbSDan Williams 220176baefbSDan Williams rc = down_write_killable(&cxl_region_rwsem); 221176baefbSDan Williams if (rc) 222176baefbSDan Williams return rc; 223176baefbSDan Williams 224176baefbSDan Williams /* Already in the requested state? */ 225176baefbSDan Williams if (commit && p->state >= CXL_CONFIG_COMMIT) 226176baefbSDan Williams goto out; 227176baefbSDan Williams if (!commit && p->state < CXL_CONFIG_COMMIT) 228176baefbSDan Williams goto out; 229176baefbSDan Williams 230176baefbSDan Williams /* Not ready to commit? */ 231176baefbSDan Williams if (commit && p->state < CXL_CONFIG_ACTIVE) { 232176baefbSDan Williams rc = -ENXIO; 233176baefbSDan Williams goto out; 234176baefbSDan Williams } 235176baefbSDan Williams 236176baefbSDan Williams if (commit) 237176baefbSDan Williams rc = cxl_region_decode_commit(cxlr); 238176baefbSDan Williams else { 239176baefbSDan Williams p->state = CXL_CONFIG_RESET_PENDING; 240176baefbSDan Williams up_write(&cxl_region_rwsem); 241176baefbSDan Williams device_release_driver(&cxlr->dev); 242176baefbSDan Williams down_write(&cxl_region_rwsem); 243176baefbSDan Williams 244176baefbSDan Williams /* 245176baefbSDan Williams * The lock was dropped, so need to revalidate that the reset is 246176baefbSDan Williams * still pending. 247176baefbSDan Williams */ 248176baefbSDan Williams if (p->state == CXL_CONFIG_RESET_PENDING) 249176baefbSDan Williams rc = cxl_region_decode_reset(cxlr, p->interleave_ways); 250176baefbSDan Williams } 251176baefbSDan Williams 252176baefbSDan Williams if (rc) 253176baefbSDan Williams goto out; 254176baefbSDan Williams 255176baefbSDan Williams if (commit) 256176baefbSDan Williams p->state = CXL_CONFIG_COMMIT; 257176baefbSDan Williams else if (p->state == CXL_CONFIG_RESET_PENDING) 258176baefbSDan Williams p->state = CXL_CONFIG_ACTIVE; 259176baefbSDan Williams 260176baefbSDan Williams out: 261176baefbSDan Williams up_write(&cxl_region_rwsem); 262176baefbSDan Williams 263176baefbSDan Williams if (rc) 264176baefbSDan Williams return rc; 265176baefbSDan Williams return len; 266176baefbSDan Williams } 267176baefbSDan Williams 268176baefbSDan Williams static ssize_t commit_show(struct device *dev, struct device_attribute *attr, 269176baefbSDan Williams char *buf) 270176baefbSDan Williams { 271176baefbSDan Williams struct cxl_region *cxlr = to_cxl_region(dev); 272176baefbSDan Williams struct cxl_region_params *p = &cxlr->params; 273176baefbSDan Williams ssize_t rc; 274176baefbSDan Williams 275176baefbSDan Williams rc = down_read_interruptible(&cxl_region_rwsem); 276176baefbSDan Williams if (rc) 277176baefbSDan Williams return rc; 278176baefbSDan Williams rc = sysfs_emit(buf, "%d\n", p->state >= CXL_CONFIG_COMMIT); 279176baefbSDan Williams up_read(&cxl_region_rwsem); 280176baefbSDan Williams 281176baefbSDan Williams return rc; 282176baefbSDan Williams } 283176baefbSDan Williams static DEVICE_ATTR_RW(commit); 284176baefbSDan Williams 285dd5ba0ebSBen Widawsky static umode_t cxl_region_visible(struct kobject *kobj, struct attribute *a, 286dd5ba0ebSBen Widawsky int n) 287dd5ba0ebSBen Widawsky { 288dd5ba0ebSBen Widawsky struct device *dev = kobj_to_dev(kobj); 289dd5ba0ebSBen Widawsky struct cxl_region *cxlr = to_cxl_region(dev); 290dd5ba0ebSBen Widawsky 291dd5ba0ebSBen Widawsky if (a == &dev_attr_uuid.attr && cxlr->mode != CXL_DECODER_PMEM) 292dd5ba0ebSBen Widawsky return 0; 293dd5ba0ebSBen Widawsky return a->mode; 294dd5ba0ebSBen Widawsky } 295dd5ba0ebSBen Widawsky 29680d10a6cSBen Widawsky static ssize_t interleave_ways_show(struct device *dev, 29780d10a6cSBen Widawsky struct device_attribute *attr, char *buf) 29880d10a6cSBen Widawsky { 29980d10a6cSBen Widawsky struct cxl_region *cxlr = to_cxl_region(dev); 30080d10a6cSBen Widawsky struct cxl_region_params *p = &cxlr->params; 30180d10a6cSBen Widawsky ssize_t rc; 30280d10a6cSBen Widawsky 30380d10a6cSBen Widawsky rc = down_read_interruptible(&cxl_region_rwsem); 30480d10a6cSBen Widawsky if (rc) 30580d10a6cSBen Widawsky return rc; 30680d10a6cSBen Widawsky rc = sysfs_emit(buf, "%d\n", p->interleave_ways); 30780d10a6cSBen Widawsky up_read(&cxl_region_rwsem); 30880d10a6cSBen Widawsky 30980d10a6cSBen Widawsky return rc; 31080d10a6cSBen Widawsky } 31180d10a6cSBen Widawsky 312b9686e8cSDan Williams static const struct attribute_group *get_cxl_region_target_group(void); 313b9686e8cSDan Williams 31480d10a6cSBen Widawsky static ssize_t interleave_ways_store(struct device *dev, 31580d10a6cSBen Widawsky struct device_attribute *attr, 31680d10a6cSBen Widawsky const char *buf, size_t len) 31780d10a6cSBen Widawsky { 31880d10a6cSBen Widawsky struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev->parent); 31980d10a6cSBen Widawsky struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; 32080d10a6cSBen Widawsky struct cxl_region *cxlr = to_cxl_region(dev); 32180d10a6cSBen Widawsky struct cxl_region_params *p = &cxlr->params; 322b9686e8cSDan Williams int rc, val, save; 32380d10a6cSBen Widawsky u8 iw; 32480d10a6cSBen Widawsky 32580d10a6cSBen Widawsky rc = kstrtoint(buf, 0, &val); 32680d10a6cSBen Widawsky if (rc) 32780d10a6cSBen Widawsky return rc; 32880d10a6cSBen Widawsky 32980d10a6cSBen Widawsky rc = ways_to_cxl(val, &iw); 33080d10a6cSBen Widawsky if (rc) 33180d10a6cSBen Widawsky return rc; 33280d10a6cSBen Widawsky 33380d10a6cSBen Widawsky /* 33480d10a6cSBen Widawsky * Even for x3, x9, and x12 interleaves the region interleave must be a 33580d10a6cSBen Widawsky * power of 2 multiple of the host bridge interleave. 33680d10a6cSBen Widawsky */ 33780d10a6cSBen Widawsky if (!is_power_of_2(val / cxld->interleave_ways) || 33880d10a6cSBen Widawsky (val % cxld->interleave_ways)) { 33980d10a6cSBen Widawsky dev_dbg(&cxlr->dev, "invalid interleave: %d\n", val); 34080d10a6cSBen Widawsky return -EINVAL; 34180d10a6cSBen Widawsky } 34280d10a6cSBen Widawsky 34380d10a6cSBen Widawsky rc = down_write_killable(&cxl_region_rwsem); 34480d10a6cSBen Widawsky if (rc) 34580d10a6cSBen Widawsky return rc; 34680d10a6cSBen Widawsky if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) { 34780d10a6cSBen Widawsky rc = -EBUSY; 34880d10a6cSBen Widawsky goto out; 34980d10a6cSBen Widawsky } 35080d10a6cSBen Widawsky 351b9686e8cSDan Williams save = p->interleave_ways; 35280d10a6cSBen Widawsky p->interleave_ways = val; 353b9686e8cSDan Williams rc = sysfs_update_group(&cxlr->dev.kobj, get_cxl_region_target_group()); 354b9686e8cSDan Williams if (rc) 355b9686e8cSDan Williams p->interleave_ways = save; 35680d10a6cSBen Widawsky out: 35780d10a6cSBen Widawsky up_write(&cxl_region_rwsem); 35880d10a6cSBen Widawsky if (rc) 35980d10a6cSBen Widawsky return rc; 36080d10a6cSBen Widawsky return len; 36180d10a6cSBen Widawsky } 36280d10a6cSBen Widawsky static DEVICE_ATTR_RW(interleave_ways); 36380d10a6cSBen Widawsky 36480d10a6cSBen Widawsky static ssize_t interleave_granularity_show(struct device *dev, 36580d10a6cSBen Widawsky struct device_attribute *attr, 36680d10a6cSBen Widawsky char *buf) 36780d10a6cSBen Widawsky { 36880d10a6cSBen Widawsky struct cxl_region *cxlr = to_cxl_region(dev); 36980d10a6cSBen Widawsky struct cxl_region_params *p = &cxlr->params; 37080d10a6cSBen Widawsky ssize_t rc; 37180d10a6cSBen Widawsky 37280d10a6cSBen Widawsky rc = down_read_interruptible(&cxl_region_rwsem); 37380d10a6cSBen Widawsky if (rc) 37480d10a6cSBen Widawsky return rc; 37580d10a6cSBen Widawsky rc = sysfs_emit(buf, "%d\n", p->interleave_granularity); 37680d10a6cSBen Widawsky up_read(&cxl_region_rwsem); 37780d10a6cSBen Widawsky 37880d10a6cSBen Widawsky return rc; 37980d10a6cSBen Widawsky } 38080d10a6cSBen Widawsky 38180d10a6cSBen Widawsky static ssize_t interleave_granularity_store(struct device *dev, 38280d10a6cSBen Widawsky struct device_attribute *attr, 38380d10a6cSBen Widawsky const char *buf, size_t len) 38480d10a6cSBen Widawsky { 38580d10a6cSBen Widawsky struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev->parent); 38680d10a6cSBen Widawsky struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; 38780d10a6cSBen Widawsky struct cxl_region *cxlr = to_cxl_region(dev); 38880d10a6cSBen Widawsky struct cxl_region_params *p = &cxlr->params; 38980d10a6cSBen Widawsky int rc, val; 39080d10a6cSBen Widawsky u16 ig; 39180d10a6cSBen Widawsky 39280d10a6cSBen Widawsky rc = kstrtoint(buf, 0, &val); 39380d10a6cSBen Widawsky if (rc) 39480d10a6cSBen Widawsky return rc; 39580d10a6cSBen Widawsky 39680d10a6cSBen Widawsky rc = granularity_to_cxl(val, &ig); 39780d10a6cSBen Widawsky if (rc) 39880d10a6cSBen Widawsky return rc; 39980d10a6cSBen Widawsky 40080d10a6cSBen Widawsky /* 40180d10a6cSBen Widawsky * Disallow region granularity less than root granularity to 40280d10a6cSBen Widawsky * simplify the implementation. Otherwise, region's with a 40380d10a6cSBen Widawsky * granularity less than the root interleave result in needing 40480d10a6cSBen Widawsky * multiple endpoints to support a single slot in the 40580d10a6cSBen Widawsky * interleave. 40680d10a6cSBen Widawsky */ 40780d10a6cSBen Widawsky if (val < cxld->interleave_granularity) 40880d10a6cSBen Widawsky return -EINVAL; 40980d10a6cSBen Widawsky 41080d10a6cSBen Widawsky rc = down_write_killable(&cxl_region_rwsem); 41180d10a6cSBen Widawsky if (rc) 41280d10a6cSBen Widawsky return rc; 41380d10a6cSBen Widawsky if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) { 41480d10a6cSBen Widawsky rc = -EBUSY; 41580d10a6cSBen Widawsky goto out; 41680d10a6cSBen Widawsky } 41780d10a6cSBen Widawsky 41880d10a6cSBen Widawsky p->interleave_granularity = val; 41980d10a6cSBen Widawsky out: 42080d10a6cSBen Widawsky up_write(&cxl_region_rwsem); 42180d10a6cSBen Widawsky if (rc) 42280d10a6cSBen Widawsky return rc; 42380d10a6cSBen Widawsky return len; 42480d10a6cSBen Widawsky } 42580d10a6cSBen Widawsky static DEVICE_ATTR_RW(interleave_granularity); 42680d10a6cSBen Widawsky 42723a22cd1SDan Williams static ssize_t resource_show(struct device *dev, struct device_attribute *attr, 42823a22cd1SDan Williams char *buf) 42923a22cd1SDan Williams { 43023a22cd1SDan Williams struct cxl_region *cxlr = to_cxl_region(dev); 43123a22cd1SDan Williams struct cxl_region_params *p = &cxlr->params; 43223a22cd1SDan Williams u64 resource = -1ULL; 43323a22cd1SDan Williams ssize_t rc; 43423a22cd1SDan Williams 43523a22cd1SDan Williams rc = down_read_interruptible(&cxl_region_rwsem); 43623a22cd1SDan Williams if (rc) 43723a22cd1SDan Williams return rc; 43823a22cd1SDan Williams if (p->res) 43923a22cd1SDan Williams resource = p->res->start; 44023a22cd1SDan Williams rc = sysfs_emit(buf, "%#llx\n", resource); 44123a22cd1SDan Williams up_read(&cxl_region_rwsem); 44223a22cd1SDan Williams 44323a22cd1SDan Williams return rc; 44423a22cd1SDan Williams } 44523a22cd1SDan Williams static DEVICE_ATTR_RO(resource); 44623a22cd1SDan Williams 44723a22cd1SDan Williams static int alloc_hpa(struct cxl_region *cxlr, resource_size_t size) 44823a22cd1SDan Williams { 44923a22cd1SDan Williams struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent); 45023a22cd1SDan Williams struct cxl_region_params *p = &cxlr->params; 45123a22cd1SDan Williams struct resource *res; 45223a22cd1SDan Williams u32 remainder = 0; 45323a22cd1SDan Williams 45423a22cd1SDan Williams lockdep_assert_held_write(&cxl_region_rwsem); 45523a22cd1SDan Williams 45623a22cd1SDan Williams /* Nothing to do... */ 45723a22cd1SDan Williams if (p->res && resource_size(res) == size) 45823a22cd1SDan Williams return 0; 45923a22cd1SDan Williams 46023a22cd1SDan Williams /* To change size the old size must be freed first */ 46123a22cd1SDan Williams if (p->res) 46223a22cd1SDan Williams return -EBUSY; 46323a22cd1SDan Williams 46423a22cd1SDan Williams if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) 46523a22cd1SDan Williams return -EBUSY; 46623a22cd1SDan Williams 46723a22cd1SDan Williams /* ways, granularity and uuid (if PMEM) need to be set before HPA */ 46823a22cd1SDan Williams if (!p->interleave_ways || !p->interleave_granularity || 46923a22cd1SDan Williams (cxlr->mode == CXL_DECODER_PMEM && uuid_is_null(&p->uuid))) 47023a22cd1SDan Williams return -ENXIO; 47123a22cd1SDan Williams 47223a22cd1SDan Williams div_u64_rem(size, SZ_256M * p->interleave_ways, &remainder); 47323a22cd1SDan Williams if (remainder) 47423a22cd1SDan Williams return -EINVAL; 47523a22cd1SDan Williams 47623a22cd1SDan Williams res = alloc_free_mem_region(cxlrd->res, size, SZ_256M, 47723a22cd1SDan Williams dev_name(&cxlr->dev)); 47823a22cd1SDan Williams if (IS_ERR(res)) { 47923a22cd1SDan Williams dev_dbg(&cxlr->dev, "failed to allocate HPA: %ld\n", 48023a22cd1SDan Williams PTR_ERR(res)); 48123a22cd1SDan Williams return PTR_ERR(res); 48223a22cd1SDan Williams } 48323a22cd1SDan Williams 48423a22cd1SDan Williams p->res = res; 48523a22cd1SDan Williams p->state = CXL_CONFIG_INTERLEAVE_ACTIVE; 48623a22cd1SDan Williams 48723a22cd1SDan Williams return 0; 48823a22cd1SDan Williams } 48923a22cd1SDan Williams 49023a22cd1SDan Williams static void cxl_region_iomem_release(struct cxl_region *cxlr) 49123a22cd1SDan Williams { 49223a22cd1SDan Williams struct cxl_region_params *p = &cxlr->params; 49323a22cd1SDan Williams 49423a22cd1SDan Williams if (device_is_registered(&cxlr->dev)) 49523a22cd1SDan Williams lockdep_assert_held_write(&cxl_region_rwsem); 49623a22cd1SDan Williams if (p->res) { 49723a22cd1SDan Williams remove_resource(p->res); 49823a22cd1SDan Williams kfree(p->res); 49923a22cd1SDan Williams p->res = NULL; 50023a22cd1SDan Williams } 50123a22cd1SDan Williams } 50223a22cd1SDan Williams 50323a22cd1SDan Williams static int free_hpa(struct cxl_region *cxlr) 50423a22cd1SDan Williams { 50523a22cd1SDan Williams struct cxl_region_params *p = &cxlr->params; 50623a22cd1SDan Williams 50723a22cd1SDan Williams lockdep_assert_held_write(&cxl_region_rwsem); 50823a22cd1SDan Williams 50923a22cd1SDan Williams if (!p->res) 51023a22cd1SDan Williams return 0; 51123a22cd1SDan Williams 51223a22cd1SDan Williams if (p->state >= CXL_CONFIG_ACTIVE) 51323a22cd1SDan Williams return -EBUSY; 51423a22cd1SDan Williams 51523a22cd1SDan Williams cxl_region_iomem_release(cxlr); 51623a22cd1SDan Williams p->state = CXL_CONFIG_IDLE; 51723a22cd1SDan Williams return 0; 51823a22cd1SDan Williams } 51923a22cd1SDan Williams 52023a22cd1SDan Williams static ssize_t size_store(struct device *dev, struct device_attribute *attr, 52123a22cd1SDan Williams const char *buf, size_t len) 52223a22cd1SDan Williams { 52323a22cd1SDan Williams struct cxl_region *cxlr = to_cxl_region(dev); 52423a22cd1SDan Williams u64 val; 52523a22cd1SDan Williams int rc; 52623a22cd1SDan Williams 52723a22cd1SDan Williams rc = kstrtou64(buf, 0, &val); 52823a22cd1SDan Williams if (rc) 52923a22cd1SDan Williams return rc; 53023a22cd1SDan Williams 53123a22cd1SDan Williams rc = down_write_killable(&cxl_region_rwsem); 53223a22cd1SDan Williams if (rc) 53323a22cd1SDan Williams return rc; 53423a22cd1SDan Williams 53523a22cd1SDan Williams if (val) 53623a22cd1SDan Williams rc = alloc_hpa(cxlr, val); 53723a22cd1SDan Williams else 53823a22cd1SDan Williams rc = free_hpa(cxlr); 53923a22cd1SDan Williams up_write(&cxl_region_rwsem); 54023a22cd1SDan Williams 54123a22cd1SDan Williams if (rc) 54223a22cd1SDan Williams return rc; 54323a22cd1SDan Williams 54423a22cd1SDan Williams return len; 54523a22cd1SDan Williams } 54623a22cd1SDan Williams 54723a22cd1SDan Williams static ssize_t size_show(struct device *dev, struct device_attribute *attr, 54823a22cd1SDan Williams char *buf) 54923a22cd1SDan Williams { 55023a22cd1SDan Williams struct cxl_region *cxlr = to_cxl_region(dev); 55123a22cd1SDan Williams struct cxl_region_params *p = &cxlr->params; 55223a22cd1SDan Williams u64 size = 0; 55323a22cd1SDan Williams ssize_t rc; 55423a22cd1SDan Williams 55523a22cd1SDan Williams rc = down_read_interruptible(&cxl_region_rwsem); 55623a22cd1SDan Williams if (rc) 55723a22cd1SDan Williams return rc; 55823a22cd1SDan Williams if (p->res) 55923a22cd1SDan Williams size = resource_size(p->res); 56023a22cd1SDan Williams rc = sysfs_emit(buf, "%#llx\n", size); 56123a22cd1SDan Williams up_read(&cxl_region_rwsem); 56223a22cd1SDan Williams 56323a22cd1SDan Williams return rc; 56423a22cd1SDan Williams } 56523a22cd1SDan Williams static DEVICE_ATTR_RW(size); 56623a22cd1SDan Williams 567dd5ba0ebSBen Widawsky static struct attribute *cxl_region_attrs[] = { 568dd5ba0ebSBen Widawsky &dev_attr_uuid.attr, 569176baefbSDan Williams &dev_attr_commit.attr, 57080d10a6cSBen Widawsky &dev_attr_interleave_ways.attr, 57180d10a6cSBen Widawsky &dev_attr_interleave_granularity.attr, 57223a22cd1SDan Williams &dev_attr_resource.attr, 57323a22cd1SDan Williams &dev_attr_size.attr, 574dd5ba0ebSBen Widawsky NULL, 575dd5ba0ebSBen Widawsky }; 576dd5ba0ebSBen Widawsky 577dd5ba0ebSBen Widawsky static const struct attribute_group cxl_region_group = { 578dd5ba0ebSBen Widawsky .attrs = cxl_region_attrs, 579dd5ba0ebSBen Widawsky .is_visible = cxl_region_visible, 580dd5ba0ebSBen Widawsky }; 581dd5ba0ebSBen Widawsky 582b9686e8cSDan Williams static size_t show_targetN(struct cxl_region *cxlr, char *buf, int pos) 583b9686e8cSDan Williams { 584b9686e8cSDan Williams struct cxl_region_params *p = &cxlr->params; 585b9686e8cSDan Williams struct cxl_endpoint_decoder *cxled; 586b9686e8cSDan Williams int rc; 587b9686e8cSDan Williams 588b9686e8cSDan Williams rc = down_read_interruptible(&cxl_region_rwsem); 589b9686e8cSDan Williams if (rc) 590b9686e8cSDan Williams return rc; 591b9686e8cSDan Williams 592b9686e8cSDan Williams if (pos >= p->interleave_ways) { 593b9686e8cSDan Williams dev_dbg(&cxlr->dev, "position %d out of range %d\n", pos, 594b9686e8cSDan Williams p->interleave_ways); 595b9686e8cSDan Williams rc = -ENXIO; 596b9686e8cSDan Williams goto out; 597b9686e8cSDan Williams } 598b9686e8cSDan Williams 599b9686e8cSDan Williams cxled = p->targets[pos]; 600b9686e8cSDan Williams if (!cxled) 601b9686e8cSDan Williams rc = sysfs_emit(buf, "\n"); 602b9686e8cSDan Williams else 603b9686e8cSDan Williams rc = sysfs_emit(buf, "%s\n", dev_name(&cxled->cxld.dev)); 604b9686e8cSDan Williams out: 605b9686e8cSDan Williams up_read(&cxl_region_rwsem); 606b9686e8cSDan Williams 607b9686e8cSDan Williams return rc; 608b9686e8cSDan Williams } 609b9686e8cSDan Williams 610384e624bSDan Williams static int match_free_decoder(struct device *dev, void *data) 611384e624bSDan Williams { 612384e624bSDan Williams struct cxl_decoder *cxld; 613384e624bSDan Williams int *id = data; 614384e624bSDan Williams 615384e624bSDan Williams if (!is_switch_decoder(dev)) 616384e624bSDan Williams return 0; 617384e624bSDan Williams 618384e624bSDan Williams cxld = to_cxl_decoder(dev); 619384e624bSDan Williams 620384e624bSDan Williams /* enforce ordered allocation */ 621384e624bSDan Williams if (cxld->id != *id) 622384e624bSDan Williams return 0; 623384e624bSDan Williams 624384e624bSDan Williams if (!cxld->region) 625384e624bSDan Williams return 1; 626384e624bSDan Williams 627384e624bSDan Williams (*id)++; 628384e624bSDan Williams 629384e624bSDan Williams return 0; 630384e624bSDan Williams } 631384e624bSDan Williams 632384e624bSDan Williams static struct cxl_decoder *cxl_region_find_decoder(struct cxl_port *port, 633384e624bSDan Williams struct cxl_region *cxlr) 634384e624bSDan Williams { 635384e624bSDan Williams struct device *dev; 636384e624bSDan Williams int id = 0; 637384e624bSDan Williams 638384e624bSDan Williams dev = device_find_child(&port->dev, &id, match_free_decoder); 639384e624bSDan Williams if (!dev) 640384e624bSDan Williams return NULL; 641b9686e8cSDan Williams /* 642384e624bSDan Williams * This decoder is pinned registered as long as the endpoint decoder is 643384e624bSDan Williams * registered, and endpoint decoder unregistration holds the 644384e624bSDan Williams * cxl_region_rwsem over unregister events, so no need to hold on to 645384e624bSDan Williams * this extra reference. 646b9686e8cSDan Williams */ 647384e624bSDan Williams put_device(dev); 648384e624bSDan Williams return to_cxl_decoder(dev); 649384e624bSDan Williams } 650384e624bSDan Williams 651384e624bSDan Williams static struct cxl_region_ref *alloc_region_ref(struct cxl_port *port, 652384e624bSDan Williams struct cxl_region *cxlr) 653384e624bSDan Williams { 654384e624bSDan Williams struct cxl_region_ref *cxl_rr; 655384e624bSDan Williams int rc; 656384e624bSDan Williams 657384e624bSDan Williams cxl_rr = kzalloc(sizeof(*cxl_rr), GFP_KERNEL); 658384e624bSDan Williams if (!cxl_rr) 659384e624bSDan Williams return NULL; 660384e624bSDan Williams cxl_rr->port = port; 661384e624bSDan Williams cxl_rr->region = cxlr; 66227b3f8d1SDan Williams cxl_rr->nr_targets = 1; 663384e624bSDan Williams xa_init(&cxl_rr->endpoints); 664384e624bSDan Williams 665384e624bSDan Williams rc = xa_insert(&port->regions, (unsigned long)cxlr, cxl_rr, GFP_KERNEL); 666384e624bSDan Williams if (rc) { 667384e624bSDan Williams dev_dbg(&cxlr->dev, 668384e624bSDan Williams "%s: failed to track region reference: %d\n", 669384e624bSDan Williams dev_name(&port->dev), rc); 670384e624bSDan Williams kfree(cxl_rr); 671384e624bSDan Williams return NULL; 672384e624bSDan Williams } 673384e624bSDan Williams 674384e624bSDan Williams return cxl_rr; 675384e624bSDan Williams } 676384e624bSDan Williams 677384e624bSDan Williams static void free_region_ref(struct cxl_region_ref *cxl_rr) 678384e624bSDan Williams { 679384e624bSDan Williams struct cxl_port *port = cxl_rr->port; 680384e624bSDan Williams struct cxl_region *cxlr = cxl_rr->region; 681384e624bSDan Williams struct cxl_decoder *cxld = cxl_rr->decoder; 682384e624bSDan Williams 683384e624bSDan Williams dev_WARN_ONCE(&cxlr->dev, cxld->region != cxlr, "region mismatch\n"); 684384e624bSDan Williams if (cxld->region == cxlr) { 685384e624bSDan Williams cxld->region = NULL; 686384e624bSDan Williams put_device(&cxlr->dev); 687384e624bSDan Williams } 688384e624bSDan Williams 689384e624bSDan Williams xa_erase(&port->regions, (unsigned long)cxlr); 690384e624bSDan Williams xa_destroy(&cxl_rr->endpoints); 691384e624bSDan Williams kfree(cxl_rr); 692384e624bSDan Williams } 693384e624bSDan Williams 694384e624bSDan Williams static int cxl_rr_ep_add(struct cxl_region_ref *cxl_rr, 695384e624bSDan Williams struct cxl_endpoint_decoder *cxled) 696384e624bSDan Williams { 697384e624bSDan Williams int rc; 698384e624bSDan Williams struct cxl_port *port = cxl_rr->port; 699384e624bSDan Williams struct cxl_region *cxlr = cxl_rr->region; 700384e624bSDan Williams struct cxl_decoder *cxld = cxl_rr->decoder; 701384e624bSDan Williams struct cxl_ep *ep = cxl_ep_load(port, cxled_to_memdev(cxled)); 702384e624bSDan Williams 70327b3f8d1SDan Williams if (ep) { 704384e624bSDan Williams rc = xa_insert(&cxl_rr->endpoints, (unsigned long)cxled, ep, 705384e624bSDan Williams GFP_KERNEL); 706384e624bSDan Williams if (rc) 707384e624bSDan Williams return rc; 70827b3f8d1SDan Williams } 709384e624bSDan Williams cxl_rr->nr_eps++; 710384e624bSDan Williams 711384e624bSDan Williams if (!cxld->region) { 712384e624bSDan Williams cxld->region = cxlr; 713384e624bSDan Williams get_device(&cxlr->dev); 714384e624bSDan Williams } 715384e624bSDan Williams 716384e624bSDan Williams return 0; 717384e624bSDan Williams } 718384e624bSDan Williams 719384e624bSDan Williams /** 720384e624bSDan Williams * cxl_port_attach_region() - track a region's interest in a port by endpoint 721384e624bSDan Williams * @port: port to add a new region reference 'struct cxl_region_ref' 722384e624bSDan Williams * @cxlr: region to attach to @port 723384e624bSDan Williams * @cxled: endpoint decoder used to create or further pin a region reference 724384e624bSDan Williams * @pos: interleave position of @cxled in @cxlr 725384e624bSDan Williams * 726384e624bSDan Williams * The attach event is an opportunity to validate CXL decode setup 727384e624bSDan Williams * constraints and record metadata needed for programming HDM decoders, 728384e624bSDan Williams * in particular decoder target lists. 729384e624bSDan Williams * 730384e624bSDan Williams * The steps are: 731384e624bSDan Williams * - validate that there are no other regions with a higher HPA already 732384e624bSDan Williams * associated with @port 733384e624bSDan Williams * - establish a region reference if one is not already present 734384e624bSDan Williams * - additionally allocate a decoder instance that will host @cxlr on 735384e624bSDan Williams * @port 736384e624bSDan Williams * - pin the region reference by the endpoint 737384e624bSDan Williams * - account for how many entries in @port's target list are needed to 738384e624bSDan Williams * cover all of the added endpoints. 739384e624bSDan Williams */ 740384e624bSDan Williams static int cxl_port_attach_region(struct cxl_port *port, 741384e624bSDan Williams struct cxl_region *cxlr, 742384e624bSDan Williams struct cxl_endpoint_decoder *cxled, int pos) 743384e624bSDan Williams { 744384e624bSDan Williams struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); 745384e624bSDan Williams struct cxl_ep *ep = cxl_ep_load(port, cxlmd); 746384e624bSDan Williams struct cxl_region_ref *cxl_rr = NULL, *iter; 747384e624bSDan Williams struct cxl_region_params *p = &cxlr->params; 748384e624bSDan Williams struct cxl_decoder *cxld = NULL; 749384e624bSDan Williams unsigned long index; 750384e624bSDan Williams int rc = -EBUSY; 751384e624bSDan Williams 752384e624bSDan Williams lockdep_assert_held_write(&cxl_region_rwsem); 753384e624bSDan Williams 754384e624bSDan Williams xa_for_each(&port->regions, index, iter) { 755384e624bSDan Williams struct cxl_region_params *ip = &iter->region->params; 756384e624bSDan Williams 757384e624bSDan Williams if (iter->region == cxlr) 758384e624bSDan Williams cxl_rr = iter; 759384e624bSDan Williams if (ip->res->start > p->res->start) { 760384e624bSDan Williams dev_dbg(&cxlr->dev, 761384e624bSDan Williams "%s: HPA order violation %s:%pr vs %pr\n", 762384e624bSDan Williams dev_name(&port->dev), 763384e624bSDan Williams dev_name(&iter->region->dev), ip->res, p->res); 764384e624bSDan Williams return -EBUSY; 765384e624bSDan Williams } 766384e624bSDan Williams } 767384e624bSDan Williams 768384e624bSDan Williams if (cxl_rr) { 769384e624bSDan Williams struct cxl_ep *ep_iter; 770384e624bSDan Williams int found = 0; 771384e624bSDan Williams 772384e624bSDan Williams cxld = cxl_rr->decoder; 773384e624bSDan Williams xa_for_each(&cxl_rr->endpoints, index, ep_iter) { 774384e624bSDan Williams if (ep_iter == ep) 775384e624bSDan Williams continue; 776384e624bSDan Williams if (ep_iter->next == ep->next) { 777384e624bSDan Williams found++; 778384e624bSDan Williams break; 779384e624bSDan Williams } 780384e624bSDan Williams } 781384e624bSDan Williams 782384e624bSDan Williams /* 783384e624bSDan Williams * If this is a new target or if this port is direct connected 784384e624bSDan Williams * to this endpoint then add to the target count. 785384e624bSDan Williams */ 786384e624bSDan Williams if (!found || !ep->next) 787384e624bSDan Williams cxl_rr->nr_targets++; 788384e624bSDan Williams } else { 789384e624bSDan Williams cxl_rr = alloc_region_ref(port, cxlr); 790384e624bSDan Williams if (!cxl_rr) { 791384e624bSDan Williams dev_dbg(&cxlr->dev, 792384e624bSDan Williams "%s: failed to allocate region reference\n", 793384e624bSDan Williams dev_name(&port->dev)); 794384e624bSDan Williams return -ENOMEM; 795384e624bSDan Williams } 796384e624bSDan Williams } 797384e624bSDan Williams 798384e624bSDan Williams if (!cxld) { 799384e624bSDan Williams if (port == cxled_to_port(cxled)) 800384e624bSDan Williams cxld = &cxled->cxld; 801384e624bSDan Williams else 802384e624bSDan Williams cxld = cxl_region_find_decoder(port, cxlr); 803384e624bSDan Williams if (!cxld) { 804384e624bSDan Williams dev_dbg(&cxlr->dev, "%s: no decoder available\n", 805384e624bSDan Williams dev_name(&port->dev)); 806384e624bSDan Williams goto out_erase; 807384e624bSDan Williams } 808384e624bSDan Williams 809384e624bSDan Williams if (cxld->region) { 810384e624bSDan Williams dev_dbg(&cxlr->dev, "%s: %s already attached to %s\n", 811384e624bSDan Williams dev_name(&port->dev), dev_name(&cxld->dev), 812384e624bSDan Williams dev_name(&cxld->region->dev)); 813384e624bSDan Williams rc = -EBUSY; 814384e624bSDan Williams goto out_erase; 815384e624bSDan Williams } 816384e624bSDan Williams 817384e624bSDan Williams cxl_rr->decoder = cxld; 818384e624bSDan Williams } 819384e624bSDan Williams 820384e624bSDan Williams rc = cxl_rr_ep_add(cxl_rr, cxled); 821384e624bSDan Williams if (rc) { 822384e624bSDan Williams dev_dbg(&cxlr->dev, 823384e624bSDan Williams "%s: failed to track endpoint %s:%s reference\n", 824384e624bSDan Williams dev_name(&port->dev), dev_name(&cxlmd->dev), 825384e624bSDan Williams dev_name(&cxld->dev)); 826384e624bSDan Williams goto out_erase; 827384e624bSDan Williams } 828384e624bSDan Williams 82927b3f8d1SDan Williams dev_dbg(&cxlr->dev, 83027b3f8d1SDan Williams "%s:%s %s add: %s:%s @ %d next: %s nr_eps: %d nr_targets: %d\n", 83127b3f8d1SDan Williams dev_name(port->uport), dev_name(&port->dev), 83227b3f8d1SDan Williams dev_name(&cxld->dev), dev_name(&cxlmd->dev), 83327b3f8d1SDan Williams dev_name(&cxled->cxld.dev), pos, 83427b3f8d1SDan Williams ep ? ep->next ? dev_name(ep->next->uport) : 83527b3f8d1SDan Williams dev_name(&cxlmd->dev) : 83627b3f8d1SDan Williams "none", 83727b3f8d1SDan Williams cxl_rr->nr_eps, cxl_rr->nr_targets); 83827b3f8d1SDan Williams 839384e624bSDan Williams return 0; 840384e624bSDan Williams out_erase: 841384e624bSDan Williams if (cxl_rr->nr_eps == 0) 842384e624bSDan Williams free_region_ref(cxl_rr); 843384e624bSDan Williams return rc; 844384e624bSDan Williams } 845384e624bSDan Williams 846384e624bSDan Williams static void cxl_port_detach_region(struct cxl_port *port, 847384e624bSDan Williams struct cxl_region *cxlr, 848384e624bSDan Williams struct cxl_endpoint_decoder *cxled) 849384e624bSDan Williams { 850384e624bSDan Williams struct cxl_region_ref *cxl_rr; 85127b3f8d1SDan Williams struct cxl_ep *ep = NULL; 852384e624bSDan Williams 853384e624bSDan Williams lockdep_assert_held_write(&cxl_region_rwsem); 854384e624bSDan Williams 855384e624bSDan Williams cxl_rr = cxl_rr_load(port, cxlr); 856384e624bSDan Williams if (!cxl_rr) 857384e624bSDan Williams return; 858384e624bSDan Williams 85927b3f8d1SDan Williams /* 86027b3f8d1SDan Williams * Endpoint ports do not carry cxl_ep references, and they 86127b3f8d1SDan Williams * never target more than one endpoint by definition 86227b3f8d1SDan Williams */ 86327b3f8d1SDan Williams if (cxl_rr->decoder == &cxled->cxld) 86427b3f8d1SDan Williams cxl_rr->nr_eps--; 86527b3f8d1SDan Williams else 866384e624bSDan Williams ep = xa_erase(&cxl_rr->endpoints, (unsigned long)cxled); 867384e624bSDan Williams if (ep) { 868384e624bSDan Williams struct cxl_ep *ep_iter; 869384e624bSDan Williams unsigned long index; 870384e624bSDan Williams int found = 0; 871384e624bSDan Williams 872384e624bSDan Williams cxl_rr->nr_eps--; 873384e624bSDan Williams xa_for_each(&cxl_rr->endpoints, index, ep_iter) { 874384e624bSDan Williams if (ep_iter->next == ep->next) { 875384e624bSDan Williams found++; 876384e624bSDan Williams break; 877384e624bSDan Williams } 878384e624bSDan Williams } 879384e624bSDan Williams if (!found) 880384e624bSDan Williams cxl_rr->nr_targets--; 881384e624bSDan Williams } 882384e624bSDan Williams 883384e624bSDan Williams if (cxl_rr->nr_eps == 0) 884384e624bSDan Williams free_region_ref(cxl_rr); 885384e624bSDan Williams } 886384e624bSDan Williams 88727b3f8d1SDan Williams static int check_last_peer(struct cxl_endpoint_decoder *cxled, 88827b3f8d1SDan Williams struct cxl_ep *ep, struct cxl_region_ref *cxl_rr, 88927b3f8d1SDan Williams int distance) 89027b3f8d1SDan Williams { 89127b3f8d1SDan Williams struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); 89227b3f8d1SDan Williams struct cxl_region *cxlr = cxl_rr->region; 89327b3f8d1SDan Williams struct cxl_region_params *p = &cxlr->params; 89427b3f8d1SDan Williams struct cxl_endpoint_decoder *cxled_peer; 89527b3f8d1SDan Williams struct cxl_port *port = cxl_rr->port; 89627b3f8d1SDan Williams struct cxl_memdev *cxlmd_peer; 89727b3f8d1SDan Williams struct cxl_ep *ep_peer; 89827b3f8d1SDan Williams int pos = cxled->pos; 89927b3f8d1SDan Williams 90027b3f8d1SDan Williams /* 90127b3f8d1SDan Williams * If this position wants to share a dport with the last endpoint mapped 90227b3f8d1SDan Williams * then that endpoint, at index 'position - distance', must also be 90327b3f8d1SDan Williams * mapped by this dport. 90427b3f8d1SDan Williams */ 90527b3f8d1SDan Williams if (pos < distance) { 90627b3f8d1SDan Williams dev_dbg(&cxlr->dev, "%s:%s: cannot host %s:%s at %d\n", 90727b3f8d1SDan Williams dev_name(port->uport), dev_name(&port->dev), 90827b3f8d1SDan Williams dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos); 90927b3f8d1SDan Williams return -ENXIO; 91027b3f8d1SDan Williams } 91127b3f8d1SDan Williams cxled_peer = p->targets[pos - distance]; 91227b3f8d1SDan Williams cxlmd_peer = cxled_to_memdev(cxled_peer); 91327b3f8d1SDan Williams ep_peer = cxl_ep_load(port, cxlmd_peer); 91427b3f8d1SDan Williams if (ep->dport != ep_peer->dport) { 91527b3f8d1SDan Williams dev_dbg(&cxlr->dev, 91627b3f8d1SDan Williams "%s:%s: %s:%s pos %d mismatched peer %s:%s\n", 91727b3f8d1SDan Williams dev_name(port->uport), dev_name(&port->dev), 91827b3f8d1SDan Williams dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos, 91927b3f8d1SDan Williams dev_name(&cxlmd_peer->dev), 92027b3f8d1SDan Williams dev_name(&cxled_peer->cxld.dev)); 92127b3f8d1SDan Williams return -ENXIO; 92227b3f8d1SDan Williams } 92327b3f8d1SDan Williams 92427b3f8d1SDan Williams return 0; 92527b3f8d1SDan Williams } 92627b3f8d1SDan Williams 92727b3f8d1SDan Williams static int cxl_port_setup_targets(struct cxl_port *port, 92827b3f8d1SDan Williams struct cxl_region *cxlr, 92927b3f8d1SDan Williams struct cxl_endpoint_decoder *cxled) 93027b3f8d1SDan Williams { 93127b3f8d1SDan Williams struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent); 93227b3f8d1SDan Williams int parent_iw, parent_ig, ig, iw, rc, inc = 0, pos = cxled->pos; 93327b3f8d1SDan Williams struct cxl_port *parent_port = to_cxl_port(port->dev.parent); 93427b3f8d1SDan Williams struct cxl_region_ref *cxl_rr = cxl_rr_load(port, cxlr); 93527b3f8d1SDan Williams struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); 93627b3f8d1SDan Williams struct cxl_ep *ep = cxl_ep_load(port, cxlmd); 93727b3f8d1SDan Williams struct cxl_region_params *p = &cxlr->params; 93827b3f8d1SDan Williams struct cxl_decoder *cxld = cxl_rr->decoder; 93927b3f8d1SDan Williams struct cxl_switch_decoder *cxlsd; 94027b3f8d1SDan Williams u16 eig, peig; 94127b3f8d1SDan Williams u8 eiw, peiw; 94227b3f8d1SDan Williams 94327b3f8d1SDan Williams /* 94427b3f8d1SDan Williams * While root level decoders support x3, x6, x12, switch level 94527b3f8d1SDan Williams * decoders only support powers of 2 up to x16. 94627b3f8d1SDan Williams */ 94727b3f8d1SDan Williams if (!is_power_of_2(cxl_rr->nr_targets)) { 94827b3f8d1SDan Williams dev_dbg(&cxlr->dev, "%s:%s: invalid target count %d\n", 94927b3f8d1SDan Williams dev_name(port->uport), dev_name(&port->dev), 95027b3f8d1SDan Williams cxl_rr->nr_targets); 95127b3f8d1SDan Williams return -EINVAL; 95227b3f8d1SDan Williams } 95327b3f8d1SDan Williams 95427b3f8d1SDan Williams cxlsd = to_cxl_switch_decoder(&cxld->dev); 95527b3f8d1SDan Williams if (cxl_rr->nr_targets_set) { 95627b3f8d1SDan Williams int i, distance; 95727b3f8d1SDan Williams 95827b3f8d1SDan Williams distance = p->nr_targets / cxl_rr->nr_targets; 95927b3f8d1SDan Williams for (i = 0; i < cxl_rr->nr_targets_set; i++) 96027b3f8d1SDan Williams if (ep->dport == cxlsd->target[i]) { 96127b3f8d1SDan Williams rc = check_last_peer(cxled, ep, cxl_rr, 96227b3f8d1SDan Williams distance); 96327b3f8d1SDan Williams if (rc) 96427b3f8d1SDan Williams return rc; 96527b3f8d1SDan Williams goto out_target_set; 96627b3f8d1SDan Williams } 96727b3f8d1SDan Williams goto add_target; 96827b3f8d1SDan Williams } 96927b3f8d1SDan Williams 97027b3f8d1SDan Williams if (is_cxl_root(parent_port)) { 97127b3f8d1SDan Williams parent_ig = cxlrd->cxlsd.cxld.interleave_granularity; 97227b3f8d1SDan Williams parent_iw = cxlrd->cxlsd.cxld.interleave_ways; 97327b3f8d1SDan Williams /* 97427b3f8d1SDan Williams * For purposes of address bit routing, use power-of-2 math for 97527b3f8d1SDan Williams * switch ports. 97627b3f8d1SDan Williams */ 97727b3f8d1SDan Williams if (!is_power_of_2(parent_iw)) 97827b3f8d1SDan Williams parent_iw /= 3; 97927b3f8d1SDan Williams } else { 98027b3f8d1SDan Williams struct cxl_region_ref *parent_rr; 98127b3f8d1SDan Williams struct cxl_decoder *parent_cxld; 98227b3f8d1SDan Williams 98327b3f8d1SDan Williams parent_rr = cxl_rr_load(parent_port, cxlr); 98427b3f8d1SDan Williams parent_cxld = parent_rr->decoder; 98527b3f8d1SDan Williams parent_ig = parent_cxld->interleave_granularity; 98627b3f8d1SDan Williams parent_iw = parent_cxld->interleave_ways; 98727b3f8d1SDan Williams } 98827b3f8d1SDan Williams 98927b3f8d1SDan Williams granularity_to_cxl(parent_ig, &peig); 99027b3f8d1SDan Williams ways_to_cxl(parent_iw, &peiw); 99127b3f8d1SDan Williams 99227b3f8d1SDan Williams iw = cxl_rr->nr_targets; 99327b3f8d1SDan Williams ways_to_cxl(iw, &eiw); 99427b3f8d1SDan Williams if (cxl_rr->nr_targets > 1) { 99527b3f8d1SDan Williams u32 address_bit = max(peig + peiw, eiw + peig); 99627b3f8d1SDan Williams 99727b3f8d1SDan Williams eig = address_bit - eiw + 1; 99827b3f8d1SDan Williams } else { 99927b3f8d1SDan Williams eiw = peiw; 100027b3f8d1SDan Williams eig = peig; 100127b3f8d1SDan Williams } 100227b3f8d1SDan Williams 100327b3f8d1SDan Williams rc = cxl_to_granularity(eig, &ig); 100427b3f8d1SDan Williams if (rc) { 100527b3f8d1SDan Williams dev_dbg(&cxlr->dev, "%s:%s: invalid interleave: %d\n", 100627b3f8d1SDan Williams dev_name(port->uport), dev_name(&port->dev), 100727b3f8d1SDan Williams 256 << eig); 100827b3f8d1SDan Williams return rc; 100927b3f8d1SDan Williams } 101027b3f8d1SDan Williams 101127b3f8d1SDan Williams cxld->interleave_ways = iw; 101227b3f8d1SDan Williams cxld->interleave_granularity = ig; 101327b3f8d1SDan Williams dev_dbg(&cxlr->dev, "%s:%s iw: %d ig: %d\n", dev_name(port->uport), 101427b3f8d1SDan Williams dev_name(&port->dev), iw, ig); 101527b3f8d1SDan Williams add_target: 101627b3f8d1SDan Williams if (cxl_rr->nr_targets_set == cxl_rr->nr_targets) { 101727b3f8d1SDan Williams dev_dbg(&cxlr->dev, 101827b3f8d1SDan Williams "%s:%s: targets full trying to add %s:%s at %d\n", 101927b3f8d1SDan Williams dev_name(port->uport), dev_name(&port->dev), 102027b3f8d1SDan Williams dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos); 102127b3f8d1SDan Williams return -ENXIO; 102227b3f8d1SDan Williams } 102327b3f8d1SDan Williams cxlsd->target[cxl_rr->nr_targets_set] = ep->dport; 102427b3f8d1SDan Williams inc = 1; 102527b3f8d1SDan Williams out_target_set: 102627b3f8d1SDan Williams cxl_rr->nr_targets_set += inc; 102727b3f8d1SDan Williams dev_dbg(&cxlr->dev, "%s:%s target[%d] = %s for %s:%s @ %d\n", 102827b3f8d1SDan Williams dev_name(port->uport), dev_name(&port->dev), 102927b3f8d1SDan Williams cxl_rr->nr_targets_set - 1, dev_name(ep->dport->dport), 103027b3f8d1SDan Williams dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos); 103127b3f8d1SDan Williams 103227b3f8d1SDan Williams return 0; 103327b3f8d1SDan Williams } 103427b3f8d1SDan Williams 103527b3f8d1SDan Williams static void cxl_port_reset_targets(struct cxl_port *port, 103627b3f8d1SDan Williams struct cxl_region *cxlr) 103727b3f8d1SDan Williams { 103827b3f8d1SDan Williams struct cxl_region_ref *cxl_rr = cxl_rr_load(port, cxlr); 103927b3f8d1SDan Williams 104027b3f8d1SDan Williams /* 104127b3f8d1SDan Williams * After the last endpoint has been detached the entire cxl_rr may now 104227b3f8d1SDan Williams * be gone. 104327b3f8d1SDan Williams */ 104427b3f8d1SDan Williams if (cxl_rr) 104527b3f8d1SDan Williams cxl_rr->nr_targets_set = 0; 104627b3f8d1SDan Williams } 104727b3f8d1SDan Williams 104827b3f8d1SDan Williams static void cxl_region_teardown_targets(struct cxl_region *cxlr) 104927b3f8d1SDan Williams { 105027b3f8d1SDan Williams struct cxl_region_params *p = &cxlr->params; 105127b3f8d1SDan Williams struct cxl_endpoint_decoder *cxled; 105227b3f8d1SDan Williams struct cxl_memdev *cxlmd; 105327b3f8d1SDan Williams struct cxl_port *iter; 105427b3f8d1SDan Williams struct cxl_ep *ep; 105527b3f8d1SDan Williams int i; 105627b3f8d1SDan Williams 105727b3f8d1SDan Williams for (i = 0; i < p->nr_targets; i++) { 105827b3f8d1SDan Williams cxled = p->targets[i]; 105927b3f8d1SDan Williams cxlmd = cxled_to_memdev(cxled); 106027b3f8d1SDan Williams 106127b3f8d1SDan Williams iter = cxled_to_port(cxled); 106227b3f8d1SDan Williams while (!is_cxl_root(to_cxl_port(iter->dev.parent))) 106327b3f8d1SDan Williams iter = to_cxl_port(iter->dev.parent); 106427b3f8d1SDan Williams 106527b3f8d1SDan Williams for (ep = cxl_ep_load(iter, cxlmd); iter; 106627b3f8d1SDan Williams iter = ep->next, ep = cxl_ep_load(iter, cxlmd)) 106727b3f8d1SDan Williams cxl_port_reset_targets(iter, cxlr); 106827b3f8d1SDan Williams } 106927b3f8d1SDan Williams } 107027b3f8d1SDan Williams 107127b3f8d1SDan Williams static int cxl_region_setup_targets(struct cxl_region *cxlr) 107227b3f8d1SDan Williams { 107327b3f8d1SDan Williams struct cxl_region_params *p = &cxlr->params; 107427b3f8d1SDan Williams struct cxl_endpoint_decoder *cxled; 107527b3f8d1SDan Williams struct cxl_memdev *cxlmd; 107627b3f8d1SDan Williams struct cxl_port *iter; 107727b3f8d1SDan Williams struct cxl_ep *ep; 107827b3f8d1SDan Williams int i, rc; 107927b3f8d1SDan Williams 108027b3f8d1SDan Williams for (i = 0; i < p->nr_targets; i++) { 108127b3f8d1SDan Williams cxled = p->targets[i]; 108227b3f8d1SDan Williams cxlmd = cxled_to_memdev(cxled); 108327b3f8d1SDan Williams 108427b3f8d1SDan Williams iter = cxled_to_port(cxled); 108527b3f8d1SDan Williams while (!is_cxl_root(to_cxl_port(iter->dev.parent))) 108627b3f8d1SDan Williams iter = to_cxl_port(iter->dev.parent); 108727b3f8d1SDan Williams 108827b3f8d1SDan Williams /* 108927b3f8d1SDan Williams * Descend the topology tree programming targets while 109027b3f8d1SDan Williams * looking for conflicts. 109127b3f8d1SDan Williams */ 109227b3f8d1SDan Williams for (ep = cxl_ep_load(iter, cxlmd); iter; 109327b3f8d1SDan Williams iter = ep->next, ep = cxl_ep_load(iter, cxlmd)) { 109427b3f8d1SDan Williams rc = cxl_port_setup_targets(iter, cxlr, cxled); 109527b3f8d1SDan Williams if (rc) { 109627b3f8d1SDan Williams cxl_region_teardown_targets(cxlr); 109727b3f8d1SDan Williams return rc; 109827b3f8d1SDan Williams } 109927b3f8d1SDan Williams } 110027b3f8d1SDan Williams } 110127b3f8d1SDan Williams 110227b3f8d1SDan Williams return 0; 110327b3f8d1SDan Williams } 110427b3f8d1SDan Williams 1105b9686e8cSDan Williams static int cxl_region_attach(struct cxl_region *cxlr, 1106b9686e8cSDan Williams struct cxl_endpoint_decoder *cxled, int pos) 1107b9686e8cSDan Williams { 1108384e624bSDan Williams struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent); 1109384e624bSDan Williams struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); 1110384e624bSDan Williams struct cxl_port *ep_port, *root_port, *iter; 1111b9686e8cSDan Williams struct cxl_region_params *p = &cxlr->params; 1112384e624bSDan Williams struct cxl_dport *dport; 1113384e624bSDan Williams int i, rc = -ENXIO; 1114b9686e8cSDan Williams 1115b9686e8cSDan Williams if (cxled->mode == CXL_DECODER_DEAD) { 1116b9686e8cSDan Williams dev_dbg(&cxlr->dev, "%s dead\n", dev_name(&cxled->cxld.dev)); 1117b9686e8cSDan Williams return -ENODEV; 1118b9686e8cSDan Williams } 1119b9686e8cSDan Williams 1120384e624bSDan Williams /* all full of members, or interleave config not established? */ 1121384e624bSDan Williams if (p->state > CXL_CONFIG_INTERLEAVE_ACTIVE) { 1122384e624bSDan Williams dev_dbg(&cxlr->dev, "region already active\n"); 1123384e624bSDan Williams return -EBUSY; 1124384e624bSDan Williams } else if (p->state < CXL_CONFIG_INTERLEAVE_ACTIVE) { 1125384e624bSDan Williams dev_dbg(&cxlr->dev, "interleave config missing\n"); 1126384e624bSDan Williams return -ENXIO; 1127384e624bSDan Williams } 1128384e624bSDan Williams 1129384e624bSDan Williams if (pos < 0 || pos >= p->interleave_ways) { 1130b9686e8cSDan Williams dev_dbg(&cxlr->dev, "position %d out of range %d\n", pos, 1131b9686e8cSDan Williams p->interleave_ways); 1132b9686e8cSDan Williams return -ENXIO; 1133b9686e8cSDan Williams } 1134b9686e8cSDan Williams 1135b9686e8cSDan Williams if (p->targets[pos] == cxled) 1136b9686e8cSDan Williams return 0; 1137b9686e8cSDan Williams 1138b9686e8cSDan Williams if (p->targets[pos]) { 1139b9686e8cSDan Williams struct cxl_endpoint_decoder *cxled_target = p->targets[pos]; 1140b9686e8cSDan Williams struct cxl_memdev *cxlmd_target = cxled_to_memdev(cxled_target); 1141b9686e8cSDan Williams 1142b9686e8cSDan Williams dev_dbg(&cxlr->dev, "position %d already assigned to %s:%s\n", 1143b9686e8cSDan Williams pos, dev_name(&cxlmd_target->dev), 1144b9686e8cSDan Williams dev_name(&cxled_target->cxld.dev)); 1145b9686e8cSDan Williams return -EBUSY; 1146b9686e8cSDan Williams } 1147b9686e8cSDan Williams 1148384e624bSDan Williams for (i = 0; i < p->interleave_ways; i++) { 1149384e624bSDan Williams struct cxl_endpoint_decoder *cxled_target; 1150384e624bSDan Williams struct cxl_memdev *cxlmd_target; 1151384e624bSDan Williams 1152384e624bSDan Williams cxled_target = p->targets[pos]; 1153384e624bSDan Williams if (!cxled_target) 1154384e624bSDan Williams continue; 1155384e624bSDan Williams 1156384e624bSDan Williams cxlmd_target = cxled_to_memdev(cxled_target); 1157384e624bSDan Williams if (cxlmd_target == cxlmd) { 1158384e624bSDan Williams dev_dbg(&cxlr->dev, 1159384e624bSDan Williams "%s already specified at position %d via: %s\n", 1160384e624bSDan Williams dev_name(&cxlmd->dev), pos, 1161384e624bSDan Williams dev_name(&cxled_target->cxld.dev)); 1162384e624bSDan Williams return -EBUSY; 1163384e624bSDan Williams } 1164384e624bSDan Williams } 1165384e624bSDan Williams 1166384e624bSDan Williams ep_port = cxled_to_port(cxled); 1167384e624bSDan Williams root_port = cxlrd_to_port(cxlrd); 1168384e624bSDan Williams dport = cxl_find_dport_by_dev(root_port, ep_port->host_bridge); 1169384e624bSDan Williams if (!dport) { 1170384e624bSDan Williams dev_dbg(&cxlr->dev, "%s:%s invalid target for %s\n", 1171384e624bSDan Williams dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), 1172384e624bSDan Williams dev_name(cxlr->dev.parent)); 1173384e624bSDan Williams return -ENXIO; 1174384e624bSDan Williams } 1175384e624bSDan Williams 1176384e624bSDan Williams if (cxlrd->calc_hb(cxlrd, pos) != dport) { 1177384e624bSDan Williams dev_dbg(&cxlr->dev, "%s:%s invalid target position for %s\n", 1178384e624bSDan Williams dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), 1179384e624bSDan Williams dev_name(&cxlrd->cxlsd.cxld.dev)); 1180384e624bSDan Williams return -ENXIO; 1181384e624bSDan Williams } 1182384e624bSDan Williams 1183384e624bSDan Williams if (cxled->cxld.target_type != cxlr->type) { 1184384e624bSDan Williams dev_dbg(&cxlr->dev, "%s:%s type mismatch: %d vs %d\n", 1185384e624bSDan Williams dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), 1186384e624bSDan Williams cxled->cxld.target_type, cxlr->type); 1187384e624bSDan Williams return -ENXIO; 1188384e624bSDan Williams } 1189384e624bSDan Williams 1190384e624bSDan Williams if (!cxled->dpa_res) { 1191384e624bSDan Williams dev_dbg(&cxlr->dev, "%s:%s: missing DPA allocation.\n", 1192384e624bSDan Williams dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev)); 1193384e624bSDan Williams return -ENXIO; 1194384e624bSDan Williams } 1195384e624bSDan Williams 1196384e624bSDan Williams if (resource_size(cxled->dpa_res) * p->interleave_ways != 1197384e624bSDan Williams resource_size(p->res)) { 1198384e624bSDan Williams dev_dbg(&cxlr->dev, 1199384e624bSDan Williams "%s:%s: decoder-size-%#llx * ways-%d != region-size-%#llx\n", 1200384e624bSDan Williams dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), 1201384e624bSDan Williams (u64)resource_size(cxled->dpa_res), p->interleave_ways, 1202384e624bSDan Williams (u64)resource_size(p->res)); 1203384e624bSDan Williams return -EINVAL; 1204384e624bSDan Williams } 1205384e624bSDan Williams 1206384e624bSDan Williams for (iter = ep_port; !is_cxl_root(iter); 1207384e624bSDan Williams iter = to_cxl_port(iter->dev.parent)) { 1208384e624bSDan Williams rc = cxl_port_attach_region(iter, cxlr, cxled, pos); 1209384e624bSDan Williams if (rc) 1210384e624bSDan Williams goto err; 1211384e624bSDan Williams } 1212384e624bSDan Williams 1213b9686e8cSDan Williams p->targets[pos] = cxled; 1214b9686e8cSDan Williams cxled->pos = pos; 1215b9686e8cSDan Williams p->nr_targets++; 1216b9686e8cSDan Williams 121727b3f8d1SDan Williams if (p->nr_targets == p->interleave_ways) { 121827b3f8d1SDan Williams rc = cxl_region_setup_targets(cxlr); 121927b3f8d1SDan Williams if (rc) 122027b3f8d1SDan Williams goto err; 1221384e624bSDan Williams p->state = CXL_CONFIG_ACTIVE; 122227b3f8d1SDan Williams } 1223384e624bSDan Williams 1224b9686e8cSDan Williams return 0; 1225384e624bSDan Williams 1226384e624bSDan Williams err: 1227384e624bSDan Williams for (iter = ep_port; !is_cxl_root(iter); 1228384e624bSDan Williams iter = to_cxl_port(iter->dev.parent)) 1229384e624bSDan Williams cxl_port_detach_region(iter, cxlr, cxled); 1230384e624bSDan Williams return rc; 1231b9686e8cSDan Williams } 1232b9686e8cSDan Williams 1233176baefbSDan Williams static int cxl_region_detach(struct cxl_endpoint_decoder *cxled) 1234b9686e8cSDan Williams { 1235384e624bSDan Williams struct cxl_port *iter, *ep_port = cxled_to_port(cxled); 1236b9686e8cSDan Williams struct cxl_region *cxlr = cxled->cxld.region; 1237b9686e8cSDan Williams struct cxl_region_params *p; 1238176baefbSDan Williams int rc = 0; 1239b9686e8cSDan Williams 1240b9686e8cSDan Williams lockdep_assert_held_write(&cxl_region_rwsem); 1241b9686e8cSDan Williams 1242b9686e8cSDan Williams if (!cxlr) 1243176baefbSDan Williams return 0; 1244b9686e8cSDan Williams 1245b9686e8cSDan Williams p = &cxlr->params; 1246b9686e8cSDan Williams get_device(&cxlr->dev); 1247b9686e8cSDan Williams 1248176baefbSDan Williams if (p->state > CXL_CONFIG_ACTIVE) { 1249176baefbSDan Williams /* 1250176baefbSDan Williams * TODO: tear down all impacted regions if a device is 1251176baefbSDan Williams * removed out of order 1252176baefbSDan Williams */ 1253176baefbSDan Williams rc = cxl_region_decode_reset(cxlr, p->interleave_ways); 1254176baefbSDan Williams if (rc) 1255176baefbSDan Williams goto out; 1256176baefbSDan Williams p->state = CXL_CONFIG_ACTIVE; 1257176baefbSDan Williams } 1258176baefbSDan Williams 1259384e624bSDan Williams for (iter = ep_port; !is_cxl_root(iter); 1260384e624bSDan Williams iter = to_cxl_port(iter->dev.parent)) 1261384e624bSDan Williams cxl_port_detach_region(iter, cxlr, cxled); 1262384e624bSDan Williams 1263b9686e8cSDan Williams if (cxled->pos < 0 || cxled->pos >= p->interleave_ways || 1264b9686e8cSDan Williams p->targets[cxled->pos] != cxled) { 1265b9686e8cSDan Williams struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); 1266b9686e8cSDan Williams 1267b9686e8cSDan Williams dev_WARN_ONCE(&cxlr->dev, 1, "expected %s:%s at position %d\n", 1268b9686e8cSDan Williams dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), 1269b9686e8cSDan Williams cxled->pos); 1270b9686e8cSDan Williams goto out; 1271b9686e8cSDan Williams } 1272b9686e8cSDan Williams 127327b3f8d1SDan Williams if (p->state == CXL_CONFIG_ACTIVE) { 1274384e624bSDan Williams p->state = CXL_CONFIG_INTERLEAVE_ACTIVE; 127527b3f8d1SDan Williams cxl_region_teardown_targets(cxlr); 127627b3f8d1SDan Williams } 1277b9686e8cSDan Williams p->targets[cxled->pos] = NULL; 1278b9686e8cSDan Williams p->nr_targets--; 1279b9686e8cSDan Williams 1280384e624bSDan Williams /* notify the region driver that one of its targets has departed */ 1281b9686e8cSDan Williams up_write(&cxl_region_rwsem); 1282b9686e8cSDan Williams device_release_driver(&cxlr->dev); 1283b9686e8cSDan Williams down_write(&cxl_region_rwsem); 1284b9686e8cSDan Williams out: 1285b9686e8cSDan Williams put_device(&cxlr->dev); 1286176baefbSDan Williams return rc; 1287b9686e8cSDan Williams } 1288b9686e8cSDan Williams 1289b9686e8cSDan Williams void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled) 1290b9686e8cSDan Williams { 1291b9686e8cSDan Williams down_write(&cxl_region_rwsem); 1292b9686e8cSDan Williams cxled->mode = CXL_DECODER_DEAD; 1293b9686e8cSDan Williams cxl_region_detach(cxled); 1294b9686e8cSDan Williams up_write(&cxl_region_rwsem); 1295b9686e8cSDan Williams } 1296b9686e8cSDan Williams 1297b9686e8cSDan Williams static int attach_target(struct cxl_region *cxlr, const char *decoder, int pos) 1298b9686e8cSDan Williams { 1299b9686e8cSDan Williams struct device *dev; 1300b9686e8cSDan Williams int rc; 1301b9686e8cSDan Williams 1302b9686e8cSDan Williams dev = bus_find_device_by_name(&cxl_bus_type, NULL, decoder); 1303b9686e8cSDan Williams if (!dev) 1304b9686e8cSDan Williams return -ENODEV; 1305b9686e8cSDan Williams 1306b9686e8cSDan Williams if (!is_endpoint_decoder(dev)) { 1307b9686e8cSDan Williams put_device(dev); 1308b9686e8cSDan Williams return -EINVAL; 1309b9686e8cSDan Williams } 1310b9686e8cSDan Williams 1311b9686e8cSDan Williams rc = down_write_killable(&cxl_region_rwsem); 1312b9686e8cSDan Williams if (rc) 1313b9686e8cSDan Williams goto out; 1314b9686e8cSDan Williams down_read(&cxl_dpa_rwsem); 1315b9686e8cSDan Williams rc = cxl_region_attach(cxlr, to_cxl_endpoint_decoder(dev), pos); 1316b9686e8cSDan Williams up_read(&cxl_dpa_rwsem); 1317b9686e8cSDan Williams up_write(&cxl_region_rwsem); 1318b9686e8cSDan Williams out: 1319b9686e8cSDan Williams put_device(dev); 1320b9686e8cSDan Williams return rc; 1321b9686e8cSDan Williams } 1322b9686e8cSDan Williams 1323b9686e8cSDan Williams static int detach_target(struct cxl_region *cxlr, int pos) 1324b9686e8cSDan Williams { 1325b9686e8cSDan Williams struct cxl_region_params *p = &cxlr->params; 1326b9686e8cSDan Williams int rc; 1327b9686e8cSDan Williams 1328b9686e8cSDan Williams rc = down_write_killable(&cxl_region_rwsem); 1329b9686e8cSDan Williams if (rc) 1330b9686e8cSDan Williams return rc; 1331b9686e8cSDan Williams 1332b9686e8cSDan Williams if (pos >= p->interleave_ways) { 1333b9686e8cSDan Williams dev_dbg(&cxlr->dev, "position %d out of range %d\n", pos, 1334b9686e8cSDan Williams p->interleave_ways); 1335b9686e8cSDan Williams rc = -ENXIO; 1336b9686e8cSDan Williams goto out; 1337b9686e8cSDan Williams } 1338b9686e8cSDan Williams 1339b9686e8cSDan Williams if (!p->targets[pos]) { 1340b9686e8cSDan Williams rc = 0; 1341b9686e8cSDan Williams goto out; 1342b9686e8cSDan Williams } 1343b9686e8cSDan Williams 1344176baefbSDan Williams rc = cxl_region_detach(p->targets[pos]); 1345b9686e8cSDan Williams out: 1346b9686e8cSDan Williams up_write(&cxl_region_rwsem); 1347b9686e8cSDan Williams return rc; 1348b9686e8cSDan Williams } 1349b9686e8cSDan Williams 1350b9686e8cSDan Williams static size_t store_targetN(struct cxl_region *cxlr, const char *buf, int pos, 1351b9686e8cSDan Williams size_t len) 1352b9686e8cSDan Williams { 1353b9686e8cSDan Williams int rc; 1354b9686e8cSDan Williams 1355b9686e8cSDan Williams if (sysfs_streq(buf, "\n")) 1356b9686e8cSDan Williams rc = detach_target(cxlr, pos); 1357b9686e8cSDan Williams else 1358b9686e8cSDan Williams rc = attach_target(cxlr, buf, pos); 1359b9686e8cSDan Williams 1360b9686e8cSDan Williams if (rc < 0) 1361b9686e8cSDan Williams return rc; 1362b9686e8cSDan Williams return len; 1363b9686e8cSDan Williams } 1364b9686e8cSDan Williams 1365b9686e8cSDan Williams #define TARGET_ATTR_RW(n) \ 1366b9686e8cSDan Williams static ssize_t target##n##_show( \ 1367b9686e8cSDan Williams struct device *dev, struct device_attribute *attr, char *buf) \ 1368b9686e8cSDan Williams { \ 1369b9686e8cSDan Williams return show_targetN(to_cxl_region(dev), buf, (n)); \ 1370b9686e8cSDan Williams } \ 1371b9686e8cSDan Williams static ssize_t target##n##_store(struct device *dev, \ 1372b9686e8cSDan Williams struct device_attribute *attr, \ 1373b9686e8cSDan Williams const char *buf, size_t len) \ 1374b9686e8cSDan Williams { \ 1375b9686e8cSDan Williams return store_targetN(to_cxl_region(dev), buf, (n), len); \ 1376b9686e8cSDan Williams } \ 1377b9686e8cSDan Williams static DEVICE_ATTR_RW(target##n) 1378b9686e8cSDan Williams 1379b9686e8cSDan Williams TARGET_ATTR_RW(0); 1380b9686e8cSDan Williams TARGET_ATTR_RW(1); 1381b9686e8cSDan Williams TARGET_ATTR_RW(2); 1382b9686e8cSDan Williams TARGET_ATTR_RW(3); 1383b9686e8cSDan Williams TARGET_ATTR_RW(4); 1384b9686e8cSDan Williams TARGET_ATTR_RW(5); 1385b9686e8cSDan Williams TARGET_ATTR_RW(6); 1386b9686e8cSDan Williams TARGET_ATTR_RW(7); 1387b9686e8cSDan Williams TARGET_ATTR_RW(8); 1388b9686e8cSDan Williams TARGET_ATTR_RW(9); 1389b9686e8cSDan Williams TARGET_ATTR_RW(10); 1390b9686e8cSDan Williams TARGET_ATTR_RW(11); 1391b9686e8cSDan Williams TARGET_ATTR_RW(12); 1392b9686e8cSDan Williams TARGET_ATTR_RW(13); 1393b9686e8cSDan Williams TARGET_ATTR_RW(14); 1394b9686e8cSDan Williams TARGET_ATTR_RW(15); 1395b9686e8cSDan Williams 1396b9686e8cSDan Williams static struct attribute *target_attrs[] = { 1397b9686e8cSDan Williams &dev_attr_target0.attr, 1398b9686e8cSDan Williams &dev_attr_target1.attr, 1399b9686e8cSDan Williams &dev_attr_target2.attr, 1400b9686e8cSDan Williams &dev_attr_target3.attr, 1401b9686e8cSDan Williams &dev_attr_target4.attr, 1402b9686e8cSDan Williams &dev_attr_target5.attr, 1403b9686e8cSDan Williams &dev_attr_target6.attr, 1404b9686e8cSDan Williams &dev_attr_target7.attr, 1405b9686e8cSDan Williams &dev_attr_target8.attr, 1406b9686e8cSDan Williams &dev_attr_target9.attr, 1407b9686e8cSDan Williams &dev_attr_target10.attr, 1408b9686e8cSDan Williams &dev_attr_target11.attr, 1409b9686e8cSDan Williams &dev_attr_target12.attr, 1410b9686e8cSDan Williams &dev_attr_target13.attr, 1411b9686e8cSDan Williams &dev_attr_target14.attr, 1412b9686e8cSDan Williams &dev_attr_target15.attr, 1413b9686e8cSDan Williams NULL, 1414b9686e8cSDan Williams }; 1415b9686e8cSDan Williams 1416b9686e8cSDan Williams static umode_t cxl_region_target_visible(struct kobject *kobj, 1417b9686e8cSDan Williams struct attribute *a, int n) 1418b9686e8cSDan Williams { 1419b9686e8cSDan Williams struct device *dev = kobj_to_dev(kobj); 1420b9686e8cSDan Williams struct cxl_region *cxlr = to_cxl_region(dev); 1421b9686e8cSDan Williams struct cxl_region_params *p = &cxlr->params; 1422b9686e8cSDan Williams 1423b9686e8cSDan Williams if (n < p->interleave_ways) 1424b9686e8cSDan Williams return a->mode; 1425b9686e8cSDan Williams return 0; 1426b9686e8cSDan Williams } 1427b9686e8cSDan Williams 1428b9686e8cSDan Williams static const struct attribute_group cxl_region_target_group = { 1429b9686e8cSDan Williams .attrs = target_attrs, 1430b9686e8cSDan Williams .is_visible = cxl_region_target_visible, 1431b9686e8cSDan Williams }; 1432b9686e8cSDan Williams 1433b9686e8cSDan Williams static const struct attribute_group *get_cxl_region_target_group(void) 1434b9686e8cSDan Williams { 1435b9686e8cSDan Williams return &cxl_region_target_group; 1436b9686e8cSDan Williams } 1437b9686e8cSDan Williams 1438dd5ba0ebSBen Widawsky static const struct attribute_group *region_groups[] = { 1439dd5ba0ebSBen Widawsky &cxl_base_attribute_group, 1440dd5ba0ebSBen Widawsky &cxl_region_group, 1441b9686e8cSDan Williams &cxl_region_target_group, 1442dd5ba0ebSBen Widawsky NULL, 1443dd5ba0ebSBen Widawsky }; 1444dd5ba0ebSBen Widawsky 1445779dd20cSBen Widawsky static void cxl_region_release(struct device *dev) 1446779dd20cSBen Widawsky { 1447779dd20cSBen Widawsky struct cxl_region *cxlr = to_cxl_region(dev); 1448779dd20cSBen Widawsky 1449779dd20cSBen Widawsky memregion_free(cxlr->id); 1450779dd20cSBen Widawsky kfree(cxlr); 1451779dd20cSBen Widawsky } 1452779dd20cSBen Widawsky 1453*8d48817dSDan Williams const struct device_type cxl_region_type = { 1454779dd20cSBen Widawsky .name = "cxl_region", 1455779dd20cSBen Widawsky .release = cxl_region_release, 1456dd5ba0ebSBen Widawsky .groups = region_groups 1457779dd20cSBen Widawsky }; 1458779dd20cSBen Widawsky 1459779dd20cSBen Widawsky bool is_cxl_region(struct device *dev) 1460779dd20cSBen Widawsky { 1461779dd20cSBen Widawsky return dev->type == &cxl_region_type; 1462779dd20cSBen Widawsky } 1463779dd20cSBen Widawsky EXPORT_SYMBOL_NS_GPL(is_cxl_region, CXL); 1464779dd20cSBen Widawsky 1465779dd20cSBen Widawsky static struct cxl_region *to_cxl_region(struct device *dev) 1466779dd20cSBen Widawsky { 1467779dd20cSBen Widawsky if (dev_WARN_ONCE(dev, dev->type != &cxl_region_type, 1468779dd20cSBen Widawsky "not a cxl_region device\n")) 1469779dd20cSBen Widawsky return NULL; 1470779dd20cSBen Widawsky 1471779dd20cSBen Widawsky return container_of(dev, struct cxl_region, dev); 1472779dd20cSBen Widawsky } 1473779dd20cSBen Widawsky 1474779dd20cSBen Widawsky static void unregister_region(void *dev) 1475779dd20cSBen Widawsky { 147623a22cd1SDan Williams struct cxl_region *cxlr = to_cxl_region(dev); 147723a22cd1SDan Williams 147823a22cd1SDan Williams device_del(dev); 147923a22cd1SDan Williams cxl_region_iomem_release(cxlr); 148023a22cd1SDan Williams put_device(dev); 1481779dd20cSBen Widawsky } 1482779dd20cSBen Widawsky 1483779dd20cSBen Widawsky static struct lock_class_key cxl_region_key; 1484779dd20cSBen Widawsky 1485779dd20cSBen Widawsky static struct cxl_region *cxl_region_alloc(struct cxl_root_decoder *cxlrd, int id) 1486779dd20cSBen Widawsky { 1487779dd20cSBen Widawsky struct cxl_region *cxlr; 1488779dd20cSBen Widawsky struct device *dev; 1489779dd20cSBen Widawsky 1490779dd20cSBen Widawsky cxlr = kzalloc(sizeof(*cxlr), GFP_KERNEL); 1491779dd20cSBen Widawsky if (!cxlr) { 1492779dd20cSBen Widawsky memregion_free(id); 1493779dd20cSBen Widawsky return ERR_PTR(-ENOMEM); 1494779dd20cSBen Widawsky } 1495779dd20cSBen Widawsky 1496779dd20cSBen Widawsky dev = &cxlr->dev; 1497779dd20cSBen Widawsky device_initialize(dev); 1498779dd20cSBen Widawsky lockdep_set_class(&dev->mutex, &cxl_region_key); 1499779dd20cSBen Widawsky dev->parent = &cxlrd->cxlsd.cxld.dev; 1500779dd20cSBen Widawsky device_set_pm_not_required(dev); 1501779dd20cSBen Widawsky dev->bus = &cxl_bus_type; 1502779dd20cSBen Widawsky dev->type = &cxl_region_type; 1503779dd20cSBen Widawsky cxlr->id = id; 1504779dd20cSBen Widawsky 1505779dd20cSBen Widawsky return cxlr; 1506779dd20cSBen Widawsky } 1507779dd20cSBen Widawsky 1508779dd20cSBen Widawsky /** 1509779dd20cSBen Widawsky * devm_cxl_add_region - Adds a region to a decoder 1510779dd20cSBen Widawsky * @cxlrd: root decoder 1511779dd20cSBen Widawsky * @id: memregion id to create, or memregion_free() on failure 1512779dd20cSBen Widawsky * @mode: mode for the endpoint decoders of this region 1513779dd20cSBen Widawsky * @type: select whether this is an expander or accelerator (type-2 or type-3) 1514779dd20cSBen Widawsky * 1515779dd20cSBen Widawsky * This is the second step of region initialization. Regions exist within an 1516779dd20cSBen Widawsky * address space which is mapped by a @cxlrd. 1517779dd20cSBen Widawsky * 1518779dd20cSBen Widawsky * Return: 0 if the region was added to the @cxlrd, else returns negative error 1519779dd20cSBen Widawsky * code. The region will be named "regionZ" where Z is the unique region number. 1520779dd20cSBen Widawsky */ 1521779dd20cSBen Widawsky static struct cxl_region *devm_cxl_add_region(struct cxl_root_decoder *cxlrd, 1522779dd20cSBen Widawsky int id, 1523779dd20cSBen Widawsky enum cxl_decoder_mode mode, 1524779dd20cSBen Widawsky enum cxl_decoder_type type) 1525779dd20cSBen Widawsky { 1526779dd20cSBen Widawsky struct cxl_port *port = to_cxl_port(cxlrd->cxlsd.cxld.dev.parent); 152780d10a6cSBen Widawsky struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; 152880d10a6cSBen Widawsky struct cxl_region_params *p; 1529779dd20cSBen Widawsky struct cxl_region *cxlr; 1530779dd20cSBen Widawsky struct device *dev; 1531779dd20cSBen Widawsky int rc; 1532779dd20cSBen Widawsky 1533779dd20cSBen Widawsky cxlr = cxl_region_alloc(cxlrd, id); 1534779dd20cSBen Widawsky if (IS_ERR(cxlr)) 1535779dd20cSBen Widawsky return cxlr; 153680d10a6cSBen Widawsky p = &cxlr->params; 1537779dd20cSBen Widawsky cxlr->mode = mode; 1538779dd20cSBen Widawsky cxlr->type = type; 153980d10a6cSBen Widawsky p->interleave_granularity = cxld->interleave_granularity; 1540779dd20cSBen Widawsky 1541779dd20cSBen Widawsky dev = &cxlr->dev; 1542779dd20cSBen Widawsky rc = dev_set_name(dev, "region%d", id); 1543779dd20cSBen Widawsky if (rc) 1544779dd20cSBen Widawsky goto err; 1545779dd20cSBen Widawsky 1546779dd20cSBen Widawsky rc = device_add(dev); 1547779dd20cSBen Widawsky if (rc) 1548779dd20cSBen Widawsky goto err; 1549779dd20cSBen Widawsky 1550779dd20cSBen Widawsky rc = devm_add_action_or_reset(port->uport, unregister_region, cxlr); 1551779dd20cSBen Widawsky if (rc) 1552779dd20cSBen Widawsky return ERR_PTR(rc); 1553779dd20cSBen Widawsky 1554779dd20cSBen Widawsky dev_dbg(port->uport, "%s: created %s\n", 1555779dd20cSBen Widawsky dev_name(&cxlrd->cxlsd.cxld.dev), dev_name(dev)); 1556779dd20cSBen Widawsky return cxlr; 1557779dd20cSBen Widawsky 1558779dd20cSBen Widawsky err: 1559779dd20cSBen Widawsky put_device(dev); 1560779dd20cSBen Widawsky return ERR_PTR(rc); 1561779dd20cSBen Widawsky } 1562779dd20cSBen Widawsky 1563779dd20cSBen Widawsky static ssize_t create_pmem_region_show(struct device *dev, 1564779dd20cSBen Widawsky struct device_attribute *attr, char *buf) 1565779dd20cSBen Widawsky { 1566779dd20cSBen Widawsky struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev); 1567779dd20cSBen Widawsky 1568779dd20cSBen Widawsky return sysfs_emit(buf, "region%u\n", atomic_read(&cxlrd->region_id)); 1569779dd20cSBen Widawsky } 1570779dd20cSBen Widawsky 1571779dd20cSBen Widawsky static ssize_t create_pmem_region_store(struct device *dev, 1572779dd20cSBen Widawsky struct device_attribute *attr, 1573779dd20cSBen Widawsky const char *buf, size_t len) 1574779dd20cSBen Widawsky { 1575779dd20cSBen Widawsky struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev); 1576779dd20cSBen Widawsky struct cxl_region *cxlr; 1577779dd20cSBen Widawsky int id, rc; 1578779dd20cSBen Widawsky 1579779dd20cSBen Widawsky rc = sscanf(buf, "region%d\n", &id); 1580779dd20cSBen Widawsky if (rc != 1) 1581779dd20cSBen Widawsky return -EINVAL; 1582779dd20cSBen Widawsky 1583779dd20cSBen Widawsky rc = memregion_alloc(GFP_KERNEL); 1584779dd20cSBen Widawsky if (rc < 0) 1585779dd20cSBen Widawsky return rc; 1586779dd20cSBen Widawsky 1587779dd20cSBen Widawsky if (atomic_cmpxchg(&cxlrd->region_id, id, rc) != id) { 1588779dd20cSBen Widawsky memregion_free(rc); 1589779dd20cSBen Widawsky return -EBUSY; 1590779dd20cSBen Widawsky } 1591779dd20cSBen Widawsky 1592779dd20cSBen Widawsky cxlr = devm_cxl_add_region(cxlrd, id, CXL_DECODER_PMEM, 1593779dd20cSBen Widawsky CXL_DECODER_EXPANDER); 1594779dd20cSBen Widawsky if (IS_ERR(cxlr)) 1595779dd20cSBen Widawsky return PTR_ERR(cxlr); 1596779dd20cSBen Widawsky 1597779dd20cSBen Widawsky return len; 1598779dd20cSBen Widawsky } 1599779dd20cSBen Widawsky DEVICE_ATTR_RW(create_pmem_region); 1600779dd20cSBen Widawsky 1601b9686e8cSDan Williams static ssize_t region_show(struct device *dev, struct device_attribute *attr, 1602b9686e8cSDan Williams char *buf) 1603b9686e8cSDan Williams { 1604b9686e8cSDan Williams struct cxl_decoder *cxld = to_cxl_decoder(dev); 1605b9686e8cSDan Williams ssize_t rc; 1606b9686e8cSDan Williams 1607b9686e8cSDan Williams rc = down_read_interruptible(&cxl_region_rwsem); 1608b9686e8cSDan Williams if (rc) 1609b9686e8cSDan Williams return rc; 1610b9686e8cSDan Williams 1611b9686e8cSDan Williams if (cxld->region) 1612b9686e8cSDan Williams rc = sysfs_emit(buf, "%s\n", dev_name(&cxld->region->dev)); 1613b9686e8cSDan Williams else 1614b9686e8cSDan Williams rc = sysfs_emit(buf, "\n"); 1615b9686e8cSDan Williams up_read(&cxl_region_rwsem); 1616b9686e8cSDan Williams 1617b9686e8cSDan Williams return rc; 1618b9686e8cSDan Williams } 1619b9686e8cSDan Williams DEVICE_ATTR_RO(region); 1620b9686e8cSDan Williams 1621779dd20cSBen Widawsky static struct cxl_region * 1622779dd20cSBen Widawsky cxl_find_region_by_name(struct cxl_root_decoder *cxlrd, const char *name) 1623779dd20cSBen Widawsky { 1624779dd20cSBen Widawsky struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; 1625779dd20cSBen Widawsky struct device *region_dev; 1626779dd20cSBen Widawsky 1627779dd20cSBen Widawsky region_dev = device_find_child_by_name(&cxld->dev, name); 1628779dd20cSBen Widawsky if (!region_dev) 1629779dd20cSBen Widawsky return ERR_PTR(-ENODEV); 1630779dd20cSBen Widawsky 1631779dd20cSBen Widawsky return to_cxl_region(region_dev); 1632779dd20cSBen Widawsky } 1633779dd20cSBen Widawsky 1634779dd20cSBen Widawsky static ssize_t delete_region_store(struct device *dev, 1635779dd20cSBen Widawsky struct device_attribute *attr, 1636779dd20cSBen Widawsky const char *buf, size_t len) 1637779dd20cSBen Widawsky { 1638779dd20cSBen Widawsky struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev); 1639779dd20cSBen Widawsky struct cxl_port *port = to_cxl_port(dev->parent); 1640779dd20cSBen Widawsky struct cxl_region *cxlr; 1641779dd20cSBen Widawsky 1642779dd20cSBen Widawsky cxlr = cxl_find_region_by_name(cxlrd, buf); 1643779dd20cSBen Widawsky if (IS_ERR(cxlr)) 1644779dd20cSBen Widawsky return PTR_ERR(cxlr); 1645779dd20cSBen Widawsky 1646779dd20cSBen Widawsky devm_release_action(port->uport, unregister_region, cxlr); 1647779dd20cSBen Widawsky put_device(&cxlr->dev); 1648779dd20cSBen Widawsky 1649779dd20cSBen Widawsky return len; 1650779dd20cSBen Widawsky } 1651779dd20cSBen Widawsky DEVICE_ATTR_WO(delete_region); 165223a22cd1SDan Williams 1653*8d48817dSDan Williams static int cxl_region_probe(struct device *dev) 1654*8d48817dSDan Williams { 1655*8d48817dSDan Williams struct cxl_region *cxlr = to_cxl_region(dev); 1656*8d48817dSDan Williams struct cxl_region_params *p = &cxlr->params; 1657*8d48817dSDan Williams int rc; 1658*8d48817dSDan Williams 1659*8d48817dSDan Williams rc = down_read_interruptible(&cxl_region_rwsem); 1660*8d48817dSDan Williams if (rc) { 1661*8d48817dSDan Williams dev_dbg(&cxlr->dev, "probe interrupted\n"); 1662*8d48817dSDan Williams return rc; 1663*8d48817dSDan Williams } 1664*8d48817dSDan Williams 1665*8d48817dSDan Williams if (p->state < CXL_CONFIG_COMMIT) { 1666*8d48817dSDan Williams dev_dbg(&cxlr->dev, "config state: %d\n", p->state); 1667*8d48817dSDan Williams rc = -ENXIO; 1668*8d48817dSDan Williams } 1669*8d48817dSDan Williams 1670*8d48817dSDan Williams /* 1671*8d48817dSDan Williams * From this point on any path that changes the region's state away from 1672*8d48817dSDan Williams * CXL_CONFIG_COMMIT is also responsible for releasing the driver. 1673*8d48817dSDan Williams */ 1674*8d48817dSDan Williams up_read(&cxl_region_rwsem); 1675*8d48817dSDan Williams 1676*8d48817dSDan Williams return rc; 1677*8d48817dSDan Williams } 1678*8d48817dSDan Williams 1679*8d48817dSDan Williams static struct cxl_driver cxl_region_driver = { 1680*8d48817dSDan Williams .name = "cxl_region", 1681*8d48817dSDan Williams .probe = cxl_region_probe, 1682*8d48817dSDan Williams .id = CXL_DEVICE_REGION, 1683*8d48817dSDan Williams }; 1684*8d48817dSDan Williams 1685*8d48817dSDan Williams int cxl_region_init(void) 1686*8d48817dSDan Williams { 1687*8d48817dSDan Williams return cxl_driver_register(&cxl_region_driver); 1688*8d48817dSDan Williams } 1689*8d48817dSDan Williams 1690*8d48817dSDan Williams void cxl_region_exit(void) 1691*8d48817dSDan Williams { 1692*8d48817dSDan Williams cxl_driver_unregister(&cxl_region_driver); 1693*8d48817dSDan Williams } 1694*8d48817dSDan Williams 169523a22cd1SDan Williams MODULE_IMPORT_NS(CXL); 1696*8d48817dSDan Williams MODULE_ALIAS_CXL(CXL_DEVICE_REGION); 1697