xref: /openbmc/linux/drivers/cxl/core/region.c (revision 779dd20c)
1*779dd20cSBen Widawsky // SPDX-License-Identifier: GPL-2.0-only
2*779dd20cSBen Widawsky /* Copyright(c) 2022 Intel Corporation. All rights reserved. */
3*779dd20cSBen Widawsky #include <linux/memregion.h>
4*779dd20cSBen Widawsky #include <linux/genalloc.h>
5*779dd20cSBen Widawsky #include <linux/device.h>
6*779dd20cSBen Widawsky #include <linux/module.h>
7*779dd20cSBen Widawsky #include <linux/slab.h>
8*779dd20cSBen Widawsky #include <linux/idr.h>
9*779dd20cSBen Widawsky #include <cxl.h>
10*779dd20cSBen Widawsky #include "core.h"
11*779dd20cSBen Widawsky 
12*779dd20cSBen Widawsky /**
13*779dd20cSBen Widawsky  * DOC: cxl core region
14*779dd20cSBen Widawsky  *
15*779dd20cSBen Widawsky  * CXL Regions represent mapped memory capacity in system physical address
16*779dd20cSBen Widawsky  * space. Whereas the CXL Root Decoders identify the bounds of potential CXL
17*779dd20cSBen Widawsky  * Memory ranges, Regions represent the active mapped capacity by the HDM
18*779dd20cSBen Widawsky  * Decoder Capability structures throughout the Host Bridges, Switches, and
19*779dd20cSBen Widawsky  * Endpoints in the topology.
20*779dd20cSBen Widawsky  */
21*779dd20cSBen Widawsky 
22*779dd20cSBen Widawsky static struct cxl_region *to_cxl_region(struct device *dev);
23*779dd20cSBen Widawsky 
24*779dd20cSBen Widawsky static void cxl_region_release(struct device *dev)
25*779dd20cSBen Widawsky {
26*779dd20cSBen Widawsky 	struct cxl_region *cxlr = to_cxl_region(dev);
27*779dd20cSBen Widawsky 
28*779dd20cSBen Widawsky 	memregion_free(cxlr->id);
29*779dd20cSBen Widawsky 	kfree(cxlr);
30*779dd20cSBen Widawsky }
31*779dd20cSBen Widawsky 
32*779dd20cSBen Widawsky static const struct device_type cxl_region_type = {
33*779dd20cSBen Widawsky 	.name = "cxl_region",
34*779dd20cSBen Widawsky 	.release = cxl_region_release,
35*779dd20cSBen Widawsky };
36*779dd20cSBen Widawsky 
37*779dd20cSBen Widawsky bool is_cxl_region(struct device *dev)
38*779dd20cSBen Widawsky {
39*779dd20cSBen Widawsky 	return dev->type == &cxl_region_type;
40*779dd20cSBen Widawsky }
41*779dd20cSBen Widawsky EXPORT_SYMBOL_NS_GPL(is_cxl_region, CXL);
42*779dd20cSBen Widawsky 
43*779dd20cSBen Widawsky static struct cxl_region *to_cxl_region(struct device *dev)
44*779dd20cSBen Widawsky {
45*779dd20cSBen Widawsky 	if (dev_WARN_ONCE(dev, dev->type != &cxl_region_type,
46*779dd20cSBen Widawsky 			  "not a cxl_region device\n"))
47*779dd20cSBen Widawsky 		return NULL;
48*779dd20cSBen Widawsky 
49*779dd20cSBen Widawsky 	return container_of(dev, struct cxl_region, dev);
50*779dd20cSBen Widawsky }
51*779dd20cSBen Widawsky 
52*779dd20cSBen Widawsky static void unregister_region(void *dev)
53*779dd20cSBen Widawsky {
54*779dd20cSBen Widawsky 	device_unregister(dev);
55*779dd20cSBen Widawsky }
56*779dd20cSBen Widawsky 
57*779dd20cSBen Widawsky static struct lock_class_key cxl_region_key;
58*779dd20cSBen Widawsky 
59*779dd20cSBen Widawsky static struct cxl_region *cxl_region_alloc(struct cxl_root_decoder *cxlrd, int id)
60*779dd20cSBen Widawsky {
61*779dd20cSBen Widawsky 	struct cxl_region *cxlr;
62*779dd20cSBen Widawsky 	struct device *dev;
63*779dd20cSBen Widawsky 
64*779dd20cSBen Widawsky 	cxlr = kzalloc(sizeof(*cxlr), GFP_KERNEL);
65*779dd20cSBen Widawsky 	if (!cxlr) {
66*779dd20cSBen Widawsky 		memregion_free(id);
67*779dd20cSBen Widawsky 		return ERR_PTR(-ENOMEM);
68*779dd20cSBen Widawsky 	}
69*779dd20cSBen Widawsky 
70*779dd20cSBen Widawsky 	dev = &cxlr->dev;
71*779dd20cSBen Widawsky 	device_initialize(dev);
72*779dd20cSBen Widawsky 	lockdep_set_class(&dev->mutex, &cxl_region_key);
73*779dd20cSBen Widawsky 	dev->parent = &cxlrd->cxlsd.cxld.dev;
74*779dd20cSBen Widawsky 	device_set_pm_not_required(dev);
75*779dd20cSBen Widawsky 	dev->bus = &cxl_bus_type;
76*779dd20cSBen Widawsky 	dev->type = &cxl_region_type;
77*779dd20cSBen Widawsky 	cxlr->id = id;
78*779dd20cSBen Widawsky 
79*779dd20cSBen Widawsky 	return cxlr;
80*779dd20cSBen Widawsky }
81*779dd20cSBen Widawsky 
82*779dd20cSBen Widawsky /**
83*779dd20cSBen Widawsky  * devm_cxl_add_region - Adds a region to a decoder
84*779dd20cSBen Widawsky  * @cxlrd: root decoder
85*779dd20cSBen Widawsky  * @id: memregion id to create, or memregion_free() on failure
86*779dd20cSBen Widawsky  * @mode: mode for the endpoint decoders of this region
87*779dd20cSBen Widawsky  * @type: select whether this is an expander or accelerator (type-2 or type-3)
88*779dd20cSBen Widawsky  *
89*779dd20cSBen Widawsky  * This is the second step of region initialization. Regions exist within an
90*779dd20cSBen Widawsky  * address space which is mapped by a @cxlrd.
91*779dd20cSBen Widawsky  *
92*779dd20cSBen Widawsky  * Return: 0 if the region was added to the @cxlrd, else returns negative error
93*779dd20cSBen Widawsky  * code. The region will be named "regionZ" where Z is the unique region number.
94*779dd20cSBen Widawsky  */
95*779dd20cSBen Widawsky static struct cxl_region *devm_cxl_add_region(struct cxl_root_decoder *cxlrd,
96*779dd20cSBen Widawsky 					      int id,
97*779dd20cSBen Widawsky 					      enum cxl_decoder_mode mode,
98*779dd20cSBen Widawsky 					      enum cxl_decoder_type type)
99*779dd20cSBen Widawsky {
100*779dd20cSBen Widawsky 	struct cxl_port *port = to_cxl_port(cxlrd->cxlsd.cxld.dev.parent);
101*779dd20cSBen Widawsky 	struct cxl_region *cxlr;
102*779dd20cSBen Widawsky 	struct device *dev;
103*779dd20cSBen Widawsky 	int rc;
104*779dd20cSBen Widawsky 
105*779dd20cSBen Widawsky 	cxlr = cxl_region_alloc(cxlrd, id);
106*779dd20cSBen Widawsky 	if (IS_ERR(cxlr))
107*779dd20cSBen Widawsky 		return cxlr;
108*779dd20cSBen Widawsky 	cxlr->mode = mode;
109*779dd20cSBen Widawsky 	cxlr->type = type;
110*779dd20cSBen Widawsky 
111*779dd20cSBen Widawsky 	dev = &cxlr->dev;
112*779dd20cSBen Widawsky 	rc = dev_set_name(dev, "region%d", id);
113*779dd20cSBen Widawsky 	if (rc)
114*779dd20cSBen Widawsky 		goto err;
115*779dd20cSBen Widawsky 
116*779dd20cSBen Widawsky 	rc = device_add(dev);
117*779dd20cSBen Widawsky 	if (rc)
118*779dd20cSBen Widawsky 		goto err;
119*779dd20cSBen Widawsky 
120*779dd20cSBen Widawsky 	rc = devm_add_action_or_reset(port->uport, unregister_region, cxlr);
121*779dd20cSBen Widawsky 	if (rc)
122*779dd20cSBen Widawsky 		return ERR_PTR(rc);
123*779dd20cSBen Widawsky 
124*779dd20cSBen Widawsky 	dev_dbg(port->uport, "%s: created %s\n",
125*779dd20cSBen Widawsky 		dev_name(&cxlrd->cxlsd.cxld.dev), dev_name(dev));
126*779dd20cSBen Widawsky 	return cxlr;
127*779dd20cSBen Widawsky 
128*779dd20cSBen Widawsky err:
129*779dd20cSBen Widawsky 	put_device(dev);
130*779dd20cSBen Widawsky 	return ERR_PTR(rc);
131*779dd20cSBen Widawsky }
132*779dd20cSBen Widawsky 
133*779dd20cSBen Widawsky static ssize_t create_pmem_region_show(struct device *dev,
134*779dd20cSBen Widawsky 				       struct device_attribute *attr, char *buf)
135*779dd20cSBen Widawsky {
136*779dd20cSBen Widawsky 	struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev);
137*779dd20cSBen Widawsky 
138*779dd20cSBen Widawsky 	return sysfs_emit(buf, "region%u\n", atomic_read(&cxlrd->region_id));
139*779dd20cSBen Widawsky }
140*779dd20cSBen Widawsky 
141*779dd20cSBen Widawsky static ssize_t create_pmem_region_store(struct device *dev,
142*779dd20cSBen Widawsky 					struct device_attribute *attr,
143*779dd20cSBen Widawsky 					const char *buf, size_t len)
144*779dd20cSBen Widawsky {
145*779dd20cSBen Widawsky 	struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev);
146*779dd20cSBen Widawsky 	struct cxl_region *cxlr;
147*779dd20cSBen Widawsky 	int id, rc;
148*779dd20cSBen Widawsky 
149*779dd20cSBen Widawsky 	rc = sscanf(buf, "region%d\n", &id);
150*779dd20cSBen Widawsky 	if (rc != 1)
151*779dd20cSBen Widawsky 		return -EINVAL;
152*779dd20cSBen Widawsky 
153*779dd20cSBen Widawsky 	rc = memregion_alloc(GFP_KERNEL);
154*779dd20cSBen Widawsky 	if (rc < 0)
155*779dd20cSBen Widawsky 		return rc;
156*779dd20cSBen Widawsky 
157*779dd20cSBen Widawsky 	if (atomic_cmpxchg(&cxlrd->region_id, id, rc) != id) {
158*779dd20cSBen Widawsky 		memregion_free(rc);
159*779dd20cSBen Widawsky 		return -EBUSY;
160*779dd20cSBen Widawsky 	}
161*779dd20cSBen Widawsky 
162*779dd20cSBen Widawsky 	cxlr = devm_cxl_add_region(cxlrd, id, CXL_DECODER_PMEM,
163*779dd20cSBen Widawsky 				   CXL_DECODER_EXPANDER);
164*779dd20cSBen Widawsky 	if (IS_ERR(cxlr))
165*779dd20cSBen Widawsky 		return PTR_ERR(cxlr);
166*779dd20cSBen Widawsky 
167*779dd20cSBen Widawsky 	return len;
168*779dd20cSBen Widawsky }
169*779dd20cSBen Widawsky DEVICE_ATTR_RW(create_pmem_region);
170*779dd20cSBen Widawsky 
171*779dd20cSBen Widawsky static struct cxl_region *
172*779dd20cSBen Widawsky cxl_find_region_by_name(struct cxl_root_decoder *cxlrd, const char *name)
173*779dd20cSBen Widawsky {
174*779dd20cSBen Widawsky 	struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld;
175*779dd20cSBen Widawsky 	struct device *region_dev;
176*779dd20cSBen Widawsky 
177*779dd20cSBen Widawsky 	region_dev = device_find_child_by_name(&cxld->dev, name);
178*779dd20cSBen Widawsky 	if (!region_dev)
179*779dd20cSBen Widawsky 		return ERR_PTR(-ENODEV);
180*779dd20cSBen Widawsky 
181*779dd20cSBen Widawsky 	return to_cxl_region(region_dev);
182*779dd20cSBen Widawsky }
183*779dd20cSBen Widawsky 
184*779dd20cSBen Widawsky static ssize_t delete_region_store(struct device *dev,
185*779dd20cSBen Widawsky 				   struct device_attribute *attr,
186*779dd20cSBen Widawsky 				   const char *buf, size_t len)
187*779dd20cSBen Widawsky {
188*779dd20cSBen Widawsky 	struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev);
189*779dd20cSBen Widawsky 	struct cxl_port *port = to_cxl_port(dev->parent);
190*779dd20cSBen Widawsky 	struct cxl_region *cxlr;
191*779dd20cSBen Widawsky 
192*779dd20cSBen Widawsky 	cxlr = cxl_find_region_by_name(cxlrd, buf);
193*779dd20cSBen Widawsky 	if (IS_ERR(cxlr))
194*779dd20cSBen Widawsky 		return PTR_ERR(cxlr);
195*779dd20cSBen Widawsky 
196*779dd20cSBen Widawsky 	devm_release_action(port->uport, unregister_region, cxlr);
197*779dd20cSBen Widawsky 	put_device(&cxlr->dev);
198*779dd20cSBen Widawsky 
199*779dd20cSBen Widawsky 	return len;
200*779dd20cSBen Widawsky }
201*779dd20cSBen Widawsky DEVICE_ATTR_WO(delete_region);
202