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:
Родитель
48a92a8179
Коммит
51c2e0a7e5
|
@ -1979,6 +1979,155 @@ void pci_disable_obff(struct pci_dev *dev)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(pci_disable_obff);
|
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;
|
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);
|
int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type);
|
||||||
void pci_disable_obff(struct pci_dev *dev);
|
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 */
|
/* For use by arch with custom probe code */
|
||||||
void set_pcie_port_type(struct pci_dev *pdev);
|
void set_pcie_port_type(struct pci_dev *pdev);
|
||||||
void set_pcie_hotplug_bridge(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_RTSTA_PENDING 0x20000 /* PME pending */
|
||||||
#define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */
|
#define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */
|
||||||
#define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */
|
#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_MASK 0xc0000 /* OBFF support mechanism */
|
||||||
#define PCI_EXP_OBFF_MSG 0x40000 /* New message signaling */
|
#define PCI_EXP_OBFF_MSG 0x40000 /* New message signaling */
|
||||||
#define PCI_EXP_OBFF_WAKE 0x80000 /* Re-use WAKE# for OBFF */
|
#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_DEVCTL2_ARI 0x20 /* Alternative Routing-ID */
|
||||||
#define PCI_EXP_IDO_REQ_EN 0x100 /* ID-based ordering request enable */
|
#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_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_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_MSGB_EN 0x4000 /* OBFF enable with Message type B */
|
||||||
#define PCI_EXP_OBFF_WAKE_EN 0x6000 /* OBFF using WAKE# signaling */
|
#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_ARI 14
|
||||||
#define PCI_EXT_CAP_ID_ATS 15
|
#define PCI_EXT_CAP_ID_ATS 15
|
||||||
#define PCI_EXT_CAP_ID_SRIOV 16
|
#define PCI_EXT_CAP_ID_SRIOV 16
|
||||||
|
#define PCI_EXT_CAP_ID_LTR 24
|
||||||
|
|
||||||
/* Advanced Error Reporting */
|
/* Advanced Error Reporting */
|
||||||
#define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */
|
#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_MO 0x2 /* Active.MigrateOut */
|
||||||
#define PCI_SRIOV_VFM_AV 0x3 /* Active.Available */
|
#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 */
|
/* Access Control Service */
|
||||||
#define PCI_ACS_CAP 0x04 /* ACS Capability Register */
|
#define PCI_ACS_CAP 0x04 /* ACS Capability Register */
|
||||||
#define PCI_ACS_SV 0x01 /* Source Validation */
|
#define PCI_ACS_SV 0x01 /* Source Validation */
|
||||||
|
|
Загрузка…
Ссылка в новой задаче