xref: /openbmc/linux/drivers/pci/pcie/rcec.c (revision 8be98d2f2a0a262f8bf8a0bc1fdf522b3c7aab17)
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