190655631SSean V Kelley // SPDX-License-Identifier: GPL-2.0
290655631SSean V Kelley /*
390655631SSean V Kelley * Root Complex Event Collector Support
490655631SSean V Kelley *
590655631SSean V Kelley * Authors:
690655631SSean V Kelley * Sean V Kelley <sean.v.kelley@intel.com>
790655631SSean V Kelley * Qiuxu Zhuo <qiuxu.zhuo@intel.com>
890655631SSean V Kelley *
990655631SSean V Kelley * Copyright (C) 2020 Intel Corp.
1090655631SSean V Kelley */
1190655631SSean V Kelley
1290655631SSean V Kelley #include <linux/kernel.h>
1390655631SSean V Kelley #include <linux/pci.h>
1490655631SSean V Kelley #include <linux/pci_regs.h>
1590655631SSean V Kelley
1690655631SSean V Kelley #include "../pci.h"
1790655631SSean V Kelley
18507b460fSSean V Kelley struct walk_rcec_data {
19507b460fSSean V Kelley struct pci_dev *rcec;
20507b460fSSean V Kelley int (*user_callback)(struct pci_dev *dev, void *data);
21507b460fSSean V Kelley void *user_data;
22507b460fSSean V Kelley };
23507b460fSSean V Kelley
rcec_assoc_rciep(struct pci_dev * rcec,struct pci_dev * rciep)24507b460fSSean V Kelley static bool rcec_assoc_rciep(struct pci_dev *rcec, struct pci_dev *rciep)
25507b460fSSean V Kelley {
26507b460fSSean V Kelley unsigned long bitmap = rcec->rcec_ea->bitmap;
27507b460fSSean V Kelley unsigned int devn;
28507b460fSSean V Kelley
29507b460fSSean V Kelley /* An RCiEP found on a different bus in range */
30507b460fSSean V Kelley if (rcec->bus->number != rciep->bus->number)
31507b460fSSean V Kelley return true;
32507b460fSSean V Kelley
33507b460fSSean V Kelley /* Same bus, so check bitmap */
34507b460fSSean V Kelley for_each_set_bit(devn, &bitmap, 32)
35*d9b7eae8SQiuxu Zhuo if (devn == PCI_SLOT(rciep->devfn))
36507b460fSSean V Kelley return true;
37507b460fSSean V Kelley
38507b460fSSean V Kelley return false;
39507b460fSSean V Kelley }
40507b460fSSean V Kelley
link_rcec_helper(struct pci_dev * dev,void * data)41507b460fSSean V Kelley static int link_rcec_helper(struct pci_dev *dev, void *data)
42507b460fSSean V Kelley {
43507b460fSSean V Kelley struct walk_rcec_data *rcec_data = data;
44507b460fSSean V Kelley struct pci_dev *rcec = rcec_data->rcec;
45507b460fSSean V Kelley
46507b460fSSean V Kelley if ((pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) &&
47507b460fSSean V Kelley rcec_assoc_rciep(rcec, dev)) {
48507b460fSSean V Kelley dev->rcec = rcec;
49507b460fSSean V Kelley pci_dbg(dev, "PME & error events signaled via %s\n",
50507b460fSSean V Kelley pci_name(rcec));
51507b460fSSean V Kelley }
52507b460fSSean V Kelley
53507b460fSSean V Kelley return 0;
54507b460fSSean V Kelley }
55507b460fSSean V Kelley
walk_rcec_helper(struct pci_dev * dev,void * data)56af113553SSean V Kelley static int walk_rcec_helper(struct pci_dev *dev, void *data)
57af113553SSean V Kelley {
58af113553SSean V Kelley struct walk_rcec_data *rcec_data = data;
59af113553SSean V Kelley struct pci_dev *rcec = rcec_data->rcec;
60af113553SSean V Kelley
61af113553SSean V Kelley if ((pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) &&
62af113553SSean V Kelley rcec_assoc_rciep(rcec, dev))
63af113553SSean V Kelley rcec_data->user_callback(dev, rcec_data->user_data);
64af113553SSean V Kelley
65af113553SSean V Kelley return 0;
66af113553SSean V Kelley }
67af113553SSean V Kelley
walk_rcec(int (* cb)(struct pci_dev * dev,void * data),void * userdata)68507b460fSSean V Kelley static void walk_rcec(int (*cb)(struct pci_dev *dev, void *data),
69507b460fSSean V Kelley void *userdata)
70507b460fSSean V Kelley {
71507b460fSSean V Kelley struct walk_rcec_data *rcec_data = userdata;
72507b460fSSean V Kelley struct pci_dev *rcec = rcec_data->rcec;
73507b460fSSean V Kelley u8 nextbusn, lastbusn;
74507b460fSSean V Kelley struct pci_bus *bus;
75507b460fSSean V Kelley unsigned int bnr;
76507b460fSSean V Kelley
77507b460fSSean V Kelley if (!rcec->rcec_ea)
78507b460fSSean V Kelley return;
79507b460fSSean V Kelley
80507b460fSSean V Kelley /* Walk own bus for bitmap based association */
81507b460fSSean V Kelley pci_walk_bus(rcec->bus, cb, rcec_data);
82507b460fSSean V Kelley
83507b460fSSean V Kelley nextbusn = rcec->rcec_ea->nextbusn;
84507b460fSSean V Kelley lastbusn = rcec->rcec_ea->lastbusn;
85507b460fSSean V Kelley
86507b460fSSean V Kelley /* All RCiEP devices are on the same bus as the RCEC */
87507b460fSSean V Kelley if (nextbusn == 0xff && lastbusn == 0x00)
88507b460fSSean V Kelley return;
89507b460fSSean V Kelley
90507b460fSSean V Kelley for (bnr = nextbusn; bnr <= lastbusn; bnr++) {
91507b460fSSean V Kelley /* No association indicated (PCIe 5.0-1, 7.9.10.3) */
92507b460fSSean V Kelley if (bnr == rcec->bus->number)
93507b460fSSean V Kelley continue;
94507b460fSSean V Kelley
95507b460fSSean V Kelley bus = pci_find_bus(pci_domain_nr(rcec->bus), bnr);
96507b460fSSean V Kelley if (!bus)
97507b460fSSean V Kelley continue;
98507b460fSSean V Kelley
99507b460fSSean V Kelley /* Find RCiEP devices on the given bus ranges */
100507b460fSSean V Kelley pci_walk_bus(bus, cb, rcec_data);
101507b460fSSean V Kelley }
102507b460fSSean V Kelley }
103507b460fSSean V Kelley
104507b460fSSean V Kelley /**
105507b460fSSean V Kelley * pcie_link_rcec - Link RCiEP devices associated with RCEC.
106507b460fSSean V Kelley * @rcec: RCEC whose RCiEP devices should be linked.
107507b460fSSean V Kelley *
108507b460fSSean V Kelley * Link the given RCEC to each RCiEP device found.
109507b460fSSean V Kelley */
pcie_link_rcec(struct pci_dev * rcec)110507b460fSSean V Kelley void pcie_link_rcec(struct pci_dev *rcec)
111507b460fSSean V Kelley {
112507b460fSSean V Kelley struct walk_rcec_data rcec_data;
113507b460fSSean V Kelley
114507b460fSSean V Kelley if (!rcec->rcec_ea)
115507b460fSSean V Kelley return;
116507b460fSSean V Kelley
117507b460fSSean V Kelley rcec_data.rcec = rcec;
118507b460fSSean V Kelley rcec_data.user_callback = NULL;
119507b460fSSean V Kelley rcec_data.user_data = NULL;
120507b460fSSean V Kelley
121507b460fSSean V Kelley walk_rcec(link_rcec_helper, &rcec_data);
122507b460fSSean V Kelley }
123507b460fSSean V Kelley
124af113553SSean V Kelley /**
125af113553SSean V Kelley * pcie_walk_rcec - Walk RCiEP devices associating with RCEC and call callback.
126af113553SSean V Kelley * @rcec: RCEC whose RCiEP devices should be walked
127af113553SSean V Kelley * @cb: Callback to be called for each RCiEP device found
128af113553SSean V Kelley * @userdata: Arbitrary pointer to be passed to callback
129af113553SSean V Kelley *
130af113553SSean V Kelley * Walk the given RCEC. Call the callback on each RCiEP found.
131af113553SSean V Kelley *
132af113553SSean V Kelley * If @cb returns anything other than 0, break out.
133af113553SSean V Kelley */
pcie_walk_rcec(struct pci_dev * rcec,int (* cb)(struct pci_dev *,void *),void * userdata)134af113553SSean V Kelley void pcie_walk_rcec(struct pci_dev *rcec, int (*cb)(struct pci_dev *, void *),
135af113553SSean V Kelley void *userdata)
136af113553SSean V Kelley {
137af113553SSean V Kelley struct walk_rcec_data rcec_data;
138af113553SSean V Kelley
139af113553SSean V Kelley if (!rcec->rcec_ea)
140af113553SSean V Kelley return;
141af113553SSean V Kelley
142af113553SSean V Kelley rcec_data.rcec = rcec;
143af113553SSean V Kelley rcec_data.user_callback = cb;
144af113553SSean V Kelley rcec_data.user_data = userdata;
145af113553SSean V Kelley
146af113553SSean V Kelley walk_rcec(walk_rcec_helper, &rcec_data);
147af113553SSean V Kelley }
148af113553SSean V Kelley
pci_rcec_init(struct pci_dev * dev)14990655631SSean V Kelley void pci_rcec_init(struct pci_dev *dev)
15090655631SSean V Kelley {
15190655631SSean V Kelley struct rcec_ea *rcec_ea;
15290655631SSean V Kelley u32 rcec, hdr, busn;
15390655631SSean V Kelley u8 ver;
15490655631SSean V Kelley
15590655631SSean V Kelley /* Only for Root Complex Event Collectors */
15690655631SSean V Kelley if (pci_pcie_type(dev) != PCI_EXP_TYPE_RC_EC)
15790655631SSean V Kelley return;
15890655631SSean V Kelley
15990655631SSean V Kelley rcec = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_RCEC);
16090655631SSean V Kelley if (!rcec)
16190655631SSean V Kelley return;
16290655631SSean V Kelley
16390655631SSean V Kelley rcec_ea = kzalloc(sizeof(*rcec_ea), GFP_KERNEL);
16490655631SSean V Kelley if (!rcec_ea)
16590655631SSean V Kelley return;
16690655631SSean V Kelley
16790655631SSean V Kelley pci_read_config_dword(dev, rcec + PCI_RCEC_RCIEP_BITMAP,
16890655631SSean V Kelley &rcec_ea->bitmap);
16990655631SSean V Kelley
17090655631SSean V Kelley /* Check whether RCEC BUSN register is present */
17190655631SSean V Kelley pci_read_config_dword(dev, rcec, &hdr);
17290655631SSean V Kelley ver = PCI_EXT_CAP_VER(hdr);
17390655631SSean V Kelley if (ver >= PCI_RCEC_BUSN_REG_VER) {
17490655631SSean V Kelley pci_read_config_dword(dev, rcec + PCI_RCEC_BUSN, &busn);
17590655631SSean V Kelley rcec_ea->nextbusn = PCI_RCEC_BUSN_NEXT(busn);
17690655631SSean V Kelley rcec_ea->lastbusn = PCI_RCEC_BUSN_LAST(busn);
17790655631SSean V Kelley } else {
17890655631SSean V Kelley /* Avoid later ver check by setting nextbusn */
17990655631SSean V Kelley rcec_ea->nextbusn = 0xff;
18090655631SSean V Kelley rcec_ea->lastbusn = 0x00;
18190655631SSean V Kelley }
18290655631SSean V Kelley
18390655631SSean V Kelley dev->rcec_ea = rcec_ea;
18490655631SSean V Kelley }
18590655631SSean V Kelley
pci_rcec_exit(struct pci_dev * dev)18690655631SSean V Kelley void pci_rcec_exit(struct pci_dev *dev)
18790655631SSean V Kelley {
18890655631SSean V Kelley kfree(dev->rcec_ea);
18990655631SSean V Kelley dev->rcec_ea = NULL;
19090655631SSean V Kelley }
191