diff options
author | Jesse Barnes <jbarnes@virtuousgeek.org> | 2011-01-14 11:53:04 -0500 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2011-05-11 18:18:53 -0400 |
commit | 51c2e0a7e5bc7ed1384cc68cfb95e702571500c9 (patch) | |
tree | 839dc5d6eb233b009c8802cb8cafde68f5a2ce6e /drivers/pci/pci.c | |
parent | 48a92a8179b3e677fac07db7bd109e68f020468c (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>
Diffstat (limited to 'drivers/pci/pci.c')
-rw-r--r-- | drivers/pci/pci.c | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 01e4cab2e5cb..53302cbdb94c 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 | } |
1980 | EXPORT_SYMBOL(pci_disable_obff); | 1980 | EXPORT_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 | */ | ||
1989 | bool 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 | } | ||
2005 | EXPORT_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 | */ | ||
2017 | int 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 | } | ||
2047 | EXPORT_SYMBOL(pci_enable_ltr); | ||
2048 | |||
2049 | /** | ||
2050 | * pci_disable_ltr - disable latency tolerance reporting | ||
2051 | * @dev: PCI device | ||
2052 | */ | ||
2053 | void 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 | } | ||
2073 | EXPORT_SYMBOL(pci_disable_ltr); | ||
2074 | |||
2075 | static 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 | */ | ||
2094 | int 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 | } | ||
2129 | EXPORT_SYMBOL(pci_set_ltr); | ||
2130 | |||
1982 | static int pci_acs_enable; | 2131 | static int pci_acs_enable; |
1983 | 2132 | ||
1984 | /** | 2133 | /** |