Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 // Copyright(c) 2021 Intel Corporation. All rights reserved.
0003 
0004 #include <linux/platform_device.h>
0005 #include <linux/genalloc.h>
0006 #include <linux/module.h>
0007 #include <linux/mutex.h>
0008 #include <linux/acpi.h>
0009 #include <linux/pci.h>
0010 #include <linux/mm.h>
0011 #include <cxlmem.h>
0012 #include "mock.h"
0013 
0014 #define NR_CXL_HOST_BRIDGES 2
0015 #define NR_CXL_ROOT_PORTS 2
0016 #define NR_CXL_SWITCH_PORTS 2
0017 #define NR_CXL_PORT_DECODERS 8
0018 
0019 static struct platform_device *cxl_acpi;
0020 static struct platform_device *cxl_host_bridge[NR_CXL_HOST_BRIDGES];
0021 static struct platform_device
0022     *cxl_root_port[NR_CXL_HOST_BRIDGES * NR_CXL_ROOT_PORTS];
0023 static struct platform_device
0024     *cxl_switch_uport[NR_CXL_HOST_BRIDGES * NR_CXL_ROOT_PORTS];
0025 static struct platform_device
0026     *cxl_switch_dport[NR_CXL_HOST_BRIDGES * NR_CXL_ROOT_PORTS *
0027               NR_CXL_SWITCH_PORTS];
0028 struct platform_device
0029     *cxl_mem[NR_CXL_HOST_BRIDGES * NR_CXL_ROOT_PORTS * NR_CXL_SWITCH_PORTS];
0030 
0031 static struct acpi_device acpi0017_mock;
0032 static struct acpi_device host_bridge[NR_CXL_HOST_BRIDGES] = {
0033     [0] = {
0034         .handle = &host_bridge[0],
0035     },
0036     [1] = {
0037         .handle = &host_bridge[1],
0038     },
0039 };
0040 
0041 static bool is_mock_dev(struct device *dev)
0042 {
0043     int i;
0044 
0045     for (i = 0; i < ARRAY_SIZE(cxl_mem); i++)
0046         if (dev == &cxl_mem[i]->dev)
0047             return true;
0048     if (dev == &cxl_acpi->dev)
0049         return true;
0050     return false;
0051 }
0052 
0053 static bool is_mock_adev(struct acpi_device *adev)
0054 {
0055     int i;
0056 
0057     if (adev == &acpi0017_mock)
0058         return true;
0059 
0060     for (i = 0; i < ARRAY_SIZE(host_bridge); i++)
0061         if (adev == &host_bridge[i])
0062             return true;
0063 
0064     return false;
0065 }
0066 
0067 static struct {
0068     struct acpi_table_cedt cedt;
0069     struct acpi_cedt_chbs chbs[NR_CXL_HOST_BRIDGES];
0070     struct {
0071         struct acpi_cedt_cfmws cfmws;
0072         u32 target[1];
0073     } cfmws0;
0074     struct {
0075         struct acpi_cedt_cfmws cfmws;
0076         u32 target[2];
0077     } cfmws1;
0078     struct {
0079         struct acpi_cedt_cfmws cfmws;
0080         u32 target[1];
0081     } cfmws2;
0082     struct {
0083         struct acpi_cedt_cfmws cfmws;
0084         u32 target[2];
0085     } cfmws3;
0086 } __packed mock_cedt = {
0087     .cedt = {
0088         .header = {
0089             .signature = "CEDT",
0090             .length = sizeof(mock_cedt),
0091             .revision = 1,
0092         },
0093     },
0094     .chbs[0] = {
0095         .header = {
0096             .type = ACPI_CEDT_TYPE_CHBS,
0097             .length = sizeof(mock_cedt.chbs[0]),
0098         },
0099         .uid = 0,
0100         .cxl_version = ACPI_CEDT_CHBS_VERSION_CXL20,
0101     },
0102     .chbs[1] = {
0103         .header = {
0104             .type = ACPI_CEDT_TYPE_CHBS,
0105             .length = sizeof(mock_cedt.chbs[0]),
0106         },
0107         .uid = 1,
0108         .cxl_version = ACPI_CEDT_CHBS_VERSION_CXL20,
0109     },
0110     .cfmws0 = {
0111         .cfmws = {
0112             .header = {
0113                 .type = ACPI_CEDT_TYPE_CFMWS,
0114                 .length = sizeof(mock_cedt.cfmws0),
0115             },
0116             .interleave_ways = 0,
0117             .granularity = 4,
0118             .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
0119                     ACPI_CEDT_CFMWS_RESTRICT_VOLATILE,
0120             .qtg_id = 0,
0121             .window_size = SZ_256M * 4UL,
0122         },
0123         .target = { 0 },
0124     },
0125     .cfmws1 = {
0126         .cfmws = {
0127             .header = {
0128                 .type = ACPI_CEDT_TYPE_CFMWS,
0129                 .length = sizeof(mock_cedt.cfmws1),
0130             },
0131             .interleave_ways = 1,
0132             .granularity = 4,
0133             .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
0134                     ACPI_CEDT_CFMWS_RESTRICT_VOLATILE,
0135             .qtg_id = 1,
0136             .window_size = SZ_256M * 8UL,
0137         },
0138         .target = { 0, 1, },
0139     },
0140     .cfmws2 = {
0141         .cfmws = {
0142             .header = {
0143                 .type = ACPI_CEDT_TYPE_CFMWS,
0144                 .length = sizeof(mock_cedt.cfmws2),
0145             },
0146             .interleave_ways = 0,
0147             .granularity = 4,
0148             .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
0149                     ACPI_CEDT_CFMWS_RESTRICT_PMEM,
0150             .qtg_id = 2,
0151             .window_size = SZ_256M * 4UL,
0152         },
0153         .target = { 0 },
0154     },
0155     .cfmws3 = {
0156         .cfmws = {
0157             .header = {
0158                 .type = ACPI_CEDT_TYPE_CFMWS,
0159                 .length = sizeof(mock_cedt.cfmws3),
0160             },
0161             .interleave_ways = 1,
0162             .granularity = 4,
0163             .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
0164                     ACPI_CEDT_CFMWS_RESTRICT_PMEM,
0165             .qtg_id = 3,
0166             .window_size = SZ_256M * 8UL,
0167         },
0168         .target = { 0, 1, },
0169     },
0170 };
0171 
0172 struct acpi_cedt_cfmws *mock_cfmws[4] = {
0173     [0] = &mock_cedt.cfmws0.cfmws,
0174     [1] = &mock_cedt.cfmws1.cfmws,
0175     [2] = &mock_cedt.cfmws2.cfmws,
0176     [3] = &mock_cedt.cfmws3.cfmws,
0177 };
0178 
0179 struct cxl_mock_res {
0180     struct list_head list;
0181     struct range range;
0182 };
0183 
0184 static LIST_HEAD(mock_res);
0185 static DEFINE_MUTEX(mock_res_lock);
0186 static struct gen_pool *cxl_mock_pool;
0187 
0188 static void depopulate_all_mock_resources(void)
0189 {
0190     struct cxl_mock_res *res, *_res;
0191 
0192     mutex_lock(&mock_res_lock);
0193     list_for_each_entry_safe(res, _res, &mock_res, list) {
0194         gen_pool_free(cxl_mock_pool, res->range.start,
0195                   range_len(&res->range));
0196         list_del(&res->list);
0197         kfree(res);
0198     }
0199     mutex_unlock(&mock_res_lock);
0200 }
0201 
0202 static struct cxl_mock_res *alloc_mock_res(resource_size_t size)
0203 {
0204     struct cxl_mock_res *res = kzalloc(sizeof(*res), GFP_KERNEL);
0205     struct genpool_data_align data = {
0206         .align = SZ_256M,
0207     };
0208     unsigned long phys;
0209 
0210     INIT_LIST_HEAD(&res->list);
0211     phys = gen_pool_alloc_algo(cxl_mock_pool, size,
0212                    gen_pool_first_fit_align, &data);
0213     if (!phys)
0214         return NULL;
0215 
0216     res->range = (struct range) {
0217         .start = phys,
0218         .end = phys + size - 1,
0219     };
0220     mutex_lock(&mock_res_lock);
0221     list_add(&res->list, &mock_res);
0222     mutex_unlock(&mock_res_lock);
0223 
0224     return res;
0225 }
0226 
0227 static int populate_cedt(void)
0228 {
0229     struct cxl_mock_res *res;
0230     int i;
0231 
0232     for (i = 0; i < ARRAY_SIZE(mock_cedt.chbs); i++) {
0233         struct acpi_cedt_chbs *chbs = &mock_cedt.chbs[i];
0234         resource_size_t size;
0235 
0236         if (chbs->cxl_version == ACPI_CEDT_CHBS_VERSION_CXL20)
0237             size = ACPI_CEDT_CHBS_LENGTH_CXL20;
0238         else
0239             size = ACPI_CEDT_CHBS_LENGTH_CXL11;
0240 
0241         res = alloc_mock_res(size);
0242         if (!res)
0243             return -ENOMEM;
0244         chbs->base = res->range.start;
0245         chbs->length = size;
0246     }
0247 
0248     for (i = 0; i < ARRAY_SIZE(mock_cfmws); i++) {
0249         struct acpi_cedt_cfmws *window = mock_cfmws[i];
0250 
0251         res = alloc_mock_res(window->window_size);
0252         if (!res)
0253             return -ENOMEM;
0254         window->base_hpa = res->range.start;
0255     }
0256 
0257     return 0;
0258 }
0259 
0260 /*
0261  * WARNING, this hack assumes the format of 'struct
0262  * cxl_cfmws_context' and 'struct cxl_chbs_context' share the property that
0263  * the first struct member is the device being probed by the cxl_acpi
0264  * driver.
0265  */
0266 struct cxl_cedt_context {
0267     struct device *dev;
0268 };
0269 
0270 static int mock_acpi_table_parse_cedt(enum acpi_cedt_type id,
0271                       acpi_tbl_entry_handler_arg handler_arg,
0272                       void *arg)
0273 {
0274     struct cxl_cedt_context *ctx = arg;
0275     struct device *dev = ctx->dev;
0276     union acpi_subtable_headers *h;
0277     unsigned long end;
0278     int i;
0279 
0280     if (dev != &cxl_acpi->dev)
0281         return acpi_table_parse_cedt(id, handler_arg, arg);
0282 
0283     if (id == ACPI_CEDT_TYPE_CHBS)
0284         for (i = 0; i < ARRAY_SIZE(mock_cedt.chbs); i++) {
0285             h = (union acpi_subtable_headers *)&mock_cedt.chbs[i];
0286             end = (unsigned long)&mock_cedt.chbs[i + 1];
0287             handler_arg(h, arg, end);
0288         }
0289 
0290     if (id == ACPI_CEDT_TYPE_CFMWS)
0291         for (i = 0; i < ARRAY_SIZE(mock_cfmws); i++) {
0292             h = (union acpi_subtable_headers *) mock_cfmws[i];
0293             end = (unsigned long) h + mock_cfmws[i]->header.length;
0294             handler_arg(h, arg, end);
0295         }
0296 
0297     return 0;
0298 }
0299 
0300 static bool is_mock_bridge(struct device *dev)
0301 {
0302     int i;
0303 
0304     for (i = 0; i < ARRAY_SIZE(cxl_host_bridge); i++)
0305         if (dev == &cxl_host_bridge[i]->dev)
0306             return true;
0307     return false;
0308 }
0309 
0310 static bool is_mock_port(struct device *dev)
0311 {
0312     int i;
0313 
0314     if (is_mock_bridge(dev))
0315         return true;
0316 
0317     for (i = 0; i < ARRAY_SIZE(cxl_root_port); i++)
0318         if (dev == &cxl_root_port[i]->dev)
0319             return true;
0320 
0321     for (i = 0; i < ARRAY_SIZE(cxl_switch_uport); i++)
0322         if (dev == &cxl_switch_uport[i]->dev)
0323             return true;
0324 
0325     for (i = 0; i < ARRAY_SIZE(cxl_switch_dport); i++)
0326         if (dev == &cxl_switch_dport[i]->dev)
0327             return true;
0328 
0329     if (is_cxl_memdev(dev))
0330         return is_mock_dev(dev->parent);
0331 
0332     return false;
0333 }
0334 
0335 static int host_bridge_index(struct acpi_device *adev)
0336 {
0337     return adev - host_bridge;
0338 }
0339 
0340 static struct acpi_device *find_host_bridge(acpi_handle handle)
0341 {
0342     int i;
0343 
0344     for (i = 0; i < ARRAY_SIZE(host_bridge); i++)
0345         if (handle == host_bridge[i].handle)
0346             return &host_bridge[i];
0347     return NULL;
0348 }
0349 
0350 static acpi_status
0351 mock_acpi_evaluate_integer(acpi_handle handle, acpi_string pathname,
0352                struct acpi_object_list *arguments,
0353                unsigned long long *data)
0354 {
0355     struct acpi_device *adev = find_host_bridge(handle);
0356 
0357     if (!adev || strcmp(pathname, METHOD_NAME__UID) != 0)
0358         return acpi_evaluate_integer(handle, pathname, arguments, data);
0359 
0360     *data = host_bridge_index(adev);
0361     return AE_OK;
0362 }
0363 
0364 static struct pci_bus mock_pci_bus[NR_CXL_HOST_BRIDGES];
0365 static struct acpi_pci_root mock_pci_root[NR_CXL_HOST_BRIDGES] = {
0366     [0] = {
0367         .bus = &mock_pci_bus[0],
0368     },
0369     [1] = {
0370         .bus = &mock_pci_bus[1],
0371     },
0372 };
0373 
0374 static bool is_mock_bus(struct pci_bus *bus)
0375 {
0376     int i;
0377 
0378     for (i = 0; i < ARRAY_SIZE(mock_pci_bus); i++)
0379         if (bus == &mock_pci_bus[i])
0380             return true;
0381     return false;
0382 }
0383 
0384 static struct acpi_pci_root *mock_acpi_pci_find_root(acpi_handle handle)
0385 {
0386     struct acpi_device *adev = find_host_bridge(handle);
0387 
0388     if (!adev)
0389         return acpi_pci_find_root(handle);
0390     return &mock_pci_root[host_bridge_index(adev)];
0391 }
0392 
0393 static struct cxl_hdm *mock_cxl_setup_hdm(struct cxl_port *port)
0394 {
0395     struct cxl_hdm *cxlhdm = devm_kzalloc(&port->dev, sizeof(*cxlhdm), GFP_KERNEL);
0396 
0397     if (!cxlhdm)
0398         return ERR_PTR(-ENOMEM);
0399 
0400     cxlhdm->port = port;
0401     return cxlhdm;
0402 }
0403 
0404 static int mock_cxl_add_passthrough_decoder(struct cxl_port *port)
0405 {
0406     dev_err(&port->dev, "unexpected passthrough decoder for cxl_test\n");
0407     return -EOPNOTSUPP;
0408 }
0409 
0410 
0411 struct target_map_ctx {
0412     int *target_map;
0413     int index;
0414     int target_count;
0415 };
0416 
0417 static int map_targets(struct device *dev, void *data)
0418 {
0419     struct platform_device *pdev = to_platform_device(dev);
0420     struct target_map_ctx *ctx = data;
0421 
0422     ctx->target_map[ctx->index++] = pdev->id;
0423 
0424     if (ctx->index > ctx->target_count) {
0425         dev_WARN_ONCE(dev, 1, "too many targets found?\n");
0426         return -ENXIO;
0427     }
0428 
0429     return 0;
0430 }
0431 
0432 static int mock_decoder_commit(struct cxl_decoder *cxld)
0433 {
0434     struct cxl_port *port = to_cxl_port(cxld->dev.parent);
0435     int id = cxld->id;
0436 
0437     if (cxld->flags & CXL_DECODER_F_ENABLE)
0438         return 0;
0439 
0440     dev_dbg(&port->dev, "%s commit\n", dev_name(&cxld->dev));
0441     if (port->commit_end + 1 != id) {
0442         dev_dbg(&port->dev,
0443             "%s: out of order commit, expected decoder%d.%d\n",
0444             dev_name(&cxld->dev), port->id, port->commit_end + 1);
0445         return -EBUSY;
0446     }
0447 
0448     port->commit_end++;
0449     cxld->flags |= CXL_DECODER_F_ENABLE;
0450 
0451     return 0;
0452 }
0453 
0454 static int mock_decoder_reset(struct cxl_decoder *cxld)
0455 {
0456     struct cxl_port *port = to_cxl_port(cxld->dev.parent);
0457     int id = cxld->id;
0458 
0459     if ((cxld->flags & CXL_DECODER_F_ENABLE) == 0)
0460         return 0;
0461 
0462     dev_dbg(&port->dev, "%s reset\n", dev_name(&cxld->dev));
0463     if (port->commit_end != id) {
0464         dev_dbg(&port->dev,
0465             "%s: out of order reset, expected decoder%d.%d\n",
0466             dev_name(&cxld->dev), port->id, port->commit_end);
0467         return -EBUSY;
0468     }
0469 
0470     port->commit_end--;
0471     cxld->flags &= ~CXL_DECODER_F_ENABLE;
0472 
0473     return 0;
0474 }
0475 
0476 static int mock_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm)
0477 {
0478     struct cxl_port *port = cxlhdm->port;
0479     struct cxl_port *parent_port = to_cxl_port(port->dev.parent);
0480     int target_count, i;
0481 
0482     if (is_cxl_endpoint(port))
0483         target_count = 0;
0484     else if (is_cxl_root(parent_port))
0485         target_count = NR_CXL_ROOT_PORTS;
0486     else
0487         target_count = NR_CXL_SWITCH_PORTS;
0488 
0489     for (i = 0; i < NR_CXL_PORT_DECODERS; i++) {
0490         int target_map[CXL_DECODER_MAX_INTERLEAVE] = { 0 };
0491         struct target_map_ctx ctx = {
0492             .target_map = target_map,
0493             .target_count = target_count,
0494         };
0495         struct cxl_decoder *cxld;
0496         int rc;
0497 
0498         if (target_count) {
0499             struct cxl_switch_decoder *cxlsd;
0500 
0501             cxlsd = cxl_switch_decoder_alloc(port, target_count);
0502             if (IS_ERR(cxlsd)) {
0503                 dev_warn(&port->dev,
0504                      "Failed to allocate the decoder\n");
0505                 return PTR_ERR(cxlsd);
0506             }
0507             cxld = &cxlsd->cxld;
0508         } else {
0509             struct cxl_endpoint_decoder *cxled;
0510 
0511             cxled = cxl_endpoint_decoder_alloc(port);
0512 
0513             if (IS_ERR(cxled)) {
0514                 dev_warn(&port->dev,
0515                      "Failed to allocate the decoder\n");
0516                 return PTR_ERR(cxled);
0517             }
0518             cxld = &cxled->cxld;
0519         }
0520 
0521         cxld->hpa_range = (struct range) {
0522             .start = 0,
0523             .end = -1,
0524         };
0525 
0526         cxld->interleave_ways = min_not_zero(target_count, 1);
0527         cxld->interleave_granularity = SZ_4K;
0528         cxld->target_type = CXL_DECODER_EXPANDER;
0529         cxld->commit = mock_decoder_commit;
0530         cxld->reset = mock_decoder_reset;
0531 
0532         if (target_count) {
0533             rc = device_for_each_child(port->uport, &ctx,
0534                            map_targets);
0535             if (rc) {
0536                 put_device(&cxld->dev);
0537                 return rc;
0538             }
0539         }
0540 
0541         rc = cxl_decoder_add_locked(cxld, target_map);
0542         if (rc) {
0543             put_device(&cxld->dev);
0544             dev_err(&port->dev, "Failed to add decoder\n");
0545             return rc;
0546         }
0547 
0548         rc = cxl_decoder_autoremove(&port->dev, cxld);
0549         if (rc)
0550             return rc;
0551         dev_dbg(&cxld->dev, "Added to port %s\n", dev_name(&port->dev));
0552     }
0553 
0554     return 0;
0555 }
0556 
0557 static int mock_cxl_port_enumerate_dports(struct cxl_port *port)
0558 {
0559     struct device *dev = &port->dev;
0560     struct platform_device **array;
0561     int i, array_size;
0562 
0563     if (port->depth == 1) {
0564         array_size = ARRAY_SIZE(cxl_root_port);
0565         array = cxl_root_port;
0566     } else if (port->depth == 2) {
0567         array_size = ARRAY_SIZE(cxl_switch_dport);
0568         array = cxl_switch_dport;
0569     } else {
0570         dev_WARN_ONCE(&port->dev, 1, "unexpected depth %d\n",
0571                   port->depth);
0572         return -ENXIO;
0573     }
0574 
0575     for (i = 0; i < array_size; i++) {
0576         struct platform_device *pdev = array[i];
0577         struct cxl_dport *dport;
0578 
0579         if (pdev->dev.parent != port->uport)
0580             continue;
0581 
0582         dport = devm_cxl_add_dport(port, &pdev->dev, pdev->id,
0583                        CXL_RESOURCE_NONE);
0584 
0585         if (IS_ERR(dport)) {
0586             dev_err(dev, "failed to add dport: %s (%ld)\n",
0587                 dev_name(&pdev->dev), PTR_ERR(dport));
0588             return PTR_ERR(dport);
0589         }
0590 
0591         dev_dbg(dev, "add dport%d: %s\n", pdev->id,
0592             dev_name(&pdev->dev));
0593     }
0594 
0595     return 0;
0596 }
0597 
0598 static struct cxl_mock_ops cxl_mock_ops = {
0599     .is_mock_adev = is_mock_adev,
0600     .is_mock_bridge = is_mock_bridge,
0601     .is_mock_bus = is_mock_bus,
0602     .is_mock_port = is_mock_port,
0603     .is_mock_dev = is_mock_dev,
0604     .acpi_table_parse_cedt = mock_acpi_table_parse_cedt,
0605     .acpi_evaluate_integer = mock_acpi_evaluate_integer,
0606     .acpi_pci_find_root = mock_acpi_pci_find_root,
0607     .devm_cxl_port_enumerate_dports = mock_cxl_port_enumerate_dports,
0608     .devm_cxl_setup_hdm = mock_cxl_setup_hdm,
0609     .devm_cxl_add_passthrough_decoder = mock_cxl_add_passthrough_decoder,
0610     .devm_cxl_enumerate_decoders = mock_cxl_enumerate_decoders,
0611     .list = LIST_HEAD_INIT(cxl_mock_ops.list),
0612 };
0613 
0614 static void mock_companion(struct acpi_device *adev, struct device *dev)
0615 {
0616     device_initialize(&adev->dev);
0617     fwnode_init(&adev->fwnode, NULL);
0618     dev->fwnode = &adev->fwnode;
0619     adev->fwnode.dev = dev;
0620 }
0621 
0622 #ifndef SZ_64G
0623 #define SZ_64G (SZ_32G * 2)
0624 #endif
0625 
0626 #ifndef SZ_512G
0627 #define SZ_512G (SZ_64G * 8)
0628 #endif
0629 
0630 static __init int cxl_test_init(void)
0631 {
0632     int rc, i;
0633 
0634     register_cxl_mock_ops(&cxl_mock_ops);
0635 
0636     cxl_mock_pool = gen_pool_create(ilog2(SZ_2M), NUMA_NO_NODE);
0637     if (!cxl_mock_pool) {
0638         rc = -ENOMEM;
0639         goto err_gen_pool_create;
0640     }
0641 
0642     rc = gen_pool_add(cxl_mock_pool, iomem_resource.end + 1 - SZ_64G,
0643               SZ_64G, NUMA_NO_NODE);
0644     if (rc)
0645         goto err_gen_pool_add;
0646 
0647     rc = populate_cedt();
0648     if (rc)
0649         goto err_populate;
0650 
0651     for (i = 0; i < ARRAY_SIZE(cxl_host_bridge); i++) {
0652         struct acpi_device *adev = &host_bridge[i];
0653         struct platform_device *pdev;
0654 
0655         pdev = platform_device_alloc("cxl_host_bridge", i);
0656         if (!pdev)
0657             goto err_bridge;
0658 
0659         mock_companion(adev, &pdev->dev);
0660         rc = platform_device_add(pdev);
0661         if (rc) {
0662             platform_device_put(pdev);
0663             goto err_bridge;
0664         }
0665 
0666         cxl_host_bridge[i] = pdev;
0667         rc = sysfs_create_link(&pdev->dev.kobj, &pdev->dev.kobj,
0668                        "physical_node");
0669         if (rc)
0670             goto err_bridge;
0671     }
0672 
0673     for (i = 0; i < ARRAY_SIZE(cxl_root_port); i++) {
0674         struct platform_device *bridge =
0675             cxl_host_bridge[i % ARRAY_SIZE(cxl_host_bridge)];
0676         struct platform_device *pdev;
0677 
0678         pdev = platform_device_alloc("cxl_root_port", i);
0679         if (!pdev)
0680             goto err_port;
0681         pdev->dev.parent = &bridge->dev;
0682 
0683         rc = platform_device_add(pdev);
0684         if (rc) {
0685             platform_device_put(pdev);
0686             goto err_port;
0687         }
0688         cxl_root_port[i] = pdev;
0689     }
0690 
0691     BUILD_BUG_ON(ARRAY_SIZE(cxl_switch_uport) != ARRAY_SIZE(cxl_root_port));
0692     for (i = 0; i < ARRAY_SIZE(cxl_switch_uport); i++) {
0693         struct platform_device *root_port = cxl_root_port[i];
0694         struct platform_device *pdev;
0695 
0696         pdev = platform_device_alloc("cxl_switch_uport", i);
0697         if (!pdev)
0698             goto err_port;
0699         pdev->dev.parent = &root_port->dev;
0700 
0701         rc = platform_device_add(pdev);
0702         if (rc) {
0703             platform_device_put(pdev);
0704             goto err_uport;
0705         }
0706         cxl_switch_uport[i] = pdev;
0707     }
0708 
0709     for (i = 0; i < ARRAY_SIZE(cxl_switch_dport); i++) {
0710         struct platform_device *uport =
0711             cxl_switch_uport[i % ARRAY_SIZE(cxl_switch_uport)];
0712         struct platform_device *pdev;
0713 
0714         pdev = platform_device_alloc("cxl_switch_dport", i);
0715         if (!pdev)
0716             goto err_port;
0717         pdev->dev.parent = &uport->dev;
0718 
0719         rc = platform_device_add(pdev);
0720         if (rc) {
0721             platform_device_put(pdev);
0722             goto err_dport;
0723         }
0724         cxl_switch_dport[i] = pdev;
0725     }
0726 
0727     BUILD_BUG_ON(ARRAY_SIZE(cxl_mem) != ARRAY_SIZE(cxl_switch_dport));
0728     for (i = 0; i < ARRAY_SIZE(cxl_mem); i++) {
0729         struct platform_device *dport = cxl_switch_dport[i];
0730         struct platform_device *pdev;
0731 
0732         pdev = platform_device_alloc("cxl_mem", i);
0733         if (!pdev)
0734             goto err_mem;
0735         pdev->dev.parent = &dport->dev;
0736         set_dev_node(&pdev->dev, i % 2);
0737 
0738         rc = platform_device_add(pdev);
0739         if (rc) {
0740             platform_device_put(pdev);
0741             goto err_mem;
0742         }
0743         cxl_mem[i] = pdev;
0744     }
0745 
0746     cxl_acpi = platform_device_alloc("cxl_acpi", 0);
0747     if (!cxl_acpi)
0748         goto err_mem;
0749 
0750     mock_companion(&acpi0017_mock, &cxl_acpi->dev);
0751     acpi0017_mock.dev.bus = &platform_bus_type;
0752 
0753     rc = platform_device_add(cxl_acpi);
0754     if (rc)
0755         goto err_add;
0756 
0757     return 0;
0758 
0759 err_add:
0760     platform_device_put(cxl_acpi);
0761 err_mem:
0762     for (i = ARRAY_SIZE(cxl_mem) - 1; i >= 0; i--)
0763         platform_device_unregister(cxl_mem[i]);
0764 err_dport:
0765     for (i = ARRAY_SIZE(cxl_switch_dport) - 1; i >= 0; i--)
0766         platform_device_unregister(cxl_switch_dport[i]);
0767 err_uport:
0768     for (i = ARRAY_SIZE(cxl_switch_uport) - 1; i >= 0; i--)
0769         platform_device_unregister(cxl_switch_uport[i]);
0770 err_port:
0771     for (i = ARRAY_SIZE(cxl_root_port) - 1; i >= 0; i--)
0772         platform_device_unregister(cxl_root_port[i]);
0773 err_bridge:
0774     for (i = ARRAY_SIZE(cxl_host_bridge) - 1; i >= 0; i--) {
0775         struct platform_device *pdev = cxl_host_bridge[i];
0776 
0777         if (!pdev)
0778             continue;
0779         sysfs_remove_link(&pdev->dev.kobj, "physical_node");
0780         platform_device_unregister(cxl_host_bridge[i]);
0781     }
0782 err_populate:
0783     depopulate_all_mock_resources();
0784 err_gen_pool_add:
0785     gen_pool_destroy(cxl_mock_pool);
0786 err_gen_pool_create:
0787     unregister_cxl_mock_ops(&cxl_mock_ops);
0788     return rc;
0789 }
0790 
0791 static __exit void cxl_test_exit(void)
0792 {
0793     int i;
0794 
0795     platform_device_unregister(cxl_acpi);
0796     for (i = ARRAY_SIZE(cxl_mem) - 1; i >= 0; i--)
0797         platform_device_unregister(cxl_mem[i]);
0798     for (i = ARRAY_SIZE(cxl_switch_dport) - 1; i >= 0; i--)
0799         platform_device_unregister(cxl_switch_dport[i]);
0800     for (i = ARRAY_SIZE(cxl_switch_uport) - 1; i >= 0; i--)
0801         platform_device_unregister(cxl_switch_uport[i]);
0802     for (i = ARRAY_SIZE(cxl_root_port) - 1; i >= 0; i--)
0803         platform_device_unregister(cxl_root_port[i]);
0804     for (i = ARRAY_SIZE(cxl_host_bridge) - 1; i >= 0; i--) {
0805         struct platform_device *pdev = cxl_host_bridge[i];
0806 
0807         if (!pdev)
0808             continue;
0809         sysfs_remove_link(&pdev->dev.kobj, "physical_node");
0810         platform_device_unregister(cxl_host_bridge[i]);
0811     }
0812     depopulate_all_mock_resources();
0813     gen_pool_destroy(cxl_mock_pool);
0814     unregister_cxl_mock_ops(&cxl_mock_ops);
0815 }
0816 
0817 module_init(cxl_test_init);
0818 module_exit(cxl_test_exit);
0819 MODULE_LICENSE("GPL v2");
0820 MODULE_IMPORT_NS(ACPI);
0821 MODULE_IMPORT_NS(CXL);