0001
0002
0003 #include <linux/platform_device.h>
0004 #include <linux/module.h>
0005 #include <linux/device.h>
0006 #include <linux/kernel.h>
0007 #include <linux/acpi.h>
0008 #include <linux/pci.h>
0009 #include "cxlpci.h"
0010 #include "cxl.h"
0011
0012 static unsigned long cfmws_to_decoder_flags(int restrictions)
0013 {
0014 unsigned long flags = CXL_DECODER_F_ENABLE;
0015
0016 if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_TYPE2)
0017 flags |= CXL_DECODER_F_TYPE2;
0018 if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_TYPE3)
0019 flags |= CXL_DECODER_F_TYPE3;
0020 if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_VOLATILE)
0021 flags |= CXL_DECODER_F_RAM;
0022 if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_PMEM)
0023 flags |= CXL_DECODER_F_PMEM;
0024 if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_FIXED)
0025 flags |= CXL_DECODER_F_LOCK;
0026
0027 return flags;
0028 }
0029
0030 static int cxl_acpi_cfmws_verify(struct device *dev,
0031 struct acpi_cedt_cfmws *cfmws)
0032 {
0033 int rc, expected_len;
0034 unsigned int ways;
0035
0036 if (cfmws->interleave_arithmetic != ACPI_CEDT_CFMWS_ARITHMETIC_MODULO) {
0037 dev_err(dev, "CFMWS Unsupported Interleave Arithmetic\n");
0038 return -EINVAL;
0039 }
0040
0041 if (!IS_ALIGNED(cfmws->base_hpa, SZ_256M)) {
0042 dev_err(dev, "CFMWS Base HPA not 256MB aligned\n");
0043 return -EINVAL;
0044 }
0045
0046 if (!IS_ALIGNED(cfmws->window_size, SZ_256M)) {
0047 dev_err(dev, "CFMWS Window Size not 256MB aligned\n");
0048 return -EINVAL;
0049 }
0050
0051 rc = cxl_to_ways(cfmws->interleave_ways, &ways);
0052 if (rc) {
0053 dev_err(dev, "CFMWS Interleave Ways (%d) invalid\n",
0054 cfmws->interleave_ways);
0055 return -EINVAL;
0056 }
0057
0058 expected_len = struct_size(cfmws, interleave_targets, ways);
0059
0060 if (cfmws->header.length < expected_len) {
0061 dev_err(dev, "CFMWS length %d less than expected %d\n",
0062 cfmws->header.length, expected_len);
0063 return -EINVAL;
0064 }
0065
0066 if (cfmws->header.length > expected_len)
0067 dev_dbg(dev, "CFMWS length %d greater than expected %d\n",
0068 cfmws->header.length, expected_len);
0069
0070 return 0;
0071 }
0072
0073 struct cxl_cfmws_context {
0074 struct device *dev;
0075 struct cxl_port *root_port;
0076 struct resource *cxl_res;
0077 int id;
0078 };
0079
0080 static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg,
0081 const unsigned long end)
0082 {
0083 int target_map[CXL_DECODER_MAX_INTERLEAVE];
0084 struct cxl_cfmws_context *ctx = arg;
0085 struct cxl_port *root_port = ctx->root_port;
0086 struct resource *cxl_res = ctx->cxl_res;
0087 struct cxl_root_decoder *cxlrd;
0088 struct device *dev = ctx->dev;
0089 struct acpi_cedt_cfmws *cfmws;
0090 struct cxl_decoder *cxld;
0091 unsigned int ways, i, ig;
0092 struct resource *res;
0093 int rc;
0094
0095 cfmws = (struct acpi_cedt_cfmws *) header;
0096
0097 rc = cxl_acpi_cfmws_verify(dev, cfmws);
0098 if (rc) {
0099 dev_err(dev, "CFMWS range %#llx-%#llx not registered\n",
0100 cfmws->base_hpa,
0101 cfmws->base_hpa + cfmws->window_size - 1);
0102 return 0;
0103 }
0104
0105 rc = cxl_to_ways(cfmws->interleave_ways, &ways);
0106 if (rc)
0107 return rc;
0108 rc = cxl_to_granularity(cfmws->granularity, &ig);
0109 if (rc)
0110 return rc;
0111 for (i = 0; i < ways; i++)
0112 target_map[i] = cfmws->interleave_targets[i];
0113
0114 res = kzalloc(sizeof(*res), GFP_KERNEL);
0115 if (!res)
0116 return -ENOMEM;
0117
0118 res->name = kasprintf(GFP_KERNEL, "CXL Window %d", ctx->id++);
0119 if (!res->name)
0120 goto err_name;
0121
0122 res->start = cfmws->base_hpa;
0123 res->end = cfmws->base_hpa + cfmws->window_size - 1;
0124 res->flags = IORESOURCE_MEM;
0125
0126
0127 rc = insert_resource(cxl_res, res);
0128 if (rc)
0129 goto err_insert;
0130
0131 cxlrd = cxl_root_decoder_alloc(root_port, ways);
0132 if (IS_ERR(cxlrd))
0133 return 0;
0134
0135 cxld = &cxlrd->cxlsd.cxld;
0136 cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions);
0137 cxld->target_type = CXL_DECODER_EXPANDER;
0138 cxld->hpa_range = (struct range) {
0139 .start = res->start,
0140 .end = res->end,
0141 };
0142 cxld->interleave_ways = ways;
0143
0144
0145
0146
0147 if (ways == 1)
0148 ig = CXL_DECODER_MIN_GRANULARITY;
0149 cxld->interleave_granularity = ig;
0150
0151 rc = cxl_decoder_add(cxld, target_map);
0152 if (rc)
0153 put_device(&cxld->dev);
0154 else
0155 rc = cxl_decoder_autoremove(dev, cxld);
0156 if (rc) {
0157 dev_err(dev, "Failed to add decode range [%#llx - %#llx]\n",
0158 cxld->hpa_range.start, cxld->hpa_range.end);
0159 return 0;
0160 }
0161 dev_dbg(dev, "add: %s node: %d range [%#llx - %#llx]\n",
0162 dev_name(&cxld->dev),
0163 phys_to_target_node(cxld->hpa_range.start),
0164 cxld->hpa_range.start, cxld->hpa_range.end);
0165
0166 return 0;
0167
0168 err_insert:
0169 kfree(res->name);
0170 err_name:
0171 kfree(res);
0172 return -ENOMEM;
0173 }
0174
0175 __mock struct acpi_device *to_cxl_host_bridge(struct device *host,
0176 struct device *dev)
0177 {
0178 struct acpi_device *adev = to_acpi_device(dev);
0179
0180 if (!acpi_pci_find_root(adev->handle))
0181 return NULL;
0182
0183 if (strcmp(acpi_device_hid(adev), "ACPI0016") == 0)
0184 return adev;
0185 return NULL;
0186 }
0187
0188
0189
0190
0191
0192 static int add_host_bridge_uport(struct device *match, void *arg)
0193 {
0194 struct cxl_port *root_port = arg;
0195 struct device *host = root_port->dev.parent;
0196 struct acpi_device *bridge = to_cxl_host_bridge(host, match);
0197 struct acpi_pci_root *pci_root;
0198 struct cxl_dport *dport;
0199 struct cxl_port *port;
0200 int rc;
0201
0202 if (!bridge)
0203 return 0;
0204
0205 dport = cxl_find_dport_by_dev(root_port, match);
0206 if (!dport) {
0207 dev_dbg(host, "host bridge expected and not found\n");
0208 return 0;
0209 }
0210
0211
0212
0213
0214
0215 pci_root = acpi_pci_find_root(bridge->handle);
0216 rc = devm_cxl_register_pci_bus(host, match, pci_root->bus);
0217 if (rc)
0218 return rc;
0219
0220 port = devm_cxl_add_port(host, match, dport->component_reg_phys, dport);
0221 if (IS_ERR(port))
0222 return PTR_ERR(port);
0223 dev_dbg(host, "%s: add: %s\n", dev_name(match), dev_name(&port->dev));
0224
0225 return 0;
0226 }
0227
0228 struct cxl_chbs_context {
0229 struct device *dev;
0230 unsigned long long uid;
0231 resource_size_t chbcr;
0232 };
0233
0234 static int cxl_get_chbcr(union acpi_subtable_headers *header, void *arg,
0235 const unsigned long end)
0236 {
0237 struct cxl_chbs_context *ctx = arg;
0238 struct acpi_cedt_chbs *chbs;
0239
0240 if (ctx->chbcr)
0241 return 0;
0242
0243 chbs = (struct acpi_cedt_chbs *) header;
0244
0245 if (ctx->uid != chbs->uid)
0246 return 0;
0247 ctx->chbcr = chbs->base;
0248
0249 return 0;
0250 }
0251
0252 static int add_host_bridge_dport(struct device *match, void *arg)
0253 {
0254 acpi_status status;
0255 unsigned long long uid;
0256 struct cxl_dport *dport;
0257 struct cxl_chbs_context ctx;
0258 struct cxl_port *root_port = arg;
0259 struct device *host = root_port->dev.parent;
0260 struct acpi_device *bridge = to_cxl_host_bridge(host, match);
0261
0262 if (!bridge)
0263 return 0;
0264
0265 status = acpi_evaluate_integer(bridge->handle, METHOD_NAME__UID, NULL,
0266 &uid);
0267 if (status != AE_OK) {
0268 dev_err(host, "unable to retrieve _UID of %s\n",
0269 dev_name(match));
0270 return -ENODEV;
0271 }
0272
0273 ctx = (struct cxl_chbs_context) {
0274 .dev = host,
0275 .uid = uid,
0276 };
0277 acpi_table_parse_cedt(ACPI_CEDT_TYPE_CHBS, cxl_get_chbcr, &ctx);
0278
0279 if (ctx.chbcr == 0) {
0280 dev_warn(host, "No CHBS found for Host Bridge: %s\n",
0281 dev_name(match));
0282 return 0;
0283 }
0284
0285 dport = devm_cxl_add_dport(root_port, match, uid, ctx.chbcr);
0286 if (IS_ERR(dport)) {
0287 dev_err(host, "failed to add downstream port: %s\n",
0288 dev_name(match));
0289 return PTR_ERR(dport);
0290 }
0291 dev_dbg(host, "add dport%llu: %s\n", uid, dev_name(match));
0292 return 0;
0293 }
0294
0295 static int add_root_nvdimm_bridge(struct device *match, void *data)
0296 {
0297 struct cxl_decoder *cxld;
0298 struct cxl_port *root_port = data;
0299 struct cxl_nvdimm_bridge *cxl_nvb;
0300 struct device *host = root_port->dev.parent;
0301
0302 if (!is_root_decoder(match))
0303 return 0;
0304
0305 cxld = to_cxl_decoder(match);
0306 if (!(cxld->flags & CXL_DECODER_F_PMEM))
0307 return 0;
0308
0309 cxl_nvb = devm_cxl_add_nvdimm_bridge(host, root_port);
0310 if (IS_ERR(cxl_nvb)) {
0311 dev_dbg(host, "failed to register pmem\n");
0312 return PTR_ERR(cxl_nvb);
0313 }
0314 dev_dbg(host, "%s: add: %s\n", dev_name(&root_port->dev),
0315 dev_name(&cxl_nvb->dev));
0316 return 1;
0317 }
0318
0319 static struct lock_class_key cxl_root_key;
0320
0321 static void cxl_acpi_lock_reset_class(void *dev)
0322 {
0323 device_lock_reset_class(dev);
0324 }
0325
0326 static void del_cxl_resource(struct resource *res)
0327 {
0328 kfree(res->name);
0329 kfree(res);
0330 }
0331
0332 static void cxl_set_public_resource(struct resource *priv, struct resource *pub)
0333 {
0334 priv->desc = (unsigned long) pub;
0335 }
0336
0337 static struct resource *cxl_get_public_resource(struct resource *priv)
0338 {
0339 return (struct resource *) priv->desc;
0340 }
0341
0342 static void remove_cxl_resources(void *data)
0343 {
0344 struct resource *res, *next, *cxl = data;
0345
0346 for (res = cxl->child; res; res = next) {
0347 struct resource *victim = cxl_get_public_resource(res);
0348
0349 next = res->sibling;
0350 remove_resource(res);
0351
0352 if (victim) {
0353 remove_resource(victim);
0354 kfree(victim);
0355 }
0356
0357 del_cxl_resource(res);
0358 }
0359 }
0360
0361
0362
0363
0364
0365
0366
0367
0368
0369
0370
0371
0372
0373
0374
0375
0376
0377
0378
0379
0380 static int add_cxl_resources(struct resource *cxl_res)
0381 {
0382 struct resource *res, *new, *next;
0383
0384 for (res = cxl_res->child; res; res = next) {
0385 new = kzalloc(sizeof(*new), GFP_KERNEL);
0386 if (!new)
0387 return -ENOMEM;
0388 new->name = res->name;
0389 new->start = res->start;
0390 new->end = res->end;
0391 new->flags = IORESOURCE_MEM;
0392 new->desc = IORES_DESC_CXL;
0393
0394
0395
0396
0397
0398 cxl_set_public_resource(res, new);
0399
0400 insert_resource_expand_to_fit(&iomem_resource, new);
0401
0402 next = res->sibling;
0403 while (next && resource_overlaps(new, next)) {
0404 if (resource_contains(new, next)) {
0405 struct resource *_next = next->sibling;
0406
0407 remove_resource(next);
0408 del_cxl_resource(next);
0409 next = _next;
0410 } else
0411 next->start = new->end + 1;
0412 }
0413 }
0414 return 0;
0415 }
0416
0417 static int pair_cxl_resource(struct device *dev, void *data)
0418 {
0419 struct resource *cxl_res = data;
0420 struct resource *p;
0421
0422 if (!is_root_decoder(dev))
0423 return 0;
0424
0425 for (p = cxl_res->child; p; p = p->sibling) {
0426 struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev);
0427 struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld;
0428 struct resource res = {
0429 .start = cxld->hpa_range.start,
0430 .end = cxld->hpa_range.end,
0431 .flags = IORESOURCE_MEM,
0432 };
0433
0434 if (resource_contains(p, &res)) {
0435 cxlrd->res = cxl_get_public_resource(p);
0436 break;
0437 }
0438 }
0439
0440 return 0;
0441 }
0442
0443 static int cxl_acpi_probe(struct platform_device *pdev)
0444 {
0445 int rc;
0446 struct resource *cxl_res;
0447 struct cxl_port *root_port;
0448 struct device *host = &pdev->dev;
0449 struct acpi_device *adev = ACPI_COMPANION(host);
0450 struct cxl_cfmws_context ctx;
0451
0452 device_lock_set_class(&pdev->dev, &cxl_root_key);
0453 rc = devm_add_action_or_reset(&pdev->dev, cxl_acpi_lock_reset_class,
0454 &pdev->dev);
0455 if (rc)
0456 return rc;
0457
0458 cxl_res = devm_kzalloc(host, sizeof(*cxl_res), GFP_KERNEL);
0459 if (!cxl_res)
0460 return -ENOMEM;
0461 cxl_res->name = "CXL mem";
0462 cxl_res->start = 0;
0463 cxl_res->end = -1;
0464 cxl_res->flags = IORESOURCE_MEM;
0465
0466 root_port = devm_cxl_add_port(host, host, CXL_RESOURCE_NONE, NULL);
0467 if (IS_ERR(root_port))
0468 return PTR_ERR(root_port);
0469 dev_dbg(host, "add: %s\n", dev_name(&root_port->dev));
0470
0471 rc = bus_for_each_dev(adev->dev.bus, NULL, root_port,
0472 add_host_bridge_dport);
0473 if (rc < 0)
0474 return rc;
0475
0476 rc = devm_add_action_or_reset(host, remove_cxl_resources, cxl_res);
0477 if (rc)
0478 return rc;
0479
0480 ctx = (struct cxl_cfmws_context) {
0481 .dev = host,
0482 .root_port = root_port,
0483 .cxl_res = cxl_res,
0484 };
0485 rc = acpi_table_parse_cedt(ACPI_CEDT_TYPE_CFMWS, cxl_parse_cfmws, &ctx);
0486 if (rc < 0)
0487 return -ENXIO;
0488
0489 rc = add_cxl_resources(cxl_res);
0490 if (rc)
0491 return rc;
0492
0493
0494
0495
0496
0497 device_for_each_child(&root_port->dev, cxl_res, pair_cxl_resource);
0498
0499
0500
0501
0502
0503 rc = bus_for_each_dev(adev->dev.bus, NULL, root_port,
0504 add_host_bridge_uport);
0505 if (rc < 0)
0506 return rc;
0507
0508 if (IS_ENABLED(CONFIG_CXL_PMEM))
0509 rc = device_for_each_child(&root_port->dev, root_port,
0510 add_root_nvdimm_bridge);
0511 if (rc < 0)
0512 return rc;
0513
0514
0515 return cxl_bus_rescan();
0516 }
0517
0518 static const struct acpi_device_id cxl_acpi_ids[] = {
0519 { "ACPI0017" },
0520 { },
0521 };
0522 MODULE_DEVICE_TABLE(acpi, cxl_acpi_ids);
0523
0524 static const struct platform_device_id cxl_test_ids[] = {
0525 { "cxl_acpi" },
0526 { },
0527 };
0528 MODULE_DEVICE_TABLE(platform, cxl_test_ids);
0529
0530 static struct platform_driver cxl_acpi_driver = {
0531 .probe = cxl_acpi_probe,
0532 .driver = {
0533 .name = KBUILD_MODNAME,
0534 .acpi_match_table = cxl_acpi_ids,
0535 },
0536 .id_table = cxl_test_ids,
0537 };
0538
0539 module_platform_driver(cxl_acpi_driver);
0540 MODULE_LICENSE("GPL v2");
0541 MODULE_IMPORT_NS(CXL);
0542 MODULE_IMPORT_NS(ACPI);