xref: /openbmc/linux/drivers/pci/pcie/ptm.c (revision 9a6b55ac)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * PCI Express Precision Time Measurement
4  * Copyright (c) 2016, Intel Corporation.
5  */
6 
7 #include <linux/module.h>
8 #include <linux/init.h>
9 #include <linux/pci.h>
10 #include "../pci.h"
11 
12 static void pci_ptm_info(struct pci_dev *dev)
13 {
14 	char clock_desc[8];
15 
16 	switch (dev->ptm_granularity) {
17 	case 0:
18 		snprintf(clock_desc, sizeof(clock_desc), "unknown");
19 		break;
20 	case 255:
21 		snprintf(clock_desc, sizeof(clock_desc), ">254ns");
22 		break;
23 	default:
24 		snprintf(clock_desc, sizeof(clock_desc), "%uns",
25 			 dev->ptm_granularity);
26 		break;
27 	}
28 	pci_info(dev, "PTM enabled%s, %s granularity\n",
29 		 dev->ptm_root ? " (root)" : "", clock_desc);
30 }
31 
32 void pci_ptm_init(struct pci_dev *dev)
33 {
34 	int pos;
35 	u32 cap, ctrl;
36 	u8 local_clock;
37 	struct pci_dev *ups;
38 
39 	if (!pci_is_pcie(dev))
40 		return;
41 
42 	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_PTM);
43 	if (!pos)
44 		return;
45 
46 	/*
47 	 * Enable PTM only on interior devices (root ports, switch ports,
48 	 * etc.) on the assumption that it causes no link traffic until an
49 	 * endpoint enables it.
50 	 */
51 	if ((pci_pcie_type(dev) == PCI_EXP_TYPE_ENDPOINT ||
52 	     pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END))
53 		return;
54 
55 	pci_read_config_dword(dev, pos + PCI_PTM_CAP, &cap);
56 	local_clock = (cap & PCI_PTM_GRANULARITY_MASK) >> 8;
57 
58 	/*
59 	 * There's no point in enabling PTM unless it's enabled in the
60 	 * upstream device or this device can be a PTM Root itself.  Per
61 	 * the spec recommendation (PCIe r3.1, sec 7.32.3), select the
62 	 * furthest upstream Time Source as the PTM Root.
63 	 */
64 	ups = pci_upstream_bridge(dev);
65 	if (ups && ups->ptm_enabled) {
66 		ctrl = PCI_PTM_CTRL_ENABLE;
67 		if (ups->ptm_granularity == 0)
68 			dev->ptm_granularity = 0;
69 		else if (ups->ptm_granularity > local_clock)
70 			dev->ptm_granularity = ups->ptm_granularity;
71 	} else {
72 		if (cap & PCI_PTM_CAP_ROOT) {
73 			ctrl = PCI_PTM_CTRL_ENABLE | PCI_PTM_CTRL_ROOT;
74 			dev->ptm_root = 1;
75 			dev->ptm_granularity = local_clock;
76 		} else
77 			return;
78 	}
79 
80 	ctrl |= dev->ptm_granularity << 8;
81 	pci_write_config_dword(dev, pos + PCI_PTM_CTRL, ctrl);
82 	dev->ptm_enabled = 1;
83 
84 	pci_ptm_info(dev);
85 }
86 
87 int pci_enable_ptm(struct pci_dev *dev, u8 *granularity)
88 {
89 	int pos;
90 	u32 cap, ctrl;
91 	struct pci_dev *ups;
92 
93 	if (!pci_is_pcie(dev))
94 		return -EINVAL;
95 
96 	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_PTM);
97 	if (!pos)
98 		return -EINVAL;
99 
100 	pci_read_config_dword(dev, pos + PCI_PTM_CAP, &cap);
101 	if (!(cap & PCI_PTM_CAP_REQ))
102 		return -EINVAL;
103 
104 	/*
105 	 * For a PCIe Endpoint, PTM is only useful if the endpoint can
106 	 * issue PTM requests to upstream devices that have PTM enabled.
107 	 *
108 	 * For Root Complex Integrated Endpoints, there is no upstream
109 	 * device, so there must be some implementation-specific way to
110 	 * associate the endpoint with a time source.
111 	 */
112 	if (pci_pcie_type(dev) == PCI_EXP_TYPE_ENDPOINT) {
113 		ups = pci_upstream_bridge(dev);
114 		if (!ups || !ups->ptm_enabled)
115 			return -EINVAL;
116 
117 		dev->ptm_granularity = ups->ptm_granularity;
118 	} else if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) {
119 		dev->ptm_granularity = 0;
120 	} else
121 		return -EINVAL;
122 
123 	ctrl = PCI_PTM_CTRL_ENABLE;
124 	ctrl |= dev->ptm_granularity << 8;
125 	pci_write_config_dword(dev, pos + PCI_PTM_CTRL, ctrl);
126 	dev->ptm_enabled = 1;
127 
128 	pci_ptm_info(dev);
129 
130 	if (granularity)
131 		*granularity = dev->ptm_granularity;
132 	return 0;
133 }
134 EXPORT_SYMBOL(pci_enable_ptm);
135