xref: /linux/drivers/cxl/core/pci.c (revision 3f0a50f345f78183f6e9b39c2f45ca5dcaa511ca)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright(c) 2021 Intel Corporation. All rights reserved. */
3 #include <linux/device.h>
4 #include <linux/pci.h>
5 #include <cxlpci.h>
6 #include <cxl.h>
7 #include "core.h"
8 
9 /**
10  * DOC: cxl core pci
11  *
12  * Compute Express Link protocols are layered on top of PCIe. CXL core provides
13  * a set of helpers for CXL interactions which occur via PCIe.
14  */
15 
16 struct cxl_walk_context {
17 	struct pci_bus *bus;
18 	struct cxl_port *port;
19 	int type;
20 	int error;
21 	int count;
22 };
23 
24 static int match_add_dports(struct pci_dev *pdev, void *data)
25 {
26 	struct cxl_walk_context *ctx = data;
27 	struct cxl_port *port = ctx->port;
28 	int type = pci_pcie_type(pdev);
29 	struct cxl_register_map map;
30 	struct cxl_dport *dport;
31 	u32 lnkcap, port_num;
32 	int rc;
33 
34 	if (pdev->bus != ctx->bus)
35 		return 0;
36 	if (!pci_is_pcie(pdev))
37 		return 0;
38 	if (type != ctx->type)
39 		return 0;
40 	if (pci_read_config_dword(pdev, pci_pcie_cap(pdev) + PCI_EXP_LNKCAP,
41 				  &lnkcap))
42 		return 0;
43 
44 	rc = cxl_find_regblock(pdev, CXL_REGLOC_RBI_COMPONENT, &map);
45 	if (rc)
46 		dev_dbg(&port->dev, "failed to find component registers\n");
47 
48 	port_num = FIELD_GET(PCI_EXP_LNKCAP_PN, lnkcap);
49 	dport = devm_cxl_add_dport(port, &pdev->dev, port_num,
50 				   cxl_regmap_to_base(pdev, &map));
51 	if (IS_ERR(dport)) {
52 		ctx->error = PTR_ERR(dport);
53 		return PTR_ERR(dport);
54 	}
55 	ctx->count++;
56 
57 	dev_dbg(&port->dev, "add dport%d: %s\n", port_num, dev_name(&pdev->dev));
58 
59 	return 0;
60 }
61 
62 /**
63  * devm_cxl_port_enumerate_dports - enumerate downstream ports of the upstream port
64  * @port: cxl_port whose ->uport is the upstream of dports to be enumerated
65  *
66  * Returns a positive number of dports enumerated or a negative error
67  * code.
68  */
69 int devm_cxl_port_enumerate_dports(struct cxl_port *port)
70 {
71 	struct pci_bus *bus = cxl_port_to_pci_bus(port);
72 	struct cxl_walk_context ctx;
73 	int type;
74 
75 	if (!bus)
76 		return -ENXIO;
77 
78 	if (pci_is_root_bus(bus))
79 		type = PCI_EXP_TYPE_ROOT_PORT;
80 	else
81 		type = PCI_EXP_TYPE_DOWNSTREAM;
82 
83 	ctx = (struct cxl_walk_context) {
84 		.port = port,
85 		.bus = bus,
86 		.type = type,
87 	};
88 	pci_walk_bus(bus, match_add_dports, &ctx);
89 
90 	if (ctx.count == 0)
91 		return -ENODEV;
92 	if (ctx.error)
93 		return ctx.error;
94 	return ctx.count;
95 }
96 EXPORT_SYMBOL_NS_GPL(devm_cxl_port_enumerate_dports, CXL);
97