mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	PCI: add latency tolerance reporting enable/disable support
Latency tolerance reporting allows devices to send messages to the root complex indicating their latency tolerance for snooped & unsnooped memory transactions. Add support for enabling & disabling this feature, along with a routine to set the max latencies a device should send upstream. Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
This commit is contained in:
		
							parent
							
								
									48a92a8179
								
							
						
					
					
						commit
						51c2e0a7e5
					
				
					 3 changed files with 163 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -1979,6 +1979,155 @@ void pci_disable_obff(struct pci_dev *dev)
 | 
			
		|||
}
 | 
			
		||||
EXPORT_SYMBOL(pci_disable_obff);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * pci_ltr_supported - check whether a device supports LTR
 | 
			
		||||
 * @dev: PCI device
 | 
			
		||||
 *
 | 
			
		||||
 * RETURNS:
 | 
			
		||||
 * True if @dev supports latency tolerance reporting, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
bool pci_ltr_supported(struct pci_dev *dev)
 | 
			
		||||
{
 | 
			
		||||
	int pos;
 | 
			
		||||
	u32 cap;
 | 
			
		||||
 | 
			
		||||
	if (!pci_is_pcie(dev))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	pos = pci_pcie_cap(dev);
 | 
			
		||||
	if (!pos)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	pci_read_config_dword(dev, pos + PCI_EXP_DEVCAP2, &cap);
 | 
			
		||||
 | 
			
		||||
	return cap & PCI_EXP_DEVCAP2_LTR;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(pci_ltr_supported);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * pci_enable_ltr - enable latency tolerance reporting
 | 
			
		||||
 * @dev: PCI device
 | 
			
		||||
 *
 | 
			
		||||
 * Enable LTR on @dev if possible, which means enabling it first on
 | 
			
		||||
 * upstream ports.
 | 
			
		||||
 *
 | 
			
		||||
 * RETURNS:
 | 
			
		||||
 * Zero on success, errno on failure.
 | 
			
		||||
 */
 | 
			
		||||
int pci_enable_ltr(struct pci_dev *dev)
 | 
			
		||||
{
 | 
			
		||||
	int pos;
 | 
			
		||||
	u16 ctrl;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if (!pci_ltr_supported(dev))
 | 
			
		||||
		return -ENOTSUPP;
 | 
			
		||||
 | 
			
		||||
	pos = pci_pcie_cap(dev);
 | 
			
		||||
	if (!pos)
 | 
			
		||||
		return -ENOTSUPP;
 | 
			
		||||
 | 
			
		||||
	/* Only primary function can enable/disable LTR */
 | 
			
		||||
	if (PCI_FUNC(dev->devfn) != 0)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* Enable upstream ports first */
 | 
			
		||||
	if (dev->bus) {
 | 
			
		||||
		ret = pci_enable_ltr(dev->bus->self);
 | 
			
		||||
		if (ret)
 | 
			
		||||
			return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &ctrl);
 | 
			
		||||
	ctrl |= PCI_EXP_LTR_EN;
 | 
			
		||||
	pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, ctrl);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(pci_enable_ltr);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * pci_disable_ltr - disable latency tolerance reporting
 | 
			
		||||
 * @dev: PCI device
 | 
			
		||||
 */
 | 
			
		||||
void pci_disable_ltr(struct pci_dev *dev)
 | 
			
		||||
{
 | 
			
		||||
	int pos;
 | 
			
		||||
	u16 ctrl;
 | 
			
		||||
 | 
			
		||||
	if (!pci_ltr_supported(dev))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	pos = pci_pcie_cap(dev);
 | 
			
		||||
	if (!pos)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* Only primary function can enable/disable LTR */
 | 
			
		||||
	if (PCI_FUNC(dev->devfn) != 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &ctrl);
 | 
			
		||||
	ctrl &= ~PCI_EXP_LTR_EN;
 | 
			
		||||
	pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, ctrl);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(pci_disable_ltr);
 | 
			
		||||
 | 
			
		||||
static int __pci_ltr_scale(int *val)
 | 
			
		||||
{
 | 
			
		||||
	int scale = 0;
 | 
			
		||||
 | 
			
		||||
	while (*val > 1023) {
 | 
			
		||||
		*val = (*val + 31) / 32;
 | 
			
		||||
		scale++;
 | 
			
		||||
	}
 | 
			
		||||
	return scale;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * pci_set_ltr - set LTR latency values
 | 
			
		||||
 * @dev: PCI device
 | 
			
		||||
 * @snoop_lat_ns: snoop latency in nanoseconds
 | 
			
		||||
 * @nosnoop_lat_ns: nosnoop latency in nanoseconds
 | 
			
		||||
 *
 | 
			
		||||
 * Figure out the scale and set the LTR values accordingly.
 | 
			
		||||
 */
 | 
			
		||||
int pci_set_ltr(struct pci_dev *dev, int snoop_lat_ns, int nosnoop_lat_ns)
 | 
			
		||||
{
 | 
			
		||||
	int pos, ret, snoop_scale, nosnoop_scale;
 | 
			
		||||
	u16 val;
 | 
			
		||||
 | 
			
		||||
	if (!pci_ltr_supported(dev))
 | 
			
		||||
		return -ENOTSUPP;
 | 
			
		||||
 | 
			
		||||
	snoop_scale = __pci_ltr_scale(&snoop_lat_ns);
 | 
			
		||||
	nosnoop_scale = __pci_ltr_scale(&nosnoop_lat_ns);
 | 
			
		||||
 | 
			
		||||
	if (snoop_lat_ns > PCI_LTR_VALUE_MASK ||
 | 
			
		||||
	    nosnoop_lat_ns > PCI_LTR_VALUE_MASK)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if ((snoop_scale > (PCI_LTR_SCALE_MASK >> PCI_LTR_SCALE_SHIFT)) ||
 | 
			
		||||
	    (nosnoop_scale > (PCI_LTR_SCALE_MASK >> PCI_LTR_SCALE_SHIFT)))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_LTR);
 | 
			
		||||
	if (!pos)
 | 
			
		||||
		return -ENOTSUPP;
 | 
			
		||||
 | 
			
		||||
	val = (snoop_scale << PCI_LTR_SCALE_SHIFT) | snoop_lat_ns;
 | 
			
		||||
	ret = pci_write_config_word(dev, pos + PCI_LTR_MAX_SNOOP_LAT, val);
 | 
			
		||||
	if (ret != 4)
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	val = (nosnoop_scale << PCI_LTR_SCALE_SHIFT) | nosnoop_lat_ns;
 | 
			
		||||
	ret = pci_write_config_word(dev, pos + PCI_LTR_MAX_NOSNOOP_LAT, val);
 | 
			
		||||
	if (ret != 4)
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(pci_set_ltr);
 | 
			
		||||
 | 
			
		||||
static int pci_acs_enable;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -840,6 +840,11 @@ enum pci_obff_signal_type {
 | 
			
		|||
int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type);
 | 
			
		||||
void pci_disable_obff(struct pci_dev *dev);
 | 
			
		||||
 | 
			
		||||
bool pci_ltr_supported(struct pci_dev *dev);
 | 
			
		||||
int pci_enable_ltr(struct pci_dev *dev);
 | 
			
		||||
void pci_disable_ltr(struct pci_dev *dev);
 | 
			
		||||
int pci_set_ltr(struct pci_dev *dev, int snoop_lat_ns, int nosnoop_lat_ns);
 | 
			
		||||
 | 
			
		||||
/* For use by arch with custom probe code */
 | 
			
		||||
void set_pcie_port_type(struct pci_dev *pdev);
 | 
			
		||||
void set_pcie_hotplug_bridge(struct pci_dev *pdev);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -508,6 +508,7 @@
 | 
			
		|||
#define PCI_EXP_RTSTA_PENDING	0x20000 /* PME pending */
 | 
			
		||||
#define PCI_EXP_DEVCAP2		36	/* Device Capabilities 2 */
 | 
			
		||||
#define  PCI_EXP_DEVCAP2_ARI	0x20	/* Alternative Routing-ID */
 | 
			
		||||
#define  PCI_EXP_DEVCAP2_LTR	0x800	/* Latency tolerance reporting */
 | 
			
		||||
#define  PCI_EXP_OBFF_MASK	0xc0000 /* OBFF support mechanism */
 | 
			
		||||
#define  PCI_EXP_OBFF_MSG	0x40000 /* New message signaling */
 | 
			
		||||
#define  PCI_EXP_OBFF_WAKE	0x80000 /* Re-use WAKE# for OBFF */
 | 
			
		||||
| 
						 | 
				
			
			@ -515,6 +516,7 @@
 | 
			
		|||
#define  PCI_EXP_DEVCTL2_ARI	0x20	/* Alternative Routing-ID */
 | 
			
		||||
#define  PCI_EXP_IDO_REQ_EN	0x100	/* ID-based ordering request enable */
 | 
			
		||||
#define  PCI_EXP_IDO_CMP_EN	0x200	/* ID-based ordering completion enable */
 | 
			
		||||
#define  PCI_EXP_LTR_EN		0x400	/* Latency tolerance reporting */
 | 
			
		||||
#define  PCI_EXP_OBFF_MSGA_EN	0x2000	/* OBFF enable with Message type A */
 | 
			
		||||
#define  PCI_EXP_OBFF_MSGB_EN	0x4000	/* OBFF enable with Message type B */
 | 
			
		||||
#define  PCI_EXP_OBFF_WAKE_EN	0x6000	/* OBFF using WAKE# signaling */
 | 
			
		||||
| 
						 | 
				
			
			@ -535,6 +537,7 @@
 | 
			
		|||
#define PCI_EXT_CAP_ID_ARI	14
 | 
			
		||||
#define PCI_EXT_CAP_ID_ATS	15
 | 
			
		||||
#define PCI_EXT_CAP_ID_SRIOV	16
 | 
			
		||||
#define PCI_EXT_CAP_ID_LTR	24
 | 
			
		||||
 | 
			
		||||
/* Advanced Error Reporting */
 | 
			
		||||
#define PCI_ERR_UNCOR_STATUS	4	/* Uncorrectable Error Status */
 | 
			
		||||
| 
						 | 
				
			
			@ -691,6 +694,12 @@
 | 
			
		|||
#define  PCI_SRIOV_VFM_MO	0x2	/* Active.MigrateOut */
 | 
			
		||||
#define  PCI_SRIOV_VFM_AV	0x3	/* Active.Available */
 | 
			
		||||
 | 
			
		||||
#define PCI_LTR_MAX_SNOOP_LAT	0x4
 | 
			
		||||
#define PCI_LTR_MAX_NOSNOOP_LAT	0x6
 | 
			
		||||
#define  PCI_LTR_VALUE_MASK	0x000003ff
 | 
			
		||||
#define  PCI_LTR_SCALE_MASK	0x00001c00
 | 
			
		||||
#define  PCI_LTR_SCALE_SHIFT	10
 | 
			
		||||
 | 
			
		||||
/* Access Control Service */
 | 
			
		||||
#define PCI_ACS_CAP		0x04	/* ACS Capability Register */
 | 
			
		||||
#define  PCI_ACS_SV		0x01	/* Source Validation */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue