1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Root Complex Event Collector Support
4  *
5  * Authors:
6  *  Sean V Kelley <sean.v.kelley@intel.com>
7  *  Qiuxu Zhuo <qiuxu.zhuo@intel.com>
8  *
9  * Copyright (C) 2020 Intel Corp.
10  */
11 
12 #include <linux/kernel.h>
13 #include <linux/pci.h>
14 #include <linux/pci_regs.h>
15 
16 #include "../pci.h"
17 
18 struct walk_rcec_data {
19 	struct pci_dev *rcec;
20 	int (*user_callback)(struct pci_dev *dev, void *data);
21 	void *user_data;
22 };
23 
rcec_assoc_rciep(struct pci_dev * rcec,struct pci_dev * rciep)24 static bool rcec_assoc_rciep(struct pci_dev *rcec, struct pci_dev *rciep)
25 {
26 	unsigned long bitmap = rcec->rcec_ea->bitmap;
27 	unsigned int devn;
28 
29 	/* An RCiEP found on a different bus in range */
30 	if (rcec->bus->number != rciep->bus->number)
31 		return true;
32 
33 	/* Same bus, so check bitmap */
34 	for_each_set_bit(devn, &bitmap, 32)
35 		if (devn == PCI_SLOT(rciep->devfn))
36 			return true;
37 
38 	return false;
39 }
40 
link_rcec_helper(struct pci_dev * dev,void * data)41 static int link_rcec_helper(struct pci_dev *dev, void *data)
42 {
43 	struct walk_rcec_data *rcec_data = data;
44 	struct pci_dev *rcec = rcec_data->rcec;
45 
46 	if ((pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) &&
47 	    rcec_assoc_rciep(rcec, dev)) {
48 		dev->rcec = rcec;
49 		pci_dbg(dev, "PME & error events signaled via %s\n",
50 			pci_name(rcec));
51 	}
52 
53 	return 0;
54 }
55 
walk_rcec_helper(struct pci_dev * dev,void * data)56 static int walk_rcec_helper(struct pci_dev *dev, void *data)
57 {
58 	struct walk_rcec_data *rcec_data = data;
59 	struct pci_dev *rcec = rcec_data->rcec;
60 
61 	if ((pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) &&
62 	    rcec_assoc_rciep(rcec, dev))
63 		rcec_data->user_callback(dev, rcec_data->user_data);
64 
65 	return 0;
66 }
67 
walk_rcec(int (* cb)(struct pci_dev * dev,void * data),void * userdata)68 static void walk_rcec(int (*cb)(struct pci_dev *dev, void *data),
69 		      void *userdata)
70 {
71 	struct walk_rcec_data *rcec_data = userdata;
72 	struct pci_dev *rcec = rcec_data->rcec;
73 	u8 nextbusn, lastbusn;
74 	struct pci_bus *bus;
75 	unsigned int bnr;
76 
77 	if (!rcec->rcec_ea)
78 		return;
79 
80 	/* Walk own bus for bitmap based association */
81 	pci_walk_bus(rcec->bus, cb, rcec_data);
82 
83 	nextbusn = rcec->rcec_ea->nextbusn;
84 	lastbusn = rcec->rcec_ea->lastbusn;
85 
86 	/* All RCiEP devices are on the same bus as the RCEC */
87 	if (nextbusn == 0xff && lastbusn == 0x00)
88 		return;
89 
90 	for (bnr = nextbusn; bnr <= lastbusn; bnr++) {
91 		/* No association indicated (PCIe 5.0-1, 7.9.10.3) */
92 		if (bnr == rcec->bus->number)
93 			continue;
94 
95 		bus = pci_find_bus(pci_domain_nr(rcec->bus), bnr);
96 		if (!bus)
97 			continue;
98 
99 		/* Find RCiEP devices on the given bus ranges */
100 		pci_walk_bus(bus, cb, rcec_data);
101 	}
102 }
103 
104 /**
105  * pcie_link_rcec - Link RCiEP devices associated with RCEC.
106  * @rcec: RCEC whose RCiEP devices should be linked.
107  *
108  * Link the given RCEC to each RCiEP device found.
109  */
pcie_link_rcec(struct pci_dev * rcec)110 void pcie_link_rcec(struct pci_dev *rcec)
111 {
112 	struct walk_rcec_data rcec_data;
113 
114 	if (!rcec->rcec_ea)
115 		return;
116 
117 	rcec_data.rcec = rcec;
118 	rcec_data.user_callback = NULL;
119 	rcec_data.user_data = NULL;
120 
121 	walk_rcec(link_rcec_helper, &rcec_data);
122 }
123 
124 /**
125  * pcie_walk_rcec - Walk RCiEP devices associating with RCEC and call callback.
126  * @rcec:	RCEC whose RCiEP devices should be walked
127  * @cb:		Callback to be called for each RCiEP device found
128  * @userdata:	Arbitrary pointer to be passed to callback
129  *
130  * Walk the given RCEC. Call the callback on each RCiEP found.
131  *
132  * If @cb returns anything other than 0, break out.
133  */
pcie_walk_rcec(struct pci_dev * rcec,int (* cb)(struct pci_dev *,void *),void * userdata)134 void pcie_walk_rcec(struct pci_dev *rcec, int (*cb)(struct pci_dev *, void *),
135 		    void *userdata)
136 {
137 	struct walk_rcec_data rcec_data;
138 
139 	if (!rcec->rcec_ea)
140 		return;
141 
142 	rcec_data.rcec = rcec;
143 	rcec_data.user_callback = cb;
144 	rcec_data.user_data = userdata;
145 
146 	walk_rcec(walk_rcec_helper, &rcec_data);
147 }
148 
pci_rcec_init(struct pci_dev * dev)149 void pci_rcec_init(struct pci_dev *dev)
150 {
151 	struct rcec_ea *rcec_ea;
152 	u32 rcec, hdr, busn;
153 	u8 ver;
154 
155 	/* Only for Root Complex Event Collectors */
156 	if (pci_pcie_type(dev) != PCI_EXP_TYPE_RC_EC)
157 		return;
158 
159 	rcec = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_RCEC);
160 	if (!rcec)
161 		return;
162 
163 	rcec_ea = kzalloc(sizeof(*rcec_ea), GFP_KERNEL);
164 	if (!rcec_ea)
165 		return;
166 
167 	pci_read_config_dword(dev, rcec + PCI_RCEC_RCIEP_BITMAP,
168 			      &rcec_ea->bitmap);
169 
170 	/* Check whether RCEC BUSN register is present */
171 	pci_read_config_dword(dev, rcec, &hdr);
172 	ver = PCI_EXT_CAP_VER(hdr);
173 	if (ver >= PCI_RCEC_BUSN_REG_VER) {
174 		pci_read_config_dword(dev, rcec + PCI_RCEC_BUSN, &busn);
175 		rcec_ea->nextbusn = PCI_RCEC_BUSN_NEXT(busn);
176 		rcec_ea->lastbusn = PCI_RCEC_BUSN_LAST(busn);
177 	} else {
178 		/* Avoid later ver check by setting nextbusn */
179 		rcec_ea->nextbusn = 0xff;
180 		rcec_ea->lastbusn = 0x00;
181 	}
182 
183 	dev->rcec_ea = rcec_ea;
184 }
185 
pci_rcec_exit(struct pci_dev * dev)186 void pci_rcec_exit(struct pci_dev *dev)
187 {
188 	kfree(dev->rcec_ea);
189 	dev->rcec_ea = NULL;
190 }
191