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