diff options
Diffstat (limited to 'drivers/pci/pcie')
-rw-r--r-- | drivers/pci/pcie/Kconfig | 11 | ||||
-rw-r--r-- | drivers/pci/pcie/Makefile | 1 | ||||
-rw-r--r-- | drivers/pci/pcie/aer/aerdrv.c | 36 | ||||
-rw-r--r-- | drivers/pci/pcie/aer/aerdrv.h | 9 | ||||
-rw-r--r-- | drivers/pci/pcie/aer/aerdrv_core.c | 61 | ||||
-rw-r--r-- | drivers/pci/pcie/aer/aerdrv_errprint.c | 6 | ||||
-rw-r--r-- | drivers/pci/pcie/pcie-dpc.c | 18 | ||||
-rw-r--r-- | drivers/pci/pcie/pme.c | 16 | ||||
-rw-r--r-- | drivers/pci/pcie/portdrv_pci.c | 17 | ||||
-rw-r--r-- | drivers/pci/pcie/ptm.c | 142 |
10 files changed, 191 insertions, 126 deletions
diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig index 7fcea75afa4c..7ce77635e5ad 100644 --- a/drivers/pci/pcie/Kconfig +++ b/drivers/pci/pcie/Kconfig @@ -92,3 +92,14 @@ config PCIE_DPC will be handled by the DPC driver. If your system doesn't have this capability or you do not want to use this feature, it is safe to answer N. + +config PCIE_PTM + bool "PCIe Precision Time Measurement support" + default n + depends on PCIEPORTBUS + help + This enables PCI Express Precision Time Measurement (PTM) + support. + + This is only useful if you have devices that support PTM, but it + is safe to enable even if you don't. diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile index b24525b3dec1..36e35ea8fde7 100644 --- a/drivers/pci/pcie/Makefile +++ b/drivers/pci/pcie/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_PCIEAER) += aer/ obj-$(CONFIG_PCIE_PME) += pme.o obj-$(CONFIG_PCIE_DPC) += pcie-dpc.o +obj-$(CONFIG_PCIE_PTM) += ptm.o diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index 48d21e0edd56..139150b2bdfd 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -15,7 +15,6 @@ * */ -#include <linux/module.h> #include <linux/pci.h> #include <linux/pci-acpi.h> #include <linux/sched.h> @@ -37,9 +36,6 @@ #define DRIVER_VERSION "v1.0" #define DRIVER_AUTHOR "tom.l.nguyen@intel.com" #define DRIVER_DESC "Root Port Advanced Error Reporting Driver" -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); static int aer_probe(struct pcie_device *dev); static void aer_remove(struct pcie_device *dev); @@ -70,7 +66,7 @@ static int pcie_aer_disable; void pci_no_aer(void) { - pcie_aer_disable = 1; /* has priority over 'forceload' */ + pcie_aer_disable = 1; } bool pci_aer_available(void) @@ -134,7 +130,7 @@ static void aer_enable_rootport(struct aer_rpc *rpc) pcie_capability_clear_word(pdev, PCI_EXP_RTCTL, SYSTEM_ERROR_INTR_ON_MESG_MASK); - aer_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); + aer_pos = pdev->aer_cap; /* Clear error status */ pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, ®32); pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32); @@ -173,7 +169,7 @@ static void aer_disable_rootport(struct aer_rpc *rpc) */ set_downstream_devices_error_reporting(pdev, false); - pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); + pos = pdev->aer_cap; /* Disable Root's interrupt in response to error messages */ pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, ®32); reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK; @@ -200,7 +196,7 @@ irqreturn_t aer_irq(int irq, void *context) unsigned long flags; int pos; - pos = pci_find_ext_capability(pdev->port, PCI_EXT_CAP_ID_ERR); + pos = pdev->port->aer_cap; /* * Must lock access to Root Error Status Reg, Root Error ID Reg, * and Root error producer/consumer index @@ -294,7 +290,6 @@ static void aer_remove(struct pcie_device *dev) /** * aer_probe - initialize resources * @dev: pointer to the pcie_dev data structure - * @id: pointer to the service id data structure * * Invoked when PCI Express bus loads AER service driver. */ @@ -304,11 +299,6 @@ static int aer_probe(struct pcie_device *dev) struct aer_rpc *rpc; struct device *device = &dev->device; - /* Init */ - status = aer_init(dev); - if (status) - return status; - /* Alloc rpc data structure */ rpc = aer_alloc_rpc(dev); if (!rpc) { @@ -343,7 +333,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev) u32 reg32; int pos; - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + pos = dev->aer_cap; /* Disable Root's interrupt in response to error messages */ pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, ®32); @@ -396,7 +386,7 @@ static void aer_error_resume(struct pci_dev *dev) pcie_capability_write_word(dev, PCI_EXP_DEVSTA, reg16); /* Clean AER Root Error Status */ - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + pos = dev->aer_cap; pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask); if (dev->error_state == pci_channel_io_normal) @@ -417,16 +407,4 @@ static int __init aer_service_init(void) return -ENXIO; return pcie_port_service_register(&aerdriver); } - -/** - * aer_service_exit - unregister AER root service driver - * - * Invoked when AER root service driver is unloaded. - */ -static void __exit aer_service_exit(void) -{ - pcie_port_service_unregister(&aerdriver); -} - -module_init(aer_service_init); -module_exit(aer_service_exit); +device_initcall(aer_service_init); diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index 945c939a86c5..d51e4a57b190 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -60,6 +60,7 @@ struct aer_rpc { struct pcie_device *rpd; /* Root Port device */ struct work_struct dpc_handler; struct aer_err_source e_sources[AER_ERROR_SOURCES_MAX]; + struct aer_err_info e_info; unsigned short prod_idx; /* Error Producer Index */ unsigned short cons_idx; /* Error Consumer Index */ int isr; @@ -105,7 +106,6 @@ static inline pci_ers_result_t merge_result(enum pci_ers_result orig, } extern struct bus_type pcie_port_bus_type; -int aer_init(struct pcie_device *dev); void aer_isr(struct work_struct *work); void aer_print_error(struct pci_dev *dev, struct aer_err_info *info); void aer_print_port_info(struct pci_dev *dev, struct aer_err_info *info); @@ -121,11 +121,4 @@ static inline int pcie_aer_get_firmware_first(struct pci_dev *pci_dev) return 0; } #endif - -static inline void pcie_aer_force_firmware_first(struct pci_dev *pci_dev, - int enable) -{ - pci_dev->__aer_firmware_first = !!enable; - pci_dev->__aer_firmware_first_valid = 1; -} #endif /* _AERDRV_H_ */ diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 521e39c1b66d..b1303b32053f 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -27,11 +27,6 @@ #include <linux/kfifo.h> #include "aerdrv.h" -static bool forceload; -static bool nosourceid; -module_param(forceload, bool, 0); -module_param(nosourceid, bool, 0); - #define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \ PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE) @@ -40,7 +35,7 @@ int pci_enable_pcie_error_reporting(struct pci_dev *dev) if (pcie_aer_get_firmware_first(dev)) return -EIO; - if (!pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR)) + if (!dev->aer_cap) return -EIO; return pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_AER_FLAGS); @@ -62,7 +57,7 @@ int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev) int pos; u32 status; - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + pos = dev->aer_cap; if (!pos) return -EIO; @@ -83,7 +78,7 @@ int pci_cleanup_aer_error_status_regs(struct pci_dev *dev) if (!pci_is_pcie(dev)) return -ENODEV; - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + pos = dev->aer_cap; if (!pos) return -EIO; @@ -102,6 +97,12 @@ int pci_cleanup_aer_error_status_regs(struct pci_dev *dev) return 0; } +int pci_aer_init(struct pci_dev *dev) +{ + dev->aer_cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + return pci_cleanup_aer_error_status_regs(dev); +} + /** * add_error_device - list device to be handled * @e_info: pointer to error info @@ -132,7 +133,8 @@ static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info) * When bus id is equal to 0, it might be a bad id * reported by root port. */ - if (!nosourceid && (PCI_BUS_NUM(e_info->id) != 0)) { + if ((PCI_BUS_NUM(e_info->id) != 0) && + !(dev->bus->bus_flags & PCI_BUS_FLAGS_NO_AERSID)) { /* Device ID match? */ if (e_info->id == ((dev->bus->number << 8) | dev->devfn)) return true; @@ -144,10 +146,10 @@ static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info) /* * When either - * 1) nosourceid==y; - * 2) bus id is equal to 0. Some ports might lose the bus + * 1) bus id is equal to 0. Some ports might lose the bus * id of error source id; - * 3) There are multiple errors and prior id comparing fails; + * 2) bus flag PCI_BUS_FLAGS_NO_AERSID is set + * 3) There are multiple errors and prior ID comparing fails; * We check AER status registers to find possible reporter. */ if (atomic_read(&dev->enable_cnt) == 0) @@ -158,7 +160,7 @@ static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info) if (!(reg16 & PCI_EXP_AER_FLAGS)) return false; - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + pos = dev->aer_cap; if (!pos) return false; @@ -555,7 +557,7 @@ static void handle_error_source(struct pcie_device *aerdev, * Correctable error does not need software intervention. * No need to go through error recovery process. */ - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + pos = dev->aer_cap; if (pos) pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, info->status); @@ -647,7 +649,7 @@ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) info->status = 0; info->tlp_header_valid = 0; - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + pos = dev->aer_cap; /* The device might not support AER */ if (!pos) @@ -715,15 +717,8 @@ static inline void aer_process_err_devices(struct pcie_device *p_device, static void aer_isr_one_error(struct pcie_device *p_device, struct aer_err_source *e_src) { - struct aer_err_info *e_info; - - /* struct aer_err_info might be big, so we allocate it with slab */ - e_info = kmalloc(sizeof(struct aer_err_info), GFP_KERNEL); - if (!e_info) { - dev_printk(KERN_DEBUG, &p_device->port->dev, - "Can't allocate mem when processing AER errors\n"); - return; - } + struct aer_rpc *rpc = get_service_data(p_device); + struct aer_err_info *e_info = &rpc->e_info; /* * There is a possibility that both correctable error and @@ -762,8 +757,6 @@ static void aer_isr_one_error(struct pcie_device *p_device, if (find_source_device(p_device->port, e_info)) aer_process_err_devices(p_device, e_info); } - - kfree(e_info); } /** @@ -812,19 +805,3 @@ void aer_isr(struct work_struct *work) aer_isr_one_error(p_device, &e_src); mutex_unlock(&rpc->rpc_mutex); } - -/** - * aer_init - provide AER initialization - * @dev: pointer to AER pcie device - * - * Invoked when AER service driver is loaded. - */ -int aer_init(struct pcie_device *dev) -{ - if (forceload) { - dev_printk(KERN_DEBUG, &dev->device, - "aerdrv forceload requested.\n"); - pcie_aer_force_firmware_first(dev->port, 0); - } - return 0; -} diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c index 167fe411ce2e..54c4b691e51f 100644 --- a/drivers/pci/pcie/aer/aerdrv_errprint.c +++ b/drivers/pci/pcie/aer/aerdrv_errprint.c @@ -219,15 +219,13 @@ int cper_severity_to_aer(int cper_severity) } EXPORT_SYMBOL_GPL(cper_severity_to_aer); -void cper_print_aer(struct pci_dev *dev, int cper_severity, +void cper_print_aer(struct pci_dev *dev, int aer_severity, struct aer_capability_regs *aer) { - int aer_severity, layer, agent, status_strs_size, tlp_header_valid = 0; + int layer, agent, status_strs_size, tlp_header_valid = 0; u32 status, mask; const char **status_strs; - aer_severity = cper_severity_to_aer(cper_severity); - if (aer_severity == AER_CORRECTABLE) { status = aer->cor_status; mask = aer->cor_mask; diff --git a/drivers/pci/pcie/pcie-dpc.c b/drivers/pci/pcie/pcie-dpc.c index 250f87861786..9811b14d9ad8 100644 --- a/drivers/pci/pcie/pcie-dpc.c +++ b/drivers/pci/pcie/pcie-dpc.c @@ -1,5 +1,7 @@ /* * PCI Express Downstream Port Containment services driver + * Author: Keith Busch <keith.busch@intel.com> + * * Copyright (C) 2016 Intel Corp. * * This file is subject to the terms and conditions of the GNU General Public @@ -9,7 +11,7 @@ #include <linux/delay.h> #include <linux/interrupt.h> -#include <linux/module.h> +#include <linux/init.h> #include <linux/pci.h> #include <linux/pcieport_if.h> @@ -143,16 +145,4 @@ static int __init dpc_service_init(void) { return pcie_port_service_register(&dpcdriver); } - -static void __exit dpc_service_exit(void) -{ - pcie_port_service_unregister(&dpcdriver); -} - -MODULE_DESCRIPTION("PCI Express Downstream Port Containment driver"); -MODULE_AUTHOR("Keith Busch <keith.busch@intel.com>"); -MODULE_LICENSE("GPL"); -MODULE_VERSION("0.1"); - -module_init(dpc_service_init); -module_exit(dpc_service_exit); +device_initcall(dpc_service_init); diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c index 1ae4c73e7a3c..884bad5320f8 100644 --- a/drivers/pci/pcie/pme.c +++ b/drivers/pci/pcie/pme.c @@ -10,7 +10,6 @@ * for more details. */ -#include <linux/module.h> #include <linux/pci.h> #include <linux/kernel.h> #include <linux/errno.h> @@ -449,17 +448,6 @@ static int pcie_pme_resume(struct pcie_device *srv) return 0; } -/** - * pcie_pme_remove - Prepare PCIe PME service device for removal. - * @srv - PCIe service device to remove. - */ -static void pcie_pme_remove(struct pcie_device *srv) -{ - pcie_pme_suspend(srv); - free_irq(srv->irq, srv); - kfree(get_service_data(srv)); -} - static struct pcie_port_service_driver pcie_pme_driver = { .name = "pcie_pme", .port_type = PCI_EXP_TYPE_ROOT_PORT, @@ -468,7 +456,6 @@ static struct pcie_port_service_driver pcie_pme_driver = { .probe = pcie_pme_probe, .suspend = pcie_pme_suspend, .resume = pcie_pme_resume, - .remove = pcie_pme_remove, }; /** @@ -478,5 +465,4 @@ static int __init pcie_pme_service_init(void) { return pcie_port_service_register(&pcie_pme_driver); } - -module_init(pcie_pme_service_init); +device_initcall(pcie_pme_service_init); diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 70d7ad8c6d17..79327cc14e7d 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -1,12 +1,13 @@ /* * File: portdrv_pci.c * Purpose: PCI Express Port Bus Driver + * Author: Tom Nguyen <tom.l.nguyen@intel.com> + * Version: v1.0 * * Copyright (C) 2004 Intel * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) */ -#include <linux/module.h> #include <linux/pci.h> #include <linux/kernel.h> #include <linux/errno.h> @@ -21,16 +22,6 @@ #include "portdrv.h" #include "aer/aerdrv.h" -/* - * Version Information - */ -#define DRIVER_VERSION "v1.0" -#define DRIVER_AUTHOR "tom.l.nguyen@intel.com" -#define DRIVER_DESC "PCIe Port Bus Driver" -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); - /* If this switch is set, PCIe port native services should not be enabled. */ bool pcie_ports_disabled; @@ -341,7 +332,6 @@ static const struct pci_device_id port_pci_ids[] = { { PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0), }, { /* end: all zeroes */ } }; -MODULE_DEVICE_TABLE(pci, port_pci_ids); static const struct pci_error_handlers pcie_portdrv_err_handler = { .error_detected = pcie_portdrv_error_detected, @@ -406,5 +396,4 @@ static int __init pcie_portdrv_init(void) out: return retval; } - -module_init(pcie_portdrv_init); +device_initcall(pcie_portdrv_init); diff --git a/drivers/pci/pcie/ptm.c b/drivers/pci/pcie/ptm.c new file mode 100644 index 000000000000..bab8ac63c4f3 --- /dev/null +++ b/drivers/pci/pcie/ptm.c @@ -0,0 +1,142 @@ +/* + * PCI Express Precision Time Measurement + * Copyright (c) 2016, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/pci.h> +#include "../pci.h" + +static void pci_ptm_info(struct pci_dev *dev) +{ + 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; + } + dev_info(&dev->dev, "PTM enabled%s, %s granularity\n", + dev->ptm_root ? " (root)" : "", clock_desc); +} + +void pci_ptm_init(struct pci_dev *dev) +{ + int pos; + u32 cap, ctrl; + u8 local_clock; + 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); + local_clock = (cap & PCI_PTM_GRANULARITY_MASK) >> 8; + + /* + * 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; + if (ups->ptm_granularity == 0) + dev->ptm_granularity = 0; + else if (ups->ptm_granularity > local_clock) + dev->ptm_granularity = ups->ptm_granularity; + } else { + if (cap & PCI_PTM_CAP_ROOT) { + ctrl = PCI_PTM_CTRL_ENABLE | PCI_PTM_CTRL_ROOT; + dev->ptm_root = 1; + dev->ptm_granularity = local_clock; + } else + return; + } + + ctrl |= dev->ptm_granularity << 8; + pci_write_config_dword(dev, pos + PCI_PTM_CTRL, ctrl); + dev->ptm_enabled = 1; + + pci_ptm_info(dev); +} + +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; + + dev->ptm_granularity = ups->ptm_granularity; + } else if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) { + dev->ptm_granularity = 0; + } else + return -EINVAL; + + ctrl = PCI_PTM_CTRL_ENABLE; + ctrl |= dev->ptm_granularity << 8; + pci_write_config_dword(dev, pos + PCI_PTM_CTRL, ctrl); + dev->ptm_enabled = 1; + + pci_ptm_info(dev); + + if (granularity) + *granularity = dev->ptm_granularity; + return 0; +} +EXPORT_SYMBOL(pci_enable_ptm); |