1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright(c) 2021 Intel Corporation. All rights reserved. */
3 #include <linux/platform_device.h>
4 #include <linux/module.h>
5 #include <linux/device.h>
6 #include <linux/kernel.h>
7 #include <linux/acpi.h>
12 static unsigned long cfmws_to_decoder_flags(int restrictions)
14 unsigned long flags = CXL_DECODER_F_ENABLE;
16 if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_TYPE2)
17 flags |= CXL_DECODER_F_TYPE2;
18 if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_TYPE3)
19 flags |= CXL_DECODER_F_TYPE3;
20 if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_VOLATILE)
21 flags |= CXL_DECODER_F_RAM;
22 if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_PMEM)
23 flags |= CXL_DECODER_F_PMEM;
24 if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_FIXED)
25 flags |= CXL_DECODER_F_LOCK;
30 static int cxl_acpi_cfmws_verify(struct device *dev,
31 struct acpi_cedt_cfmws *cfmws)
36 if (cfmws->interleave_arithmetic != ACPI_CEDT_CFMWS_ARITHMETIC_MODULO) {
37 dev_err(dev, "CFMWS Unsupported Interleave Arithmetic\n");
41 if (!IS_ALIGNED(cfmws->base_hpa, SZ_256M)) {
42 dev_err(dev, "CFMWS Base HPA not 256MB aligned\n");
46 if (!IS_ALIGNED(cfmws->window_size, SZ_256M)) {
47 dev_err(dev, "CFMWS Window Size not 256MB aligned\n");
51 rc = cxl_to_ways(cfmws->interleave_ways, &ways);
53 dev_err(dev, "CFMWS Interleave Ways (%d) invalid\n",
54 cfmws->interleave_ways);
58 expected_len = struct_size(cfmws, interleave_targets, ways);
60 if (cfmws->header.length < expected_len) {
61 dev_err(dev, "CFMWS length %d less than expected %d\n",
62 cfmws->header.length, expected_len);
66 if (cfmws->header.length > expected_len)
67 dev_dbg(dev, "CFMWS length %d greater than expected %d\n",
68 cfmws->header.length, expected_len);
73 struct cxl_cfmws_context {
75 struct cxl_port *root_port;
76 struct resource *cxl_res;
80 static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg,
81 const unsigned long end)
83 int target_map[CXL_DECODER_MAX_INTERLEAVE];
84 struct cxl_cfmws_context *ctx = arg;
85 struct cxl_port *root_port = ctx->root_port;
86 struct resource *cxl_res = ctx->cxl_res;
87 struct cxl_root_decoder *cxlrd;
88 struct device *dev = ctx->dev;
89 struct acpi_cedt_cfmws *cfmws;
90 struct cxl_decoder *cxld;
91 unsigned int ways, i, ig;
95 cfmws = (struct acpi_cedt_cfmws *) header;
97 rc = cxl_acpi_cfmws_verify(dev, cfmws);
99 dev_err(dev, "CFMWS range %#llx-%#llx not registered\n",
101 cfmws->base_hpa + cfmws->window_size - 1);
105 rc = cxl_to_ways(cfmws->interleave_ways, &ways);
108 rc = cxl_to_granularity(cfmws->granularity, &ig);
111 for (i = 0; i < ways; i++)
112 target_map[i] = cfmws->interleave_targets[i];
114 res = kzalloc(sizeof(*res), GFP_KERNEL);
118 res->name = kasprintf(GFP_KERNEL, "CXL Window %d", ctx->id++);
122 res->start = cfmws->base_hpa;
123 res->end = cfmws->base_hpa + cfmws->window_size - 1;
124 res->flags = IORESOURCE_MEM;
126 /* add to the local resource tracking to establish a sort order */
127 rc = insert_resource(cxl_res, res);
131 cxlrd = cxl_root_decoder_alloc(root_port, ways);
135 cxld = &cxlrd->cxlsd.cxld;
136 cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions);
137 cxld->target_type = CXL_DECODER_EXPANDER;
138 cxld->hpa_range = (struct range) {
142 cxld->interleave_ways = ways;
144 * Minimize the x1 granularity to advertise support for any
145 * valid region granularity
148 ig = CXL_DECODER_MIN_GRANULARITY;
149 cxld->interleave_granularity = ig;
151 rc = cxl_decoder_add(cxld, target_map);
153 put_device(&cxld->dev);
155 rc = cxl_decoder_autoremove(dev, cxld);
157 dev_err(dev, "Failed to add decode range [%#llx - %#llx]\n",
158 cxld->hpa_range.start, cxld->hpa_range.end);
161 dev_dbg(dev, "add: %s node: %d range [%#llx - %#llx]\n",
162 dev_name(&cxld->dev),
163 phys_to_target_node(cxld->hpa_range.start),
164 cxld->hpa_range.start, cxld->hpa_range.end);
175 __mock struct acpi_device *to_cxl_host_bridge(struct device *host,
178 struct acpi_device *adev = to_acpi_device(dev);
180 if (!acpi_pci_find_root(adev->handle))
183 if (strcmp(acpi_device_hid(adev), "ACPI0016") == 0)
189 * A host bridge is a dport to a CFMWS decode and it is a uport to the
190 * dport (PCIe Root Ports) in the host bridge.
192 static int add_host_bridge_uport(struct device *match, void *arg)
194 struct cxl_port *root_port = arg;
195 struct device *host = root_port->dev.parent;
196 struct acpi_device *bridge = to_cxl_host_bridge(host, match);
197 struct acpi_pci_root *pci_root;
198 struct cxl_dport *dport;
199 struct cxl_port *port;
205 dport = cxl_find_dport_by_dev(root_port, match);
207 dev_dbg(host, "host bridge expected and not found\n");
212 * Note that this lookup already succeeded in
213 * to_cxl_host_bridge(), so no need to check for failure here
215 pci_root = acpi_pci_find_root(bridge->handle);
216 rc = devm_cxl_register_pci_bus(host, match, pci_root->bus);
220 port = devm_cxl_add_port(host, match, dport->component_reg_phys, dport);
222 return PTR_ERR(port);
227 struct cxl_chbs_context {
229 unsigned long long uid;
230 resource_size_t chbcr;
233 static int cxl_get_chbcr(union acpi_subtable_headers *header, void *arg,
234 const unsigned long end)
236 struct cxl_chbs_context *ctx = arg;
237 struct acpi_cedt_chbs *chbs;
242 chbs = (struct acpi_cedt_chbs *) header;
244 if (ctx->uid != chbs->uid)
246 ctx->chbcr = chbs->base;
251 static int add_host_bridge_dport(struct device *match, void *arg)
254 unsigned long long uid;
255 struct cxl_dport *dport;
256 struct cxl_chbs_context ctx;
257 struct cxl_port *root_port = arg;
258 struct device *host = root_port->dev.parent;
259 struct acpi_device *bridge = to_cxl_host_bridge(host, match);
264 status = acpi_evaluate_integer(bridge->handle, METHOD_NAME__UID, NULL,
266 if (status != AE_OK) {
267 dev_err(host, "unable to retrieve _UID of %s\n",
272 ctx = (struct cxl_chbs_context) {
276 acpi_table_parse_cedt(ACPI_CEDT_TYPE_CHBS, cxl_get_chbcr, &ctx);
278 if (ctx.chbcr == 0) {
279 dev_warn(host, "No CHBS found for Host Bridge: %s\n",
284 dport = devm_cxl_add_dport(root_port, match, uid, ctx.chbcr);
286 dev_err(host, "failed to add downstream port: %s\n",
288 return PTR_ERR(dport);
290 dev_dbg(host, "add dport%llu: %s\n", uid, dev_name(match));
294 static int add_root_nvdimm_bridge(struct device *match, void *data)
296 struct cxl_decoder *cxld;
297 struct cxl_port *root_port = data;
298 struct cxl_nvdimm_bridge *cxl_nvb;
299 struct device *host = root_port->dev.parent;
301 if (!is_root_decoder(match))
304 cxld = to_cxl_decoder(match);
305 if (!(cxld->flags & CXL_DECODER_F_PMEM))
308 cxl_nvb = devm_cxl_add_nvdimm_bridge(host, root_port);
309 if (IS_ERR(cxl_nvb)) {
310 dev_dbg(host, "failed to register pmem\n");
311 return PTR_ERR(cxl_nvb);
313 dev_dbg(host, "%s: add: %s\n", dev_name(&root_port->dev),
314 dev_name(&cxl_nvb->dev));
318 static struct lock_class_key cxl_root_key;
320 static void cxl_acpi_lock_reset_class(void *dev)
322 device_lock_reset_class(dev);
325 static void del_cxl_resource(struct resource *res)
331 static void cxl_set_public_resource(struct resource *priv, struct resource *pub)
333 priv->desc = (unsigned long) pub;
336 static struct resource *cxl_get_public_resource(struct resource *priv)
338 return (struct resource *) priv->desc;
341 static void remove_cxl_resources(void *data)
343 struct resource *res, *next, *cxl = data;
345 for (res = cxl->child; res; res = next) {
346 struct resource *victim = cxl_get_public_resource(res);
349 remove_resource(res);
352 remove_resource(victim);
356 del_cxl_resource(res);
361 * add_cxl_resources() - reflect CXL fixed memory windows in iomem_resource
362 * @cxl_res: A standalone resource tree where each CXL window is a sibling
364 * Walk each CXL window in @cxl_res and add it to iomem_resource potentially
365 * expanding its boundaries to ensure that any conflicting resources become
366 * children. If a window is expanded it may then conflict with a another window
367 * entry and require the window to be truncated or trimmed. Consider this
370 * |-- "CXL Window 0" --||----- "CXL Window 1" -----|
371 * |--------------- "System RAM" -------------|
373 * ...where platform firmware has established as System RAM resource across 2
374 * windows, but has left some portion of window 1 for dynamic CXL region
375 * provisioning. In this case "Window 0" will span the entirety of the "System
376 * RAM" span, and "CXL Window 1" is truncated to the remaining tail past the end
377 * of that "System RAM" resource.
379 static int add_cxl_resources(struct resource *cxl_res)
381 struct resource *res, *new, *next;
383 for (res = cxl_res->child; res; res = next) {
384 new = kzalloc(sizeof(*new), GFP_KERNEL);
387 new->name = res->name;
388 new->start = res->start;
390 new->flags = IORESOURCE_MEM;
391 new->desc = IORES_DESC_CXL;
394 * Record the public resource in the private cxl_res tree for
397 cxl_set_public_resource(res, new);
399 insert_resource_expand_to_fit(&iomem_resource, new);
402 while (next && resource_overlaps(new, next)) {
403 if (resource_contains(new, next)) {
404 struct resource *_next = next->sibling;
406 remove_resource(next);
407 del_cxl_resource(next);
410 next->start = new->end + 1;
416 static int pair_cxl_resource(struct device *dev, void *data)
418 struct resource *cxl_res = data;
421 if (!is_root_decoder(dev))
424 for (p = cxl_res->child; p; p = p->sibling) {
425 struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev);
426 struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld;
427 struct resource res = {
428 .start = cxld->hpa_range.start,
429 .end = cxld->hpa_range.end,
430 .flags = IORESOURCE_MEM,
433 if (resource_contains(p, &res)) {
434 cxlrd->res = cxl_get_public_resource(p);
442 static int cxl_acpi_probe(struct platform_device *pdev)
445 struct resource *cxl_res;
446 struct cxl_port *root_port;
447 struct device *host = &pdev->dev;
448 struct acpi_device *adev = ACPI_COMPANION(host);
449 struct cxl_cfmws_context ctx;
451 device_lock_set_class(&pdev->dev, &cxl_root_key);
452 rc = devm_add_action_or_reset(&pdev->dev, cxl_acpi_lock_reset_class,
457 cxl_res = devm_kzalloc(host, sizeof(*cxl_res), GFP_KERNEL);
460 cxl_res->name = "CXL mem";
463 cxl_res->flags = IORESOURCE_MEM;
465 root_port = devm_cxl_add_port(host, host, CXL_RESOURCE_NONE, NULL);
466 if (IS_ERR(root_port))
467 return PTR_ERR(root_port);
469 rc = bus_for_each_dev(adev->dev.bus, NULL, root_port,
470 add_host_bridge_dport);
474 rc = devm_add_action_or_reset(host, remove_cxl_resources, cxl_res);
478 ctx = (struct cxl_cfmws_context) {
480 .root_port = root_port,
483 rc = acpi_table_parse_cedt(ACPI_CEDT_TYPE_CFMWS, cxl_parse_cfmws, &ctx);
487 rc = add_cxl_resources(cxl_res);
492 * Populate the root decoders with their related iomem resource,
495 device_for_each_child(&root_port->dev, cxl_res, pair_cxl_resource);
498 * Root level scanned with host-bridge as dports, now scan host-bridges
499 * for their role as CXL uports to their CXL-capable PCIe Root Ports.
501 rc = bus_for_each_dev(adev->dev.bus, NULL, root_port,
502 add_host_bridge_uport);
506 if (IS_ENABLED(CONFIG_CXL_PMEM))
507 rc = device_for_each_child(&root_port->dev, root_port,
508 add_root_nvdimm_bridge);
512 /* In case PCI is scanned before ACPI re-trigger memdev attach */
513 return cxl_bus_rescan();
516 static const struct acpi_device_id cxl_acpi_ids[] = {
520 MODULE_DEVICE_TABLE(acpi, cxl_acpi_ids);
522 static const struct platform_device_id cxl_test_ids[] = {
526 MODULE_DEVICE_TABLE(platform, cxl_test_ids);
528 static struct platform_driver cxl_acpi_driver = {
529 .probe = cxl_acpi_probe,
531 .name = KBUILD_MODNAME,
532 .acpi_match_table = cxl_acpi_ids,
534 .id_table = cxl_test_ids,
537 module_platform_driver(cxl_acpi_driver);
538 MODULE_LICENSE("GPL v2");
539 MODULE_IMPORT_NS(CXL);
540 MODULE_IMPORT_NS(ACPI);