Merge tag 'i3c/for-5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux
[linux-2.6-microblaze.git] / drivers / cxl / core / pci.c
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);