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 | |
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>
-rw-r--r-- | drivers/pci/pci.c | 149 | ||||
-rw-r--r-- | include/linux/pci.h | 5 | ||||
-rw-r--r-- | include/linux/pci_regs.h | 9 |
3 files changed, 163 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 | /** |
diff --git a/include/linux/pci.h b/include/linux/pci.h index 45a035cccd93..df4d69b82144 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
@@ -840,6 +840,11 @@ enum pci_obff_signal_type { | |||
840 | int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type); | 840 | int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type); |
841 | void pci_disable_obff(struct pci_dev *dev); | 841 | void pci_disable_obff(struct pci_dev *dev); |
842 | 842 | ||
843 | bool pci_ltr_supported(struct pci_dev *dev); | ||
844 | int pci_enable_ltr(struct pci_dev *dev); | ||
845 | void pci_disable_ltr(struct pci_dev *dev); | ||
846 | int 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 */ |
844 | void set_pcie_port_type(struct pci_dev *pdev); | 849 | void set_pcie_port_type(struct pci_dev *pdev); |
845 | void set_pcie_hotplug_bridge(struct pci_dev *pdev); | 850 | void set_pcie_hotplug_bridge(struct pci_dev *pdev); |
diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index aa420261843d..e8840964aca1 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 */ |