aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Barnes <jbarnes@virtuousgeek.org>2011-01-14 11:53:04 -0500
committerJesse Barnes <jbarnes@virtuousgeek.org>2011-05-11 18:18:53 -0400
commit51c2e0a7e5bc7ed1384cc68cfb95e702571500c9 (patch)
tree839dc5d6eb233b009c8802cb8cafde68f5a2ce6e
parent48a92a8179b3e677fac07db7bd109e68f020468c (diff)
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>
-rw-r--r--drivers/pci/pci.c149
-rw-r--r--include/linux/pci.h5
-rw-r--r--include/linux/pci_regs.h9
3 files changed, 163 insertions, 0 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 01e4cab2e5c..53302cbdb94 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1979,6 +1979,155 @@ void pci_disable_obff(struct pci_dev *dev)
1979} 1979}
1980EXPORT_SYMBOL(pci_disable_obff); 1980EXPORT_SYMBOL(pci_disable_obff);
1981 1981
1982/**
1983 * pci_ltr_supported - check whether a device supports LTR
1984 * @dev: PCI device
1985 *
1986 * RETURNS:
1987 * True if @dev supports latency tolerance reporting, false otherwise.
1988 */
1989bool pci_ltr_supported(struct pci_dev *dev)
1990{
1991 int pos;
1992 u32 cap;
1993
1994 if (!pci_is_pcie(dev))
1995 return false;
1996
1997 pos = pci_pcie_cap(dev);
1998 if (!pos)
1999 return false;
2000
2001 pci_read_config_dword(dev, pos + PCI_EXP_DEVCAP2, &cap);
2002
2003 return cap & PCI_EXP_DEVCAP2_LTR;
2004}
2005EXPORT_SYMBOL(pci_ltr_supported);
2006
2007/**
2008 * pci_enable_ltr - enable latency tolerance reporting
2009 * @dev: PCI device
2010 *
2011 * Enable LTR on @dev if possible, which means enabling it first on
2012 * upstream ports.
2013 *
2014 * RETURNS:
2015 * Zero on success, errno on failure.
2016 */
2017int pci_enable_ltr(struct pci_dev *dev)
2018{
2019 int pos;
2020 u16 ctrl;
2021 int ret;
2022
2023 if (!pci_ltr_supported(dev))
2024 return -ENOTSUPP;
2025
2026 pos = pci_pcie_cap(dev);
2027 if (!pos)
2028 return -ENOTSUPP;
2029
2030 /* Only primary function can enable/disable LTR */
2031 if (PCI_FUNC(dev->devfn) != 0)
2032 return -EINVAL;
2033
2034 /* Enable upstream ports first */
2035 if (dev->bus) {
2036 ret = pci_enable_ltr(dev->bus->self);
2037 if (ret)
2038 return ret;
2039 }
2040
2041 pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &ctrl);
2042 ctrl |= PCI_EXP_LTR_EN;
2043 pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, ctrl);
2044
2045 return 0;
2046}
2047EXPORT_SYMBOL(pci_enable_ltr);
2048
2049/**
2050 * pci_disable_ltr - disable latency tolerance reporting
2051 * @dev: PCI device
2052 */
2053void pci_disable_ltr(struct pci_dev *dev)
2054{
2055 int pos;
2056 u16 ctrl;
2057
2058 if (!pci_ltr_supported(dev))
2059 return;
2060
2061 pos = pci_pcie_cap(dev);
2062 if (!pos)
2063 return;
2064
2065 /* Only primary function can enable/disable LTR */
2066 if (PCI_FUNC(dev->devfn) != 0)
2067 return;
2068
2069 pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &ctrl);
2070 ctrl &= ~PCI_EXP_LTR_EN;
2071 pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, ctrl);
2072}
2073EXPORT_SYMBOL(pci_disable_ltr);
2074
2075static int __pci_ltr_scale(int *val)
2076{
2077 int scale = 0;
2078
2079 while (*val > 1023) {
2080 *val = (*val + 31) / 32;
2081 scale++;
2082 }
2083 return scale;
2084}
2085
2086/**
2087 * pci_set_ltr - set LTR latency values
2088 * @dev: PCI device
2089 * @snoop_lat_ns: snoop latency in nanoseconds
2090 * @nosnoop_lat_ns: nosnoop latency in nanoseconds
2091 *
2092 * Figure out the scale and set the LTR values accordingly.
2093 */
2094int pci_set_ltr(struct pci_dev *dev, int snoop_lat_ns, int nosnoop_lat_ns)
2095{
2096 int pos, ret, snoop_scale, nosnoop_scale;
2097 u16 val;
2098
2099 if (!pci_ltr_supported(dev))
2100 return -ENOTSUPP;
2101
2102 snoop_scale = __pci_ltr_scale(&snoop_lat_ns);
2103 nosnoop_scale = __pci_ltr_scale(&nosnoop_lat_ns);
2104
2105 if (snoop_lat_ns > PCI_LTR_VALUE_MASK ||
2106 nosnoop_lat_ns > PCI_LTR_VALUE_MASK)
2107 return -EINVAL;
2108
2109 if ((snoop_scale > (PCI_LTR_SCALE_MASK >> PCI_LTR_SCALE_SHIFT)) ||
2110 (nosnoop_scale > (PCI_LTR_SCALE_MASK >> PCI_LTR_SCALE_SHIFT)))
2111 return -EINVAL;
2112
2113 pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_LTR);
2114 if (!pos)
2115 return -ENOTSUPP;
2116
2117 val = (snoop_scale << PCI_LTR_SCALE_SHIFT) | snoop_lat_ns;
2118 ret = pci_write_config_word(dev, pos + PCI_LTR_MAX_SNOOP_LAT, val);
2119 if (ret != 4)
2120 return -EIO;
2121
2122 val = (nosnoop_scale << PCI_LTR_SCALE_SHIFT) | nosnoop_lat_ns;
2123 ret = pci_write_config_word(dev, pos + PCI_LTR_MAX_NOSNOOP_LAT, val);
2124 if (ret != 4)
2125 return -EIO;
2126
2127 return 0;
2128}
2129EXPORT_SYMBOL(pci_set_ltr);
2130
1982static int pci_acs_enable; 2131static int pci_acs_enable;
1983 2132
1984/** 2133/**
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 45a035cccd9..df4d69b8214 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -840,6 +840,11 @@ enum pci_obff_signal_type {
840int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type); 840int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type);
841void pci_disable_obff(struct pci_dev *dev); 841void pci_disable_obff(struct pci_dev *dev);
842 842
843bool pci_ltr_supported(struct pci_dev *dev);
844int pci_enable_ltr(struct pci_dev *dev);
845void pci_disable_ltr(struct pci_dev *dev);
846int pci_set_ltr(struct pci_dev *dev, int snoop_lat_ns, int nosnoop_lat_ns);
847
843/* For use by arch with custom probe code */ 848/* For use by arch with custom probe code */
844void set_pcie_port_type(struct pci_dev *pdev); 849void set_pcie_port_type(struct pci_dev *pdev);
845void set_pcie_hotplug_bridge(struct pci_dev *pdev); 850void set_pcie_hotplug_bridge(struct pci_dev *pdev);
diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h
index aa420261843..e8840964aca 100644
--- a/include/linux/pci_regs.h
+++ b/include/linux/pci_regs.h
@@ -508,6 +508,7 @@
508#define PCI_EXP_RTSTA_PENDING 0x20000 /* PME pending */ 508#define PCI_EXP_RTSTA_PENDING 0x20000 /* PME pending */
509#define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */ 509#define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */
510#define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */ 510#define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */
511#define PCI_EXP_DEVCAP2_LTR 0x800 /* Latency tolerance reporting */
511#define PCI_EXP_OBFF_MASK 0xc0000 /* OBFF support mechanism */ 512#define PCI_EXP_OBFF_MASK 0xc0000 /* OBFF support mechanism */
512#define PCI_EXP_OBFF_MSG 0x40000 /* New message signaling */ 513#define PCI_EXP_OBFF_MSG 0x40000 /* New message signaling */
513#define PCI_EXP_OBFF_WAKE 0x80000 /* Re-use WAKE# for OBFF */ 514#define PCI_EXP_OBFF_WAKE 0x80000 /* Re-use WAKE# for OBFF */
@@ -515,6 +516,7 @@
515#define PCI_EXP_DEVCTL2_ARI 0x20 /* Alternative Routing-ID */ 516#define PCI_EXP_DEVCTL2_ARI 0x20 /* Alternative Routing-ID */
516#define PCI_EXP_IDO_REQ_EN 0x100 /* ID-based ordering request enable */ 517#define PCI_EXP_IDO_REQ_EN 0x100 /* ID-based ordering request enable */
517#define PCI_EXP_IDO_CMP_EN 0x200 /* ID-based ordering completion enable */ 518#define PCI_EXP_IDO_CMP_EN 0x200 /* ID-based ordering completion enable */
519#define PCI_EXP_LTR_EN 0x400 /* Latency tolerance reporting */
518#define PCI_EXP_OBFF_MSGA_EN 0x2000 /* OBFF enable with Message type A */ 520#define PCI_EXP_OBFF_MSGA_EN 0x2000 /* OBFF enable with Message type A */
519#define PCI_EXP_OBFF_MSGB_EN 0x4000 /* OBFF enable with Message type B */ 521#define PCI_EXP_OBFF_MSGB_EN 0x4000 /* OBFF enable with Message type B */
520#define PCI_EXP_OBFF_WAKE_EN 0x6000 /* OBFF using WAKE# signaling */ 522#define PCI_EXP_OBFF_WAKE_EN 0x6000 /* OBFF using WAKE# signaling */
@@ -535,6 +537,7 @@
535#define PCI_EXT_CAP_ID_ARI 14 537#define PCI_EXT_CAP_ID_ARI 14
536#define PCI_EXT_CAP_ID_ATS 15 538#define PCI_EXT_CAP_ID_ATS 15
537#define PCI_EXT_CAP_ID_SRIOV 16 539#define PCI_EXT_CAP_ID_SRIOV 16
540#define PCI_EXT_CAP_ID_LTR 24
538 541
539/* Advanced Error Reporting */ 542/* Advanced Error Reporting */
540#define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */ 543#define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */
@@ -691,6 +694,12 @@
691#define PCI_SRIOV_VFM_MO 0x2 /* Active.MigrateOut */ 694#define PCI_SRIOV_VFM_MO 0x2 /* Active.MigrateOut */
692#define PCI_SRIOV_VFM_AV 0x3 /* Active.Available */ 695#define PCI_SRIOV_VFM_AV 0x3 /* Active.Available */
693 696
697#define PCI_LTR_MAX_SNOOP_LAT 0x4
698#define PCI_LTR_MAX_NOSNOOP_LAT 0x6
699#define PCI_LTR_VALUE_MASK 0x000003ff
700#define PCI_LTR_SCALE_MASK 0x00001c00
701#define PCI_LTR_SCALE_SHIFT 10
702
694/* Access Control Service */ 703/* Access Control Service */
695#define PCI_ACS_CAP 0x04 /* ACS Capability Register */ 704#define PCI_ACS_CAP 0x04 /* ACS Capability Register */
696#define PCI_ACS_SV 0x01 /* Source Validation */ 705#define PCI_ACS_SV 0x01 /* Source Validation */