blob: fb649683dd3ac7bfb08da8047f879497312f4e04 [file] [log] [blame]
Dan Williams4812be92021-06-09 09:01:35 -07001// 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>
Dan Williams3b94ce72021-06-09 09:01:51 -07008#include <linux/pci.h>
Dan Williamsaf9cae92022-01-23 16:30:25 -08009#include "cxlpci.h"
Dan Williams4812be92021-06-09 09:01:35 -070010#include "cxl.h"
11
Alison Schofield3e23d172021-06-17 16:12:16 -070012static unsigned long cfmws_to_decoder_flags(int restrictions)
13{
Dan Williams0909b4e2022-01-25 21:24:04 -080014 unsigned long flags = CXL_DECODER_F_ENABLE;
Alison Schofield3e23d172021-06-17 16:12:16 -070015
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;
26
27 return flags;
28}
29
30static int cxl_acpi_cfmws_verify(struct device *dev,
31 struct acpi_cedt_cfmws *cfmws)
32{
Dan Williams419af592022-05-22 17:04:27 -070033 int rc, expected_len;
34 unsigned int ways;
Alison Schofield3e23d172021-06-17 16:12:16 -070035
36 if (cfmws->interleave_arithmetic != ACPI_CEDT_CFMWS_ARITHMETIC_MODULO) {
37 dev_err(dev, "CFMWS Unsupported Interleave Arithmetic\n");
38 return -EINVAL;
39 }
40
41 if (!IS_ALIGNED(cfmws->base_hpa, SZ_256M)) {
42 dev_err(dev, "CFMWS Base HPA not 256MB aligned\n");
43 return -EINVAL;
44 }
45
46 if (!IS_ALIGNED(cfmws->window_size, SZ_256M)) {
47 dev_err(dev, "CFMWS Window Size not 256MB aligned\n");
48 return -EINVAL;
49 }
50
Dan Williams419af592022-05-22 17:04:27 -070051 rc = cxl_to_ways(cfmws->interleave_ways, &ways);
52 if (rc) {
53 dev_err(dev, "CFMWS Interleave Ways (%d) invalid\n",
54 cfmws->interleave_ways);
Dan Williamsa5c25802021-09-08 22:13:10 -070055 return -EINVAL;
56 }
57
Dan Williams419af592022-05-22 17:04:27 -070058 expected_len = struct_size(cfmws, interleave_targets, ways);
Alison Schofield3e23d172021-06-17 16:12:16 -070059
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);
63 return -EINVAL;
64 }
65
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);
69
70 return 0;
71}
72
Dan Williamsf4ce1f72021-10-29 12:51:48 -070073struct cxl_cfmws_context {
74 struct device *dev;
75 struct cxl_port *root_port;
Dan Williams974854a2022-07-12 18:37:54 -070076 struct resource *cxl_res;
77 int id;
Dan Williamsf4ce1f72021-10-29 12:51:48 -070078};
79
80static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg,
81 const unsigned long end)
Alison Schofield3e23d172021-06-17 16:12:16 -070082{
Dan Williamsa5c25802021-09-08 22:13:10 -070083 int target_map[CXL_DECODER_MAX_INTERLEAVE];
Dan Williamsf4ce1f72021-10-29 12:51:48 -070084 struct cxl_cfmws_context *ctx = arg;
85 struct cxl_port *root_port = ctx->root_port;
Dan Williams974854a2022-07-12 18:37:54 -070086 struct resource *cxl_res = ctx->cxl_res;
Dan Williams0f157c72022-07-12 18:38:26 -070087 struct cxl_root_decoder *cxlrd;
Dan Williamsf4ce1f72021-10-29 12:51:48 -070088 struct device *dev = ctx->dev;
Alison Schofield3e23d172021-06-17 16:12:16 -070089 struct acpi_cedt_cfmws *cfmws;
90 struct cxl_decoder *cxld;
Dan Williams419af592022-05-22 17:04:27 -070091 unsigned int ways, i, ig;
Dan Williams974854a2022-07-12 18:37:54 -070092 struct resource *res;
Dan Williams419af592022-05-22 17:04:27 -070093 int rc;
Alison Schofield3e23d172021-06-17 16:12:16 -070094
Dan Williamsf4ce1f72021-10-29 12:51:48 -070095 cfmws = (struct acpi_cedt_cfmws *) header;
Alison Schofield3e23d172021-06-17 16:12:16 -070096
Dan Williamsf4ce1f72021-10-29 12:51:48 -070097 rc = cxl_acpi_cfmws_verify(dev, cfmws);
98 if (rc) {
99 dev_err(dev, "CFMWS range %#llx-%#llx not registered\n",
100 cfmws->base_hpa,
Dan Williams48667f62021-09-21 12:22:16 -0700101 cfmws->base_hpa + cfmws->window_size - 1);
Dan Williamsf4ce1f72021-10-29 12:51:48 -0700102 return 0;
Alison Schofieldda6aafe2021-06-17 16:12:15 -0700103 }
104
Dan Williams419af592022-05-22 17:04:27 -0700105 rc = cxl_to_ways(cfmws->interleave_ways, &ways);
106 if (rc)
107 return rc;
108 rc = cxl_to_granularity(cfmws->granularity, &ig);
109 if (rc)
110 return rc;
111 for (i = 0; i < ways; i++)
Dan Williamsf4ce1f72021-10-29 12:51:48 -0700112 target_map[i] = cfmws->interleave_targets[i];
Alison Schofieldda6aafe2021-06-17 16:12:15 -0700113
Dan Williams974854a2022-07-12 18:37:54 -0700114 res = kzalloc(sizeof(*res), GFP_KERNEL);
115 if (!res)
116 return -ENOMEM;
117
118 res->name = kasprintf(GFP_KERNEL, "CXL Window %d", ctx->id++);
119 if (!res->name)
120 goto err_name;
121
122 res->start = cfmws->base_hpa;
123 res->end = cfmws->base_hpa + cfmws->window_size - 1;
124 res->flags = IORESOURCE_MEM;
125
126 /* add to the local resource tracking to establish a sort order */
127 rc = insert_resource(cxl_res, res);
128 if (rc)
129 goto err_insert;
130
Dan Williams0f157c72022-07-12 18:38:26 -0700131 cxlrd = cxl_root_decoder_alloc(root_port, ways);
132 if (IS_ERR(cxlrd))
Dan Williamsf4ce1f72021-10-29 12:51:48 -0700133 return 0;
134
Dan Williams0f157c72022-07-12 18:38:26 -0700135 cxld = &cxlrd->cxlsd.cxld;
Dan Williamsf4ce1f72021-10-29 12:51:48 -0700136 cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions);
137 cxld->target_type = CXL_DECODER_EXPANDER;
Dan Williamse50fe012022-05-18 18:02:39 -0700138 cxld->hpa_range = (struct range) {
Dan Williams974854a2022-07-12 18:37:54 -0700139 .start = res->start,
140 .end = res->end,
Dan Williamse50fe012022-05-18 18:02:39 -0700141 };
Dan Williams419af592022-05-22 17:04:27 -0700142 cxld->interleave_ways = ways;
Dan Williamse7748302022-07-22 17:56:09 -0700143 /*
144 * Minimize the x1 granularity to advertise support for any
145 * valid region granularity
146 */
147 if (ways == 1)
148 ig = CXL_DECODER_MIN_GRANULARITY;
Dan Williams419af592022-05-22 17:04:27 -0700149 cxld->interleave_granularity = ig;
Dan Williamsf4ce1f72021-10-29 12:51:48 -0700150
151 rc = cxl_decoder_add(cxld, target_map);
152 if (rc)
153 put_device(&cxld->dev);
154 else
155 rc = cxl_decoder_autoremove(dev, cxld);
156 if (rc) {
Dan Williamse50fe012022-05-18 18:02:39 -0700157 dev_err(dev, "Failed to add decode range [%#llx - %#llx]\n",
158 cxld->hpa_range.start, cxld->hpa_range.end);
Dan Williamsf4ce1f72021-10-29 12:51:48 -0700159 return 0;
160 }
Dan Williamse50fe012022-05-18 18:02:39 -0700161 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);
Dan Williamsf4ce1f72021-10-29 12:51:48 -0700165
166 return 0;
Dan Williams974854a2022-07-12 18:37:54 -0700167
168err_insert:
169 kfree(res->name);
170err_name:
171 kfree(res);
172 return -ENOMEM;
Alison Schofieldda6aafe2021-06-17 16:12:15 -0700173}
174
Dan Williams67dcdd42021-09-14 12:14:22 -0700175__mock struct acpi_device *to_cxl_host_bridge(struct device *host,
176 struct device *dev)
Dan Williams7d4b5ca2021-06-09 09:01:46 -0700177{
178 struct acpi_device *adev = to_acpi_device(dev);
179
Alison Schofielda7bfaad2021-09-03 19:20:39 -0700180 if (!acpi_pci_find_root(adev->handle))
181 return NULL;
182
Dan Williams7d4b5ca2021-06-09 09:01:46 -0700183 if (strcmp(acpi_device_hid(adev), "ACPI0016") == 0)
184 return adev;
185 return NULL;
186}
187
Dan Williams3b94ce72021-06-09 09:01:51 -0700188/*
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.
191 */
192static int add_host_bridge_uport(struct device *match, void *arg)
193{
Dan Williams3b94ce72021-06-09 09:01:51 -0700194 struct cxl_port *root_port = arg;
195 struct device *host = root_port->dev.parent;
Dan Williams67dcdd42021-09-14 12:14:22 -0700196 struct acpi_device *bridge = to_cxl_host_bridge(host, match);
Dan Williams3b94ce72021-06-09 09:01:51 -0700197 struct acpi_pci_root *pci_root;
Alison Schofieldda6aafe2021-06-17 16:12:15 -0700198 struct cxl_dport *dport;
Dan Williams3b94ce72021-06-09 09:01:51 -0700199 struct cxl_port *port;
Dan Williamsd17d0542022-02-01 12:24:30 -0800200 int rc;
Dan Williams3b94ce72021-06-09 09:01:51 -0700201
202 if (!bridge)
203 return 0;
204
Dan Williams2703c162022-02-04 07:08:40 -0800205 dport = cxl_find_dport_by_dev(root_port, match);
Alison Schofieldda6aafe2021-06-17 16:12:15 -0700206 if (!dport) {
207 dev_dbg(host, "host bridge expected and not found\n");
Alison Schofield91a45b12021-10-07 14:34:26 -0700208 return 0;
Alison Schofieldda6aafe2021-06-17 16:12:15 -0700209 }
210
Dan Williams5ff73162022-01-31 08:44:52 -0800211 /*
212 * Note that this lookup already succeeded in
213 * to_cxl_host_bridge(), so no need to check for failure here
214 */
215 pci_root = acpi_pci_find_root(bridge->handle);
216 rc = devm_cxl_register_pci_bus(host, match, pci_root->bus);
217 if (rc)
218 return rc;
219
Dan Williams1b58b4c2022-05-27 10:57:01 -0700220 port = devm_cxl_add_port(host, match, dport->component_reg_phys, dport);
Dan Williams3b94ce72021-06-09 09:01:51 -0700221 if (IS_ERR(port))
222 return PTR_ERR(port);
223 dev_dbg(host, "%s: add: %s\n", dev_name(match), dev_name(&port->dev));
224
Ben Widawsky54cdbf82022-02-01 13:07:51 -0800225 return 0;
Dan Williams3b94ce72021-06-09 09:01:51 -0700226}
227
Dan Williamsf4ce1f72021-10-29 12:51:48 -0700228struct cxl_chbs_context {
Dan Williams814dff92021-10-29 12:51:53 -0700229 struct device *dev;
Dan Williamsf4ce1f72021-10-29 12:51:48 -0700230 unsigned long long uid;
231 resource_size_t chbcr;
232};
233
234static int cxl_get_chbcr(union acpi_subtable_headers *header, void *arg,
235 const unsigned long end)
236{
237 struct cxl_chbs_context *ctx = arg;
238 struct acpi_cedt_chbs *chbs;
239
240 if (ctx->chbcr)
241 return 0;
242
243 chbs = (struct acpi_cedt_chbs *) header;
244
245 if (ctx->uid != chbs->uid)
246 return 0;
247 ctx->chbcr = chbs->base;
248
249 return 0;
250}
251
Dan Williams7d4b5ca2021-06-09 09:01:46 -0700252static int add_host_bridge_dport(struct device *match, void *arg)
253{
Dan Williams7d4b5ca2021-06-09 09:01:46 -0700254 acpi_status status;
255 unsigned long long uid;
Dan Williams98d2d3a2022-01-31 18:10:04 -0800256 struct cxl_dport *dport;
Dan Williamsf4ce1f72021-10-29 12:51:48 -0700257 struct cxl_chbs_context ctx;
Dan Williams7d4b5ca2021-06-09 09:01:46 -0700258 struct cxl_port *root_port = arg;
259 struct device *host = root_port->dev.parent;
Dan Williams67dcdd42021-09-14 12:14:22 -0700260 struct acpi_device *bridge = to_cxl_host_bridge(host, match);
Dan Williams7d4b5ca2021-06-09 09:01:46 -0700261
262 if (!bridge)
263 return 0;
264
265 status = acpi_evaluate_integer(bridge->handle, METHOD_NAME__UID, NULL,
266 &uid);
267 if (status != AE_OK) {
268 dev_err(host, "unable to retrieve _UID of %s\n",
269 dev_name(match));
270 return -ENODEV;
271 }
272
Dan Williamsf4ce1f72021-10-29 12:51:48 -0700273 ctx = (struct cxl_chbs_context) {
Dan Williams814dff92021-10-29 12:51:53 -0700274 .dev = host,
Dan Williamsf4ce1f72021-10-29 12:51:48 -0700275 .uid = uid,
276 };
277 acpi_table_parse_cedt(ACPI_CEDT_TYPE_CHBS, cxl_get_chbcr, &ctx);
278
279 if (ctx.chbcr == 0) {
Alison Schofield91a45b12021-10-07 14:34:26 -0700280 dev_warn(host, "No CHBS found for Host Bridge: %s\n",
281 dev_name(match));
282 return 0;
283 }
Alison Schofieldda6aafe2021-06-17 16:12:15 -0700284
Dan Williams664bf1152022-02-01 13:23:14 -0800285 dport = devm_cxl_add_dport(root_port, match, uid, ctx.chbcr);
Dan Williams98d2d3a2022-01-31 18:10:04 -0800286 if (IS_ERR(dport)) {
Dan Williams7d4b5ca2021-06-09 09:01:46 -0700287 dev_err(host, "failed to add downstream port: %s\n",
288 dev_name(match));
Dan Williams98d2d3a2022-01-31 18:10:04 -0800289 return PTR_ERR(dport);
Dan Williams7d4b5ca2021-06-09 09:01:46 -0700290 }
291 dev_dbg(host, "add dport%llu: %s\n", uid, dev_name(match));
292 return 0;
293}
294
Dan Williams8fdcb172021-06-15 16:18:17 -0700295static int add_root_nvdimm_bridge(struct device *match, void *data)
296{
297 struct cxl_decoder *cxld;
298 struct cxl_port *root_port = data;
299 struct cxl_nvdimm_bridge *cxl_nvb;
300 struct device *host = root_port->dev.parent;
301
302 if (!is_root_decoder(match))
303 return 0;
304
305 cxld = to_cxl_decoder(match);
306 if (!(cxld->flags & CXL_DECODER_F_PMEM))
307 return 0;
308
309 cxl_nvb = devm_cxl_add_nvdimm_bridge(host, root_port);
310 if (IS_ERR(cxl_nvb)) {
311 dev_dbg(host, "failed to register pmem\n");
312 return PTR_ERR(cxl_nvb);
313 }
314 dev_dbg(host, "%s: add: %s\n", dev_name(&root_port->dev),
315 dev_name(&cxl_nvb->dev));
316 return 1;
317}
318
Dan Williamsd864b8e2022-04-26 12:22:44 -0700319static struct lock_class_key cxl_root_key;
320
321static void cxl_acpi_lock_reset_class(void *dev)
322{
323 device_lock_reset_class(dev);
324}
325
Dan Williams974854a2022-07-12 18:37:54 -0700326static void del_cxl_resource(struct resource *res)
327{
328 kfree(res->name);
329 kfree(res);
330}
331
332static void cxl_set_public_resource(struct resource *priv, struct resource *pub)
333{
334 priv->desc = (unsigned long) pub;
335}
336
337static struct resource *cxl_get_public_resource(struct resource *priv)
338{
339 return (struct resource *) priv->desc;
340}
341
342static void remove_cxl_resources(void *data)
343{
344 struct resource *res, *next, *cxl = data;
345
346 for (res = cxl->child; res; res = next) {
347 struct resource *victim = cxl_get_public_resource(res);
348
349 next = res->sibling;
350 remove_resource(res);
351
352 if (victim) {
353 remove_resource(victim);
354 kfree(victim);
355 }
356
357 del_cxl_resource(res);
358 }
359}
360
361/**
362 * add_cxl_resources() - reflect CXL fixed memory windows in iomem_resource
363 * @cxl_res: A standalone resource tree where each CXL window is a sibling
364 *
365 * Walk each CXL window in @cxl_res and add it to iomem_resource potentially
366 * expanding its boundaries to ensure that any conflicting resources become
367 * children. If a window is expanded it may then conflict with a another window
368 * entry and require the window to be truncated or trimmed. Consider this
369 * situation:
370 *
371 * |-- "CXL Window 0" --||----- "CXL Window 1" -----|
372 * |--------------- "System RAM" -------------|
373 *
374 * ...where platform firmware has established as System RAM resource across 2
375 * windows, but has left some portion of window 1 for dynamic CXL region
376 * provisioning. In this case "Window 0" will span the entirety of the "System
377 * RAM" span, and "CXL Window 1" is truncated to the remaining tail past the end
378 * of that "System RAM" resource.
379 */
380static int add_cxl_resources(struct resource *cxl_res)
381{
382 struct resource *res, *new, *next;
383
384 for (res = cxl_res->child; res; res = next) {
385 new = kzalloc(sizeof(*new), GFP_KERNEL);
386 if (!new)
387 return -ENOMEM;
388 new->name = res->name;
389 new->start = res->start;
390 new->end = res->end;
391 new->flags = IORESOURCE_MEM;
392 new->desc = IORES_DESC_CXL;
393
394 /*
395 * Record the public resource in the private cxl_res tree for
396 * later removal.
397 */
398 cxl_set_public_resource(res, new);
399
400 insert_resource_expand_to_fit(&iomem_resource, new);
401
402 next = res->sibling;
403 while (next && resource_overlaps(new, next)) {
404 if (resource_contains(new, next)) {
405 struct resource *_next = next->sibling;
406
407 remove_resource(next);
408 del_cxl_resource(next);
409 next = _next;
410 } else
411 next->start = new->end + 1;
412 }
413 }
414 return 0;
415}
416
Dan Williams0f157c72022-07-12 18:38:26 -0700417static int pair_cxl_resource(struct device *dev, void *data)
418{
419 struct resource *cxl_res = data;
420 struct resource *p;
421
422 if (!is_root_decoder(dev))
423 return 0;
424
425 for (p = cxl_res->child; p; p = p->sibling) {
426 struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev);
427 struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld;
428 struct resource res = {
429 .start = cxld->hpa_range.start,
430 .end = cxld->hpa_range.end,
431 .flags = IORESOURCE_MEM,
432 };
433
434 if (resource_contains(p, &res)) {
435 cxlrd->res = cxl_get_public_resource(p);
436 break;
437 }
438 }
439
440 return 0;
441}
442
Dan Williams4812be92021-06-09 09:01:35 -0700443static int cxl_acpi_probe(struct platform_device *pdev)
444{
Dan Williams3b94ce72021-06-09 09:01:51 -0700445 int rc;
Dan Williams974854a2022-07-12 18:37:54 -0700446 struct resource *cxl_res;
Dan Williams4812be92021-06-09 09:01:35 -0700447 struct cxl_port *root_port;
448 struct device *host = &pdev->dev;
Dan Williams7d4b5ca2021-06-09 09:01:46 -0700449 struct acpi_device *adev = ACPI_COMPANION(host);
Dan Williamsf4ce1f72021-10-29 12:51:48 -0700450 struct cxl_cfmws_context ctx;
Dan Williams4812be92021-06-09 09:01:35 -0700451
Dan Williamsd864b8e2022-04-26 12:22:44 -0700452 device_lock_set_class(&pdev->dev, &cxl_root_key);
453 rc = devm_add_action_or_reset(&pdev->dev, cxl_acpi_lock_reset_class,
454 &pdev->dev);
455 if (rc)
456 return rc;
457
Dan Williams974854a2022-07-12 18:37:54 -0700458 cxl_res = devm_kzalloc(host, sizeof(*cxl_res), GFP_KERNEL);
459 if (!cxl_res)
460 return -ENOMEM;
461 cxl_res->name = "CXL mem";
462 cxl_res->start = 0;
463 cxl_res->end = -1;
464 cxl_res->flags = IORESOURCE_MEM;
465
Dan Williams4812be92021-06-09 09:01:35 -0700466 root_port = devm_cxl_add_port(host, host, CXL_RESOURCE_NONE, NULL);
467 if (IS_ERR(root_port))
468 return PTR_ERR(root_port);
469 dev_dbg(host, "add: %s\n", dev_name(&root_port->dev));
470
Dan Williams3b94ce72021-06-09 09:01:51 -0700471 rc = bus_for_each_dev(adev->dev.bus, NULL, root_port,
472 add_host_bridge_dport);
Dan Williamsf4ce1f72021-10-29 12:51:48 -0700473 if (rc < 0)
474 return rc;
Dan Williams3b94ce72021-06-09 09:01:51 -0700475
Dan Williams974854a2022-07-12 18:37:54 -0700476 rc = devm_add_action_or_reset(host, remove_cxl_resources, cxl_res);
477 if (rc)
478 return rc;
479
Dan Williamsf4ce1f72021-10-29 12:51:48 -0700480 ctx = (struct cxl_cfmws_context) {
481 .dev = host,
482 .root_port = root_port,
Dan Williams974854a2022-07-12 18:37:54 -0700483 .cxl_res = cxl_res,
Dan Williamsf4ce1f72021-10-29 12:51:48 -0700484 };
Dan Williams974854a2022-07-12 18:37:54 -0700485 rc = acpi_table_parse_cedt(ACPI_CEDT_TYPE_CFMWS, cxl_parse_cfmws, &ctx);
486 if (rc < 0)
487 return -ENXIO;
488
489 rc = add_cxl_resources(cxl_res);
490 if (rc)
491 return rc;
Alison Schofield3e23d172021-06-17 16:12:16 -0700492
Dan Williams3b94ce72021-06-09 09:01:51 -0700493 /*
Dan Williams0f157c72022-07-12 18:38:26 -0700494 * Populate the root decoders with their related iomem resource,
495 * if present
496 */
497 device_for_each_child(&root_port->dev, cxl_res, pair_cxl_resource);
498
499 /*
Dan Williams3b94ce72021-06-09 09:01:51 -0700500 * Root level scanned with host-bridge as dports, now scan host-bridges
501 * for their role as CXL uports to their CXL-capable PCIe Root Ports.
502 */
Dan Williams8fdcb172021-06-15 16:18:17 -0700503 rc = bus_for_each_dev(adev->dev.bus, NULL, root_port,
504 add_host_bridge_uport);
Dan Williamsf4ce1f72021-10-29 12:51:48 -0700505 if (rc < 0)
506 return rc;
Dan Williams8fdcb172021-06-15 16:18:17 -0700507
508 if (IS_ENABLED(CONFIG_CXL_PMEM))
509 rc = device_for_each_child(&root_port->dev, root_port,
510 add_root_nvdimm_bridge);
511 if (rc < 0)
512 return rc;
Dan Williamsf4ce1f72021-10-29 12:51:48 -0700513
Ben Widawsky8dd2bc02022-02-04 07:18:31 -0800514 /* In case PCI is scanned before ACPI re-trigger memdev attach */
515 return cxl_bus_rescan();
Dan Williams4812be92021-06-09 09:01:35 -0700516}
517
518static const struct acpi_device_id cxl_acpi_ids[] = {
Dan Williamsf4ce1f72021-10-29 12:51:48 -0700519 { "ACPI0017" },
Dan Williams67dcdd42021-09-14 12:14:22 -0700520 { },
Dan Williams4812be92021-06-09 09:01:35 -0700521};
522MODULE_DEVICE_TABLE(acpi, cxl_acpi_ids);
523
Dan Williamsa53c28b2022-07-22 17:55:57 -0700524static const struct platform_device_id cxl_test_ids[] = {
525 { "cxl_acpi" },
526 { },
527};
528MODULE_DEVICE_TABLE(platform, cxl_test_ids);
529
Dan Williams4812be92021-06-09 09:01:35 -0700530static struct platform_driver cxl_acpi_driver = {
531 .probe = cxl_acpi_probe,
532 .driver = {
533 .name = KBUILD_MODNAME,
534 .acpi_match_table = cxl_acpi_ids,
535 },
Dan Williamsa53c28b2022-07-22 17:55:57 -0700536 .id_table = cxl_test_ids,
Dan Williams4812be92021-06-09 09:01:35 -0700537};
538
539module_platform_driver(cxl_acpi_driver);
540MODULE_LICENSE("GPL v2");
541MODULE_IMPORT_NS(CXL);
Dan Williamsf4ce1f72021-10-29 12:51:48 -0700542MODULE_IMPORT_NS(ACPI);