2018-01-26 12:50:27 -06:00
// SPDX-License-Identifier: GPL-2.0
2016-06-11 14:13:38 -05:00
/*
* PCI Express Precision Time Measurement
* Copyright ( c ) 2016 , Intel Corporation .
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/pci.h>
# include "../pci.h"
static void pci_ptm_info ( struct pci_dev * dev )
{
2016-06-12 16:26:40 -05:00
char clock_desc [ 8 ] ;
switch ( dev - > ptm_granularity ) {
case 0 :
snprintf ( clock_desc , sizeof ( clock_desc ) , " unknown " ) ;
break ;
case 255 :
snprintf ( clock_desc , sizeof ( clock_desc ) , " >254ns " ) ;
break ;
default :
snprintf ( clock_desc , sizeof ( clock_desc ) , " %udns " ,
dev - > ptm_granularity ) ;
break ;
}
2018-01-18 12:55:24 -06:00
pci_info ( dev , " PTM enabled%s, %s granularity \n " ,
2016-06-12 16:26:40 -05:00
dev - > ptm_root ? " (root) " : " " , clock_desc ) ;
2016-06-11 14:13:38 -05:00
}
void pci_ptm_init ( struct pci_dev * dev )
{
int pos ;
u32 cap , ctrl ;
2016-06-12 16:26:40 -05:00
u8 local_clock ;
2016-06-11 14:13:38 -05:00
struct pci_dev * ups ;
if ( ! pci_is_pcie ( dev ) )
return ;
pos = pci_find_ext_capability ( dev , PCI_EXT_CAP_ID_PTM ) ;
if ( ! pos )
return ;
/*
* Enable PTM only on interior devices ( root ports , switch ports ,
* etc . ) on the assumption that it causes no link traffic until an
* endpoint enables it .
*/
if ( ( pci_pcie_type ( dev ) = = PCI_EXP_TYPE_ENDPOINT | |
pci_pcie_type ( dev ) = = PCI_EXP_TYPE_RC_END ) )
return ;
pci_read_config_dword ( dev , pos + PCI_PTM_CAP , & cap ) ;
2016-06-12 16:26:40 -05:00
local_clock = ( cap & PCI_PTM_GRANULARITY_MASK ) > > 8 ;
2016-06-11 14:13:38 -05:00
/*
* There ' s no point in enabling PTM unless it ' s enabled in the
* upstream device or this device can be a PTM Root itself . Per
* the spec recommendation ( PCIe r3 .1 , sec 7.32 .3 ) , select the
* furthest upstream Time Source as the PTM Root .
*/
ups = pci_upstream_bridge ( dev ) ;
if ( ups & & ups - > ptm_enabled ) {
ctrl = PCI_PTM_CTRL_ENABLE ;
2016-06-12 16:26:40 -05:00
if ( ups - > ptm_granularity = = 0 )
dev - > ptm_granularity = 0 ;
else if ( ups - > ptm_granularity > local_clock )
dev - > ptm_granularity = ups - > ptm_granularity ;
2016-06-11 14:13:38 -05:00
} else {
if ( cap & PCI_PTM_CAP_ROOT ) {
ctrl = PCI_PTM_CTRL_ENABLE | PCI_PTM_CTRL_ROOT ;
dev - > ptm_root = 1 ;
2016-06-12 16:26:40 -05:00
dev - > ptm_granularity = local_clock ;
2016-06-11 14:13:38 -05:00
} else
return ;
}
2016-06-12 16:26:40 -05:00
ctrl | = dev - > ptm_granularity < < 8 ;
2016-06-11 14:13:38 -05:00
pci_write_config_dword ( dev , pos + PCI_PTM_CTRL , ctrl ) ;
dev - > ptm_enabled = 1 ;
pci_ptm_info ( dev ) ;
}
2016-06-13 11:01:51 -05:00
int pci_enable_ptm ( struct pci_dev * dev , u8 * granularity )
{
int pos ;
u32 cap , ctrl ;
struct pci_dev * ups ;
if ( ! pci_is_pcie ( dev ) )
return - EINVAL ;
pos = pci_find_ext_capability ( dev , PCI_EXT_CAP_ID_PTM ) ;
if ( ! pos )
return - EINVAL ;
pci_read_config_dword ( dev , pos + PCI_PTM_CAP , & cap ) ;
if ( ! ( cap & PCI_PTM_CAP_REQ ) )
return - EINVAL ;
/*
* For a PCIe Endpoint , PTM is only useful if the endpoint can
* issue PTM requests to upstream devices that have PTM enabled .
*
* For Root Complex Integrated Endpoints , there is no upstream
* device , so there must be some implementation - specific way to
* associate the endpoint with a time source .
*/
if ( pci_pcie_type ( dev ) = = PCI_EXP_TYPE_ENDPOINT ) {
ups = pci_upstream_bridge ( dev ) ;
if ( ! ups | | ! ups - > ptm_enabled )
return - EINVAL ;
2016-06-12 16:26:40 -05:00
dev - > ptm_granularity = ups - > ptm_granularity ;
2016-06-13 11:01:51 -05:00
} else if ( pci_pcie_type ( dev ) = = PCI_EXP_TYPE_RC_END ) {
2016-06-12 16:26:40 -05:00
dev - > ptm_granularity = 0 ;
2016-06-13 11:01:51 -05:00
} else
return - EINVAL ;
ctrl = PCI_PTM_CTRL_ENABLE ;
2016-06-12 16:26:40 -05:00
ctrl | = dev - > ptm_granularity < < 8 ;
2016-06-13 11:01:51 -05:00
pci_write_config_dword ( dev , pos + PCI_PTM_CTRL , ctrl ) ;
dev - > ptm_enabled = 1 ;
pci_ptm_info ( dev ) ;
if ( granularity )
2016-06-12 16:26:40 -05:00
* granularity = dev - > ptm_granularity ;
2016-06-13 11:01:51 -05:00
return 0 ;
}
EXPORT_SYMBOL ( pci_enable_ptm ) ;