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