Commit | Line | Data |
---|---|---|
54cdbf84 BW |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* Copyright(c) 2022 Intel Corporation. All rights reserved. */ | |
3 | #include <linux/device.h> | |
4 | #include <linux/module.h> | |
5 | #include <linux/slab.h> | |
6 | ||
7 | #include "cxlmem.h" | |
8 | #include "cxlpci.h" | |
9 | ||
10 | /** | |
11 | * DOC: cxl port | |
12 | * | |
13 | * The port driver enumerates dport via PCI and scans for HDM | |
14 | * (Host-managed-Device-Memory) decoder resources via the | |
15 | * @component_reg_phys value passed in by the agent that registered the | |
16 | * port. All descendant ports of a CXL root port (described by platform | |
17 | * firmware) are managed in this drivers context. Each driver instance | |
18 | * is responsible for tearing down the driver context of immediate | |
19 | * descendant ports. The locking for this is validated by | |
20 | * CONFIG_PROVE_CXL_LOCKING. | |
21 | * | |
22 | * The primary service this driver provides is presenting APIs to other | |
23 | * drivers to utilize the decoders, and indicating to userspace (via bind | |
24 | * status) the connectivity of the CXL.mem protocol throughout the | |
25 | * PCIe topology. | |
26 | */ | |
27 | ||
8dd2bc0f BW |
28 | static void schedule_detach(void *cxlmd) |
29 | { | |
30 | schedule_cxl_memdev_detach(cxlmd); | |
31 | } | |
32 | ||
54cdbf84 BW |
33 | static int cxl_port_probe(struct device *dev) |
34 | { | |
35 | struct cxl_port *port = to_cxl_port(dev); | |
36 | struct cxl_hdm *cxlhdm; | |
37 | int rc; | |
38 | ||
fcfbc93c DW |
39 | |
40 | if (!is_cxl_endpoint(port)) { | |
41 | rc = devm_cxl_port_enumerate_dports(port); | |
42 | if (rc < 0) | |
43 | return rc; | |
44 | if (rc == 1) | |
45 | return devm_cxl_add_passthrough_decoder(port); | |
46 | } | |
47 | ||
48 | cxlhdm = devm_cxl_setup_hdm(port); | |
49 | if (IS_ERR(cxlhdm)) | |
50 | return PTR_ERR(cxlhdm); | |
51 | ||
8dd2bc0f BW |
52 | if (is_cxl_endpoint(port)) { |
53 | struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport); | |
5e5f4ad5 | 54 | struct cxl_dev_state *cxlds = cxlmd->cxlds; |
8dd2bc0f BW |
55 | |
56 | get_device(&cxlmd->dev); | |
9b71e1c9 BW |
57 | rc = devm_add_action_or_reset(dev, schedule_detach, cxlmd); |
58 | if (rc) | |
59 | return rc; | |
5e5f4ad5 | 60 | |
fcfbc93c | 61 | rc = cxl_hdm_decode_init(cxlds, cxlhdm); |
5e5f4ad5 DW |
62 | if (rc) |
63 | return rc; | |
64 | ||
65 | rc = cxl_await_media_ready(cxlds); | |
66 | if (rc) { | |
67 | dev_err(dev, "Media not active (%d)\n", rc); | |
68 | return rc; | |
69 | } | |
8dd2bc0f BW |
70 | } |
71 | ||
664bf115 | 72 | rc = devm_cxl_enumerate_decoders(cxlhdm); |
54cdbf84 BW |
73 | if (rc) { |
74 | dev_err(dev, "Couldn't enumerate decoders (%d)\n", rc); | |
75 | return rc; | |
76 | } | |
77 | ||
78 | return 0; | |
79 | } | |
80 | ||
81 | static struct cxl_driver cxl_port_driver = { | |
82 | .name = "cxl_port", | |
83 | .probe = cxl_port_probe, | |
84 | .id = CXL_DEVICE_PORT, | |
85 | }; | |
86 | ||
87 | module_cxl_driver(cxl_port_driver); | |
88 | MODULE_LICENSE("GPL v2"); | |
89 | MODULE_IMPORT_NS(CXL); | |
90 | MODULE_ALIAS_CXL(CXL_DEVICE_PORT); |