1*8a1ec5fbSGregory Price // SPDX-License-Identifier: GPL-2.0-only 2*8a1ec5fbSGregory Price /* Copyright(c) 2022 Intel Corporation. All rights reserved. */ 3*8a1ec5fbSGregory Price #include <linux/device.h> 4*8a1ec5fbSGregory Price #include <linux/slab.h> 5*8a1ec5fbSGregory Price #include <cxlmem.h> 6*8a1ec5fbSGregory Price #include <cxl.h> 7*8a1ec5fbSGregory Price #include "core.h" 8*8a1ec5fbSGregory Price 9*8a1ec5fbSGregory Price static void cxl_pmem_region_release(struct device *dev) 10*8a1ec5fbSGregory Price { 11*8a1ec5fbSGregory Price struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev); 12*8a1ec5fbSGregory Price int i; 13*8a1ec5fbSGregory Price 14*8a1ec5fbSGregory Price for (i = 0; i < cxlr_pmem->nr_mappings; i++) { 15*8a1ec5fbSGregory Price struct cxl_memdev *cxlmd = cxlr_pmem->mapping[i].cxlmd; 16*8a1ec5fbSGregory Price 17*8a1ec5fbSGregory Price put_device(&cxlmd->dev); 18*8a1ec5fbSGregory Price } 19*8a1ec5fbSGregory Price 20*8a1ec5fbSGregory Price kfree(cxlr_pmem); 21*8a1ec5fbSGregory Price } 22*8a1ec5fbSGregory Price 23*8a1ec5fbSGregory Price static const struct attribute_group *cxl_pmem_region_attribute_groups[] = { 24*8a1ec5fbSGregory Price &cxl_base_attribute_group, 25*8a1ec5fbSGregory Price NULL 26*8a1ec5fbSGregory Price }; 27*8a1ec5fbSGregory Price 28*8a1ec5fbSGregory Price const struct device_type cxl_pmem_region_type = { 29*8a1ec5fbSGregory Price .name = "cxl_pmem_region", 30*8a1ec5fbSGregory Price .release = cxl_pmem_region_release, 31*8a1ec5fbSGregory Price .groups = cxl_pmem_region_attribute_groups, 32*8a1ec5fbSGregory Price }; 33*8a1ec5fbSGregory Price 34*8a1ec5fbSGregory Price bool is_cxl_pmem_region(struct device *dev) 35*8a1ec5fbSGregory Price { 36*8a1ec5fbSGregory Price return dev->type == &cxl_pmem_region_type; 37*8a1ec5fbSGregory Price } 38*8a1ec5fbSGregory Price EXPORT_SYMBOL_NS_GPL(is_cxl_pmem_region, "CXL"); 39*8a1ec5fbSGregory Price 40*8a1ec5fbSGregory Price struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev) 41*8a1ec5fbSGregory Price { 42*8a1ec5fbSGregory Price if (dev_WARN_ONCE(dev, !is_cxl_pmem_region(dev), 43*8a1ec5fbSGregory Price "not a cxl_pmem_region device\n")) 44*8a1ec5fbSGregory Price return NULL; 45*8a1ec5fbSGregory Price return container_of(dev, struct cxl_pmem_region, dev); 46*8a1ec5fbSGregory Price } 47*8a1ec5fbSGregory Price EXPORT_SYMBOL_NS_GPL(to_cxl_pmem_region, "CXL"); 48*8a1ec5fbSGregory Price 49*8a1ec5fbSGregory Price static struct lock_class_key cxl_pmem_region_key; 50*8a1ec5fbSGregory Price 51*8a1ec5fbSGregory Price static int cxl_pmem_region_alloc(struct cxl_region *cxlr) 52*8a1ec5fbSGregory Price { 53*8a1ec5fbSGregory Price struct cxl_region_params *p = &cxlr->params; 54*8a1ec5fbSGregory Price struct cxl_nvdimm_bridge *cxl_nvb; 55*8a1ec5fbSGregory Price struct device *dev; 56*8a1ec5fbSGregory Price int i; 57*8a1ec5fbSGregory Price 58*8a1ec5fbSGregory Price guard(rwsem_read)(&cxl_rwsem.region); 59*8a1ec5fbSGregory Price if (p->state != CXL_CONFIG_COMMIT) 60*8a1ec5fbSGregory Price return -ENXIO; 61*8a1ec5fbSGregory Price 62*8a1ec5fbSGregory Price struct cxl_pmem_region *cxlr_pmem __free(kfree) = 63*8a1ec5fbSGregory Price kzalloc_flex(*cxlr_pmem, mapping, p->nr_targets); 64*8a1ec5fbSGregory Price if (!cxlr_pmem) 65*8a1ec5fbSGregory Price return -ENOMEM; 66*8a1ec5fbSGregory Price 67*8a1ec5fbSGregory Price cxlr_pmem->hpa_range.start = p->res->start; 68*8a1ec5fbSGregory Price cxlr_pmem->hpa_range.end = p->res->end; 69*8a1ec5fbSGregory Price 70*8a1ec5fbSGregory Price /* Snapshot the region configuration underneath the cxl_rwsem.region */ 71*8a1ec5fbSGregory Price cxlr_pmem->nr_mappings = p->nr_targets; 72*8a1ec5fbSGregory Price for (i = 0; i < p->nr_targets; i++) { 73*8a1ec5fbSGregory Price struct cxl_endpoint_decoder *cxled = p->targets[i]; 74*8a1ec5fbSGregory Price struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); 75*8a1ec5fbSGregory Price struct cxl_pmem_region_mapping *m = &cxlr_pmem->mapping[i]; 76*8a1ec5fbSGregory Price 77*8a1ec5fbSGregory Price /* 78*8a1ec5fbSGregory Price * Regions never span CXL root devices, so by definition the 79*8a1ec5fbSGregory Price * bridge for one device is the same for all. 80*8a1ec5fbSGregory Price */ 81*8a1ec5fbSGregory Price if (i == 0) { 82*8a1ec5fbSGregory Price cxl_nvb = cxl_find_nvdimm_bridge(cxlmd->endpoint); 83*8a1ec5fbSGregory Price if (!cxl_nvb) 84*8a1ec5fbSGregory Price return -ENODEV; 85*8a1ec5fbSGregory Price cxlr->cxl_nvb = cxl_nvb; 86*8a1ec5fbSGregory Price } 87*8a1ec5fbSGregory Price m->cxlmd = cxlmd; 88*8a1ec5fbSGregory Price get_device(&cxlmd->dev); 89*8a1ec5fbSGregory Price m->start = cxled->dpa_res->start; 90*8a1ec5fbSGregory Price m->size = resource_size(cxled->dpa_res); 91*8a1ec5fbSGregory Price m->position = i; 92*8a1ec5fbSGregory Price } 93*8a1ec5fbSGregory Price 94*8a1ec5fbSGregory Price dev = &cxlr_pmem->dev; 95*8a1ec5fbSGregory Price device_initialize(dev); 96*8a1ec5fbSGregory Price lockdep_set_class(&dev->mutex, &cxl_pmem_region_key); 97*8a1ec5fbSGregory Price device_set_pm_not_required(dev); 98*8a1ec5fbSGregory Price dev->parent = &cxlr->dev; 99*8a1ec5fbSGregory Price dev->bus = &cxl_bus_type; 100*8a1ec5fbSGregory Price dev->type = &cxl_pmem_region_type; 101*8a1ec5fbSGregory Price cxlr_pmem->cxlr = cxlr; 102*8a1ec5fbSGregory Price cxlr->cxlr_pmem = no_free_ptr(cxlr_pmem); 103*8a1ec5fbSGregory Price 104*8a1ec5fbSGregory Price return 0; 105*8a1ec5fbSGregory Price } 106*8a1ec5fbSGregory Price 107*8a1ec5fbSGregory Price static void cxlr_pmem_unregister(void *_cxlr_pmem) 108*8a1ec5fbSGregory Price { 109*8a1ec5fbSGregory Price struct cxl_pmem_region *cxlr_pmem = _cxlr_pmem; 110*8a1ec5fbSGregory Price struct cxl_region *cxlr = cxlr_pmem->cxlr; 111*8a1ec5fbSGregory Price struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb; 112*8a1ec5fbSGregory Price 113*8a1ec5fbSGregory Price /* 114*8a1ec5fbSGregory Price * Either the bridge is in ->remove() context under the device_lock(), 115*8a1ec5fbSGregory Price * or cxlr_release_nvdimm() is cancelling the bridge's release action 116*8a1ec5fbSGregory Price * for @cxlr_pmem and doing it itself (while manually holding the bridge 117*8a1ec5fbSGregory Price * lock). 118*8a1ec5fbSGregory Price */ 119*8a1ec5fbSGregory Price device_lock_assert(&cxl_nvb->dev); 120*8a1ec5fbSGregory Price cxlr->cxlr_pmem = NULL; 121*8a1ec5fbSGregory Price cxlr_pmem->cxlr = NULL; 122*8a1ec5fbSGregory Price device_unregister(&cxlr_pmem->dev); 123*8a1ec5fbSGregory Price } 124*8a1ec5fbSGregory Price 125*8a1ec5fbSGregory Price static void cxlr_release_nvdimm(void *_cxlr) 126*8a1ec5fbSGregory Price { 127*8a1ec5fbSGregory Price struct cxl_region *cxlr = _cxlr; 128*8a1ec5fbSGregory Price struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb; 129*8a1ec5fbSGregory Price 130*8a1ec5fbSGregory Price scoped_guard(device, &cxl_nvb->dev) { 131*8a1ec5fbSGregory Price if (cxlr->cxlr_pmem) 132*8a1ec5fbSGregory Price devm_release_action(&cxl_nvb->dev, cxlr_pmem_unregister, 133*8a1ec5fbSGregory Price cxlr->cxlr_pmem); 134*8a1ec5fbSGregory Price } 135*8a1ec5fbSGregory Price cxlr->cxl_nvb = NULL; 136*8a1ec5fbSGregory Price put_device(&cxl_nvb->dev); 137*8a1ec5fbSGregory Price } 138*8a1ec5fbSGregory Price 139*8a1ec5fbSGregory Price /** 140*8a1ec5fbSGregory Price * devm_cxl_add_pmem_region() - add a cxl_region-to-nd_region bridge 141*8a1ec5fbSGregory Price * @cxlr: parent CXL region for this pmem region bridge device 142*8a1ec5fbSGregory Price * 143*8a1ec5fbSGregory Price * Return: 0 on success negative error code on failure. 144*8a1ec5fbSGregory Price */ 145*8a1ec5fbSGregory Price int devm_cxl_add_pmem_region(struct cxl_region *cxlr) 146*8a1ec5fbSGregory Price { 147*8a1ec5fbSGregory Price struct cxl_pmem_region *cxlr_pmem; 148*8a1ec5fbSGregory Price struct cxl_nvdimm_bridge *cxl_nvb; 149*8a1ec5fbSGregory Price struct device *dev; 150*8a1ec5fbSGregory Price int rc; 151*8a1ec5fbSGregory Price 152*8a1ec5fbSGregory Price rc = cxl_pmem_region_alloc(cxlr); 153*8a1ec5fbSGregory Price if (rc) 154*8a1ec5fbSGregory Price return rc; 155*8a1ec5fbSGregory Price cxlr_pmem = cxlr->cxlr_pmem; 156*8a1ec5fbSGregory Price cxl_nvb = cxlr->cxl_nvb; 157*8a1ec5fbSGregory Price 158*8a1ec5fbSGregory Price dev = &cxlr_pmem->dev; 159*8a1ec5fbSGregory Price rc = dev_set_name(dev, "pmem_region%d", cxlr->id); 160*8a1ec5fbSGregory Price if (rc) 161*8a1ec5fbSGregory Price goto err; 162*8a1ec5fbSGregory Price 163*8a1ec5fbSGregory Price rc = device_add(dev); 164*8a1ec5fbSGregory Price if (rc) 165*8a1ec5fbSGregory Price goto err; 166*8a1ec5fbSGregory Price 167*8a1ec5fbSGregory Price dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent), 168*8a1ec5fbSGregory Price dev_name(dev)); 169*8a1ec5fbSGregory Price 170*8a1ec5fbSGregory Price scoped_guard(device, &cxl_nvb->dev) { 171*8a1ec5fbSGregory Price if (cxl_nvb->dev.driver) 172*8a1ec5fbSGregory Price rc = devm_add_action_or_reset(&cxl_nvb->dev, 173*8a1ec5fbSGregory Price cxlr_pmem_unregister, 174*8a1ec5fbSGregory Price cxlr_pmem); 175*8a1ec5fbSGregory Price else 176*8a1ec5fbSGregory Price rc = -ENXIO; 177*8a1ec5fbSGregory Price } 178*8a1ec5fbSGregory Price 179*8a1ec5fbSGregory Price if (rc) 180*8a1ec5fbSGregory Price goto err_bridge; 181*8a1ec5fbSGregory Price 182*8a1ec5fbSGregory Price /* @cxlr carries a reference on @cxl_nvb until cxlr_release_nvdimm */ 183*8a1ec5fbSGregory Price return devm_add_action_or_reset(&cxlr->dev, cxlr_release_nvdimm, cxlr); 184*8a1ec5fbSGregory Price 185*8a1ec5fbSGregory Price err: 186*8a1ec5fbSGregory Price put_device(dev); 187*8a1ec5fbSGregory Price err_bridge: 188*8a1ec5fbSGregory Price put_device(&cxl_nvb->dev); 189*8a1ec5fbSGregory Price cxlr->cxl_nvb = NULL; 190*8a1ec5fbSGregory Price return rc; 191*8a1ec5fbSGregory Price } 192