Merge branch 'next-smack' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris...
[linux-2.6-microblaze.git] / drivers / pci / pcie / ptm.c
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), "%udns",
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);