diff options
| author | Sheng Yang <sheng@linux.intel.com> | 2008-10-21 05:38:25 -0400 |
|---|---|---|
| committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2008-10-22 19:42:35 -0400 |
| commit | 8dd7f8036c123296fc4214f9d8810eb485570422 (patch) | |
| tree | 0c6cab9083be724d2c72ba4aabef9e3b0dffd7fc | |
| parent | c4ed02fae78bf6cea0b22edd34a67df972f29832 (diff) | |
PCI: add support for function level reset
Sometimes, it's necessary to enable software's ability to quiesce and
reset endpoint hardware with function-level granularity, so provide
support for it.
The patch implement Function Level Reset(FLR) feature following PCI-e
spec. And this is the first step. We would add more generic method, like
D0/D3, to allow more devices support this function.
The patch contains two functions. pcie_reset_function() is the new
driver API, and, contains some action to quiesce a device. The other
function is a helper: pcie_execute_reset_function() just executes the
reset for a particular device function.
Current the usage model is in KVM. Function reset is necessary for
assigning device to a guest, or moving it between partitions.
For Function Level Reset(FLR), please refer to PCI Express spec chapter
6.6.2.
Signed-off-by: Sheng Yang <sheng@linux.intel.com>
Signed-off-by: Matthew Wilcox <willy@linux.intel.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
| -rw-r--r-- | drivers/pci/pci.c | 98 | ||||
| -rw-r--r-- | include/linux/pci.h | 2 | ||||
| -rw-r--r-- | include/linux/pci_regs.h | 2 |
3 files changed, 102 insertions, 0 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index aee73cf251b6..533aeb5fcbe4 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include <linux/log2.h> | 18 | #include <linux/log2.h> |
| 19 | #include <linux/pci-aspm.h> | 19 | #include <linux/pci-aspm.h> |
| 20 | #include <linux/pm_wakeup.h> | 20 | #include <linux/pm_wakeup.h> |
| 21 | #include <linux/interrupt.h> | ||
| 21 | #include <asm/dma.h> /* isa_dma_bridge_buggy */ | 22 | #include <asm/dma.h> /* isa_dma_bridge_buggy */ |
| 22 | #include "pci.h" | 23 | #include "pci.h" |
| 23 | 24 | ||
| @@ -1746,6 +1747,103 @@ EXPORT_SYMBOL(pci_set_dma_seg_boundary); | |||
| 1746 | #endif | 1747 | #endif |
| 1747 | 1748 | ||
| 1748 | /** | 1749 | /** |
| 1750 | * pci_execute_reset_function() - Reset a PCI device function | ||
| 1751 | * @dev: Device function to reset | ||
| 1752 | * | ||
| 1753 | * Some devices allow an individual function to be reset without affecting | ||
| 1754 | * other functions in the same device. The PCI device must be responsive | ||
| 1755 | * to PCI config space in order to use this function. | ||
| 1756 | * | ||
| 1757 | * The device function is presumed to be unused when this function is called. | ||
| 1758 | * Resetting the device will make the contents of PCI configuration space | ||
| 1759 | * random, so any caller of this must be prepared to reinitialise the | ||
| 1760 | * device including MSI, bus mastering, BARs, decoding IO and memory spaces, | ||
| 1761 | * etc. | ||
| 1762 | * | ||
| 1763 | * Returns 0 if the device function was successfully reset or -ENOTTY if the | ||
| 1764 | * device doesn't support resetting a single function. | ||
| 1765 | */ | ||
| 1766 | int pci_execute_reset_function(struct pci_dev *dev) | ||
| 1767 | { | ||
| 1768 | u16 status; | ||
| 1769 | u32 cap; | ||
| 1770 | int exppos = pci_find_capability(dev, PCI_CAP_ID_EXP); | ||
| 1771 | |||
| 1772 | if (!exppos) | ||
| 1773 | return -ENOTTY; | ||
| 1774 | pci_read_config_dword(dev, exppos + PCI_EXP_DEVCAP, &cap); | ||
| 1775 | if (!(cap & PCI_EXP_DEVCAP_FLR)) | ||
| 1776 | return -ENOTTY; | ||
| 1777 | |||
| 1778 | pci_block_user_cfg_access(dev); | ||
| 1779 | |||
| 1780 | /* Wait for Transaction Pending bit clean */ | ||
| 1781 | msleep(100); | ||
| 1782 | pci_read_config_word(dev, exppos + PCI_EXP_DEVSTA, &status); | ||
| 1783 | if (status & PCI_EXP_DEVSTA_TRPND) { | ||
| 1784 | dev_info(&dev->dev, "Busy after 100ms while trying to reset; " | ||
| 1785 | "sleeping for 1 second\n"); | ||
| 1786 | ssleep(1); | ||
| 1787 | pci_read_config_word(dev, exppos + PCI_EXP_DEVSTA, &status); | ||
| 1788 | if (status & PCI_EXP_DEVSTA_TRPND) | ||
| 1789 | dev_info(&dev->dev, "Still busy after 1s; " | ||
| 1790 | "proceeding with reset anyway\n"); | ||
| 1791 | } | ||
| 1792 | |||
| 1793 | pci_write_config_word(dev, exppos + PCI_EXP_DEVCTL, | ||
| 1794 | PCI_EXP_DEVCTL_BCR_FLR); | ||
| 1795 | mdelay(100); | ||
| 1796 | |||
| 1797 | pci_unblock_user_cfg_access(dev); | ||
| 1798 | return 0; | ||
| 1799 | } | ||
| 1800 | EXPORT_SYMBOL_GPL(pci_execute_reset_function); | ||
| 1801 | |||
| 1802 | /** | ||
| 1803 | * pci_reset_function() - quiesce and reset a PCI device function | ||
| 1804 | * @dev: Device function to reset | ||
| 1805 | * | ||
| 1806 | * Some devices allow an individual function to be reset without affecting | ||
| 1807 | * other functions in the same device. The PCI device must be responsive | ||
| 1808 | * to PCI config space in order to use this function. | ||
| 1809 | * | ||
| 1810 | * This function does not just reset the PCI portion of a device, but | ||
| 1811 | * clears all the state associated with the device. This function differs | ||
| 1812 | * from pci_execute_reset_function in that it saves and restores device state | ||
| 1813 | * over the reset. | ||
| 1814 | * | ||
| 1815 | * Returns 0 if the device function was successfully reset or -ENOTTY if the | ||
| 1816 | * device doesn't support resetting a single function. | ||
| 1817 | */ | ||
| 1818 | int pci_reset_function(struct pci_dev *dev) | ||
| 1819 | { | ||
| 1820 | u32 cap; | ||
| 1821 | int exppos = pci_find_capability(dev, PCI_CAP_ID_EXP); | ||
| 1822 | int r; | ||
| 1823 | |||
| 1824 | if (!exppos) | ||
| 1825 | return -ENOTTY; | ||
| 1826 | pci_read_config_dword(dev, exppos + PCI_EXP_DEVCAP, &cap); | ||
| 1827 | if (!(cap & PCI_EXP_DEVCAP_FLR)) | ||
| 1828 | return -ENOTTY; | ||
| 1829 | |||
| 1830 | if (!dev->msi_enabled && !dev->msix_enabled) | ||
| 1831 | disable_irq(dev->irq); | ||
| 1832 | pci_save_state(dev); | ||
| 1833 | |||
| 1834 | pci_write_config_word(dev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE); | ||
| 1835 | |||
| 1836 | r = pci_execute_reset_function(dev); | ||
| 1837 | |||
| 1838 | pci_restore_state(dev); | ||
| 1839 | if (!dev->msi_enabled && !dev->msix_enabled) | ||
| 1840 | enable_irq(dev->irq); | ||
| 1841 | |||
| 1842 | return r; | ||
| 1843 | } | ||
| 1844 | EXPORT_SYMBOL_GPL(pci_reset_function); | ||
| 1845 | |||
| 1846 | /** | ||
| 1749 | * pcix_get_max_mmrbc - get PCI-X maximum designed memory read byte count | 1847 | * pcix_get_max_mmrbc - get PCI-X maximum designed memory read byte count |
| 1750 | * @dev: PCI device to query | 1848 | * @dev: PCI device to query |
| 1751 | * | 1849 | * |
diff --git a/include/linux/pci.h b/include/linux/pci.h index 085187be29c7..f6f6810296e6 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
| @@ -626,6 +626,8 @@ int pcix_get_mmrbc(struct pci_dev *dev); | |||
| 626 | int pcix_set_mmrbc(struct pci_dev *dev, int mmrbc); | 626 | int pcix_set_mmrbc(struct pci_dev *dev, int mmrbc); |
| 627 | int pcie_get_readrq(struct pci_dev *dev); | 627 | int pcie_get_readrq(struct pci_dev *dev); |
| 628 | int pcie_set_readrq(struct pci_dev *dev, int rq); | 628 | int pcie_set_readrq(struct pci_dev *dev, int rq); |
| 629 | int pci_reset_function(struct pci_dev *dev); | ||
| 630 | int pci_execute_reset_function(struct pci_dev *dev); | ||
| 629 | void pci_update_resource(struct pci_dev *dev, struct resource *res, int resno); | 631 | void pci_update_resource(struct pci_dev *dev, struct resource *res, int resno); |
| 630 | int __must_check pci_assign_resource(struct pci_dev *dev, int i); | 632 | int __must_check pci_assign_resource(struct pci_dev *dev, int i); |
| 631 | int pci_select_bars(struct pci_dev *dev, unsigned long flags); | 633 | int pci_select_bars(struct pci_dev *dev, unsigned long flags); |
diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index eb6686b88f9a..e5effd47ed74 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h | |||
| @@ -377,6 +377,7 @@ | |||
| 377 | #define PCI_EXP_DEVCAP_RBER 0x8000 /* Role-Based Error Reporting */ | 377 | #define PCI_EXP_DEVCAP_RBER 0x8000 /* Role-Based Error Reporting */ |
| 378 | #define PCI_EXP_DEVCAP_PWR_VAL 0x3fc0000 /* Slot Power Limit Value */ | 378 | #define PCI_EXP_DEVCAP_PWR_VAL 0x3fc0000 /* Slot Power Limit Value */ |
| 379 | #define PCI_EXP_DEVCAP_PWR_SCL 0xc000000 /* Slot Power Limit Scale */ | 379 | #define PCI_EXP_DEVCAP_PWR_SCL 0xc000000 /* Slot Power Limit Scale */ |
| 380 | #define PCI_EXP_DEVCAP_FLR 0x10000000 /* Function Level Reset */ | ||
| 380 | #define PCI_EXP_DEVCTL 8 /* Device Control */ | 381 | #define PCI_EXP_DEVCTL 8 /* Device Control */ |
| 381 | #define PCI_EXP_DEVCTL_CERE 0x0001 /* Correctable Error Reporting En. */ | 382 | #define PCI_EXP_DEVCTL_CERE 0x0001 /* Correctable Error Reporting En. */ |
| 382 | #define PCI_EXP_DEVCTL_NFERE 0x0002 /* Non-Fatal Error Reporting Enable */ | 383 | #define PCI_EXP_DEVCTL_NFERE 0x0002 /* Non-Fatal Error Reporting Enable */ |
| @@ -389,6 +390,7 @@ | |||
| 389 | #define PCI_EXP_DEVCTL_AUX_PME 0x0400 /* Auxiliary Power PM Enable */ | 390 | #define PCI_EXP_DEVCTL_AUX_PME 0x0400 /* Auxiliary Power PM Enable */ |
| 390 | #define PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800 /* Enable No Snoop */ | 391 | #define PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800 /* Enable No Snoop */ |
| 391 | #define PCI_EXP_DEVCTL_READRQ 0x7000 /* Max_Read_Request_Size */ | 392 | #define PCI_EXP_DEVCTL_READRQ 0x7000 /* Max_Read_Request_Size */ |
| 393 | #define PCI_EXP_DEVCTL_BCR_FLR 0x8000 /* Bridge Configuration Retry / FLR */ | ||
| 392 | #define PCI_EXP_DEVSTA 10 /* Device Status */ | 394 | #define PCI_EXP_DEVSTA 10 /* Device Status */ |
| 393 | #define PCI_EXP_DEVSTA_CED 0x01 /* Correctable Error Detected */ | 395 | #define PCI_EXP_DEVSTA_CED 0x01 /* Correctable Error Detected */ |
| 394 | #define PCI_EXP_DEVSTA_NFED 0x02 /* Non-Fatal Error Detected */ | 396 | #define PCI_EXP_DEVSTA_NFED 0x02 /* Non-Fatal Error Detected */ |
