/* * If the next upstream device supports PTM, return it; otherwise return * NULL. PTM Messages are local, so both link partners must support it.
*/ staticstruct pci_dev *pci_upstream_ptm(struct pci_dev *dev)
{ struct pci_dev *ups = pci_upstream_bridge(dev);
/* * Switch Downstream Ports are not permitted to have a PTM * capability; their PTM behavior is controlled by the Upstream * Port (PCIe r5.0, sec 7.9.16), so if the upstream bridge is a * Switch Downstream Port, look up one more level.
*/ if (ups && pci_pcie_type(ups) == PCI_EXP_TYPE_DOWNSTREAM)
ups = pci_upstream_bridge(ups);
if (ups && ups->ptm_cap) return ups;
return NULL;
}
/* * Find the PTM Capability (if present) and extract the information we need * to use it.
*/ void pci_ptm_init(struct pci_dev *dev)
{
u16 ptm;
u32 cap; struct pci_dev *ups;
if (!pci_is_pcie(dev)) return;
ptm = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_PTM); if (!ptm) return;
/* * Per the spec recommendation (PCIe r6.0, sec 7.9.15.3), select the * furthest upstream Time Source as the PTM Root. For Endpoints, * "the Effective Granularity is the maximum Local Clock Granularity * reported by the PTM Root and all intervening PTM Time Sources."
*/
ups = pci_upstream_ptm(dev); if (ups) { if (ups->ptm_granularity == 0)
dev->ptm_granularity = 0; elseif (ups->ptm_granularity > dev->ptm_granularity)
dev->ptm_granularity = ups->ptm_granularity;
} elseif (cap & PCI_PTM_CAP_ROOT) {
dev->ptm_root = 1;
} elseif (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) {
/* * Per sec 7.9.15.3, this should be the Local Clock * Granularity of the associated Time Source. But it * doesn't say how to find that Time Source.
*/
dev->ptm_granularity = 0;
}
save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_PTM); if (!save_state) return;
cap = (u32 *)&save_state->cap.data[0];
pci_write_config_dword(dev, ptm + PCI_PTM_CTRL, *cap);
}
/* Enable PTM in the Control register if possible */ staticint __pci_enable_ptm(struct pci_dev *dev)
{
u16 ptm = dev->ptm_cap; struct pci_dev *ups;
u32 ctrl;
if (!ptm) return -EINVAL;
/* * A device uses local PTM Messages to request time information * from a PTM Root that's farther upstream. Every device along the * path must support PTM and have it enabled so it can handle the * messages. Therefore, if this device is not a PTM Root, the * upstream link partner must have PTM enabled before we can enable * PTM.
*/ if (!dev->ptm_root) {
ups = pci_upstream_ptm(dev); if (!ups || !ups->ptm_enabled) return -EINVAL;
}
/** * pci_enable_ptm() - Enable Precision Time Measurement * @dev: PCI device * @granularity: pointer to return granularity * * Enable Precision Time Measurement for @dev. If successful and * @granularity is non-NULL, return the Effective Granularity. * * Return: zero if successful, or -EINVAL if @dev lacks a PTM Capability or * is not a PTM Root and lacks an upstream path of PTM-enabled devices.
*/ int pci_enable_ptm(struct pci_dev *dev, u8 *granularity)
{ int rc; char clock_desc[8];
rc = __pci_enable_ptm(dev); if (rc) return rc;
dev->ptm_enabled = 1;
if (granularity)
*granularity = dev->ptm_granularity;
/** * pci_disable_ptm() - Disable Precision Time Measurement * @dev: PCI device * * Disable Precision Time Measurement for @dev.
*/ void pci_disable_ptm(struct pci_dev *dev)
{ if (dev->ptm_enabled) {
__pci_disable_ptm(dev);
dev->ptm_enabled = 0;
}
}
EXPORT_SYMBOL(pci_disable_ptm);
/* * Disable PTM, but preserve dev->ptm_enabled so we silently re-enable it on * resume if necessary.
*/ void pci_suspend_ptm(struct pci_dev *dev)
{ if (dev->ptm_enabled)
__pci_disable_ptm(dev);
}
/* If PTM was enabled before suspend, re-enable it when resuming */ void pci_resume_ptm(struct pci_dev *dev)
{ if (dev->ptm_enabled)
__pci_enable_ptm(dev);
}
bool pcie_ptm_enabled(struct pci_dev *dev)
{ if (!dev) returnfalse;
mutex_lock(&ptm_debugfs->lock);
ret = ptm_debugfs->ops->context_update_write(ptm_debugfs->pdata, mode);
mutex_unlock(&ptm_debugfs->lock); if (ret) return ret;
return count;
}
static ssize_t context_update_read(struct file *file, char __user *ubuf,
size_t count, loff_t *ppos)
{ struct pci_ptm_debugfs *ptm_debugfs = file->private_data; char buf[8]; /* Extra space for NULL termination at the end */
ssize_t pos;
u8 mode;
if (!ptm_debugfs->ops->context_update_read) return -EOPNOTSUPP;
if (!ptm_debugfs->ops->context_valid_read) return -EOPNOTSUPP;
mutex_lock(&ptm_debugfs->lock);
ret = ptm_debugfs->ops->context_valid_read(ptm_debugfs->pdata, &valid);
mutex_unlock(&ptm_debugfs->lock); if (ret) return ret;
#define pcie_ptm_create_debugfs_file(pdata, mode, attr) \ do { \ if (ops->attr##_visible && ops->attr##_visible(pdata)) \
debugfs_create_file(#attr, mode, ptm_debugfs->debugfs, \
ptm_debugfs, &attr##_fops); \
} while (0)
/* * pcie_ptm_create_debugfs() - Create debugfs entries for the PTM context * @dev: PTM capable component device * @pdata: Private data of the PTM capable component device * @ops: PTM callback structure * * Create debugfs entries for exposing the PTM context of the PTM capable * components such as Root Complex and Endpoint controllers. * * Return: Pointer to 'struct pci_ptm_debugfs' if success, NULL otherwise.
*/ struct pci_ptm_debugfs *pcie_ptm_create_debugfs(struct device *dev, void *pdata, conststruct pcie_ptm_ops *ops)
{ struct pci_ptm_debugfs *ptm_debugfs; char *dirname; int ret;
/* Caller must provide check_capability() callback */ if (!ops->check_capability) return NULL;
/* Check for PTM capability before creating debugfs attributes */
ret = ops->check_capability(pdata); if (!ret) {
dev_dbg(dev, "PTM capability not present\n"); return NULL;
}
ptm_debugfs = kzalloc(sizeof(*ptm_debugfs), GFP_KERNEL); if (!ptm_debugfs) return NULL;
dirname = devm_kasprintf(dev, GFP_KERNEL, "pcie_ptm_%s", dev_name(dev)); if (!dirname) return NULL;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.