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