xref: /kernel/linux/linux-6.6/drivers/pci/pcie/rcec.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Root Complex Event Collector Support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Authors:
662306a36Sopenharmony_ci *  Sean V Kelley <sean.v.kelley@intel.com>
762306a36Sopenharmony_ci *  Qiuxu Zhuo <qiuxu.zhuo@intel.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Copyright (C) 2020 Intel Corp.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/pci.h>
1462306a36Sopenharmony_ci#include <linux/pci_regs.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "../pci.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistruct walk_rcec_data {
1962306a36Sopenharmony_ci	struct pci_dev *rcec;
2062306a36Sopenharmony_ci	int (*user_callback)(struct pci_dev *dev, void *data);
2162306a36Sopenharmony_ci	void *user_data;
2262306a36Sopenharmony_ci};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic bool rcec_assoc_rciep(struct pci_dev *rcec, struct pci_dev *rciep)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	unsigned long bitmap = rcec->rcec_ea->bitmap;
2762306a36Sopenharmony_ci	unsigned int devn;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	/* An RCiEP found on a different bus in range */
3062306a36Sopenharmony_ci	if (rcec->bus->number != rciep->bus->number)
3162306a36Sopenharmony_ci		return true;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	/* Same bus, so check bitmap */
3462306a36Sopenharmony_ci	for_each_set_bit(devn, &bitmap, 32)
3562306a36Sopenharmony_ci		if (devn == PCI_SLOT(rciep->devfn))
3662306a36Sopenharmony_ci			return true;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	return false;
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic int link_rcec_helper(struct pci_dev *dev, void *data)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	struct walk_rcec_data *rcec_data = data;
4462306a36Sopenharmony_ci	struct pci_dev *rcec = rcec_data->rcec;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	if ((pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) &&
4762306a36Sopenharmony_ci	    rcec_assoc_rciep(rcec, dev)) {
4862306a36Sopenharmony_ci		dev->rcec = rcec;
4962306a36Sopenharmony_ci		pci_dbg(dev, "PME & error events signaled via %s\n",
5062306a36Sopenharmony_ci			pci_name(rcec));
5162306a36Sopenharmony_ci	}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	return 0;
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int walk_rcec_helper(struct pci_dev *dev, void *data)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	struct walk_rcec_data *rcec_data = data;
5962306a36Sopenharmony_ci	struct pci_dev *rcec = rcec_data->rcec;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if ((pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) &&
6262306a36Sopenharmony_ci	    rcec_assoc_rciep(rcec, dev))
6362306a36Sopenharmony_ci		rcec_data->user_callback(dev, rcec_data->user_data);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	return 0;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic void walk_rcec(int (*cb)(struct pci_dev *dev, void *data),
6962306a36Sopenharmony_ci		      void *userdata)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	struct walk_rcec_data *rcec_data = userdata;
7262306a36Sopenharmony_ci	struct pci_dev *rcec = rcec_data->rcec;
7362306a36Sopenharmony_ci	u8 nextbusn, lastbusn;
7462306a36Sopenharmony_ci	struct pci_bus *bus;
7562306a36Sopenharmony_ci	unsigned int bnr;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (!rcec->rcec_ea)
7862306a36Sopenharmony_ci		return;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	/* Walk own bus for bitmap based association */
8162306a36Sopenharmony_ci	pci_walk_bus(rcec->bus, cb, rcec_data);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	nextbusn = rcec->rcec_ea->nextbusn;
8462306a36Sopenharmony_ci	lastbusn = rcec->rcec_ea->lastbusn;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	/* All RCiEP devices are on the same bus as the RCEC */
8762306a36Sopenharmony_ci	if (nextbusn == 0xff && lastbusn == 0x00)
8862306a36Sopenharmony_ci		return;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	for (bnr = nextbusn; bnr <= lastbusn; bnr++) {
9162306a36Sopenharmony_ci		/* No association indicated (PCIe 5.0-1, 7.9.10.3) */
9262306a36Sopenharmony_ci		if (bnr == rcec->bus->number)
9362306a36Sopenharmony_ci			continue;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci		bus = pci_find_bus(pci_domain_nr(rcec->bus), bnr);
9662306a36Sopenharmony_ci		if (!bus)
9762306a36Sopenharmony_ci			continue;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci		/* Find RCiEP devices on the given bus ranges */
10062306a36Sopenharmony_ci		pci_walk_bus(bus, cb, rcec_data);
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/**
10562306a36Sopenharmony_ci * pcie_link_rcec - Link RCiEP devices associated with RCEC.
10662306a36Sopenharmony_ci * @rcec: RCEC whose RCiEP devices should be linked.
10762306a36Sopenharmony_ci *
10862306a36Sopenharmony_ci * Link the given RCEC to each RCiEP device found.
10962306a36Sopenharmony_ci */
11062306a36Sopenharmony_civoid pcie_link_rcec(struct pci_dev *rcec)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	struct walk_rcec_data rcec_data;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	if (!rcec->rcec_ea)
11562306a36Sopenharmony_ci		return;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	rcec_data.rcec = rcec;
11862306a36Sopenharmony_ci	rcec_data.user_callback = NULL;
11962306a36Sopenharmony_ci	rcec_data.user_data = NULL;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	walk_rcec(link_rcec_helper, &rcec_data);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/**
12562306a36Sopenharmony_ci * pcie_walk_rcec - Walk RCiEP devices associating with RCEC and call callback.
12662306a36Sopenharmony_ci * @rcec:	RCEC whose RCiEP devices should be walked
12762306a36Sopenharmony_ci * @cb:		Callback to be called for each RCiEP device found
12862306a36Sopenharmony_ci * @userdata:	Arbitrary pointer to be passed to callback
12962306a36Sopenharmony_ci *
13062306a36Sopenharmony_ci * Walk the given RCEC. Call the callback on each RCiEP found.
13162306a36Sopenharmony_ci *
13262306a36Sopenharmony_ci * If @cb returns anything other than 0, break out.
13362306a36Sopenharmony_ci */
13462306a36Sopenharmony_civoid pcie_walk_rcec(struct pci_dev *rcec, int (*cb)(struct pci_dev *, void *),
13562306a36Sopenharmony_ci		    void *userdata)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	struct walk_rcec_data rcec_data;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	if (!rcec->rcec_ea)
14062306a36Sopenharmony_ci		return;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	rcec_data.rcec = rcec;
14362306a36Sopenharmony_ci	rcec_data.user_callback = cb;
14462306a36Sopenharmony_ci	rcec_data.user_data = userdata;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	walk_rcec(walk_rcec_helper, &rcec_data);
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_civoid pci_rcec_init(struct pci_dev *dev)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct rcec_ea *rcec_ea;
15262306a36Sopenharmony_ci	u32 rcec, hdr, busn;
15362306a36Sopenharmony_ci	u8 ver;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	/* Only for Root Complex Event Collectors */
15662306a36Sopenharmony_ci	if (pci_pcie_type(dev) != PCI_EXP_TYPE_RC_EC)
15762306a36Sopenharmony_ci		return;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	rcec = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_RCEC);
16062306a36Sopenharmony_ci	if (!rcec)
16162306a36Sopenharmony_ci		return;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	rcec_ea = kzalloc(sizeof(*rcec_ea), GFP_KERNEL);
16462306a36Sopenharmony_ci	if (!rcec_ea)
16562306a36Sopenharmony_ci		return;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	pci_read_config_dword(dev, rcec + PCI_RCEC_RCIEP_BITMAP,
16862306a36Sopenharmony_ci			      &rcec_ea->bitmap);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	/* Check whether RCEC BUSN register is present */
17162306a36Sopenharmony_ci	pci_read_config_dword(dev, rcec, &hdr);
17262306a36Sopenharmony_ci	ver = PCI_EXT_CAP_VER(hdr);
17362306a36Sopenharmony_ci	if (ver >= PCI_RCEC_BUSN_REG_VER) {
17462306a36Sopenharmony_ci		pci_read_config_dword(dev, rcec + PCI_RCEC_BUSN, &busn);
17562306a36Sopenharmony_ci		rcec_ea->nextbusn = PCI_RCEC_BUSN_NEXT(busn);
17662306a36Sopenharmony_ci		rcec_ea->lastbusn = PCI_RCEC_BUSN_LAST(busn);
17762306a36Sopenharmony_ci	} else {
17862306a36Sopenharmony_ci		/* Avoid later ver check by setting nextbusn */
17962306a36Sopenharmony_ci		rcec_ea->nextbusn = 0xff;
18062306a36Sopenharmony_ci		rcec_ea->lastbusn = 0x00;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	dev->rcec_ea = rcec_ea;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_civoid pci_rcec_exit(struct pci_dev *dev)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	kfree(dev->rcec_ea);
18962306a36Sopenharmony_ci	dev->rcec_ea = NULL;
19062306a36Sopenharmony_ci}
191