diff options
author | Alexander Duyck <alexander.h.duyck@intel.com> | 2014-09-20 19:50:27 -0400 |
---|---|---|
committer | Jeff Kirsher <jeffrey.t.kirsher@intel.com> | 2014-09-23 06:59:19 -0400 |
commit | 19ae1b3fb99c2d5a5a7540c296c4cb4df3e7f552 (patch) | |
tree | 2e65b54e0d55d4aede7c5749dbf612b7468cd889 /drivers/net/ethernet | |
parent | 82dd0f7ee9a3b6e5d861a7c550c738ec308d37c5 (diff) |
fm10k: Add support for PCI power management and error handling
Add PCI power management and error handling to allow the device to support
suspend/resume and recovery of any PCIe errors. The fm10k devices do not
support wake on LAN, and there is no plan to add this as a feature.
Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Diffstat (limited to 'drivers/net/ethernet')
-rw-r--r-- | drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index 229c7e491251..b54c9e18cbdf 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c | |||
@@ -19,6 +19,7 @@ | |||
19 | */ | 19 | */ |
20 | 20 | ||
21 | #include <linux/module.h> | 21 | #include <linux/module.h> |
22 | #include <linux/aer.h> | ||
22 | 23 | ||
23 | #include "fm10k.h" | 24 | #include "fm10k.h" |
24 | 25 | ||
@@ -1535,6 +1536,8 @@ static int fm10k_probe(struct pci_dev *pdev, | |||
1535 | goto err_pci_reg; | 1536 | goto err_pci_reg; |
1536 | } | 1537 | } |
1537 | 1538 | ||
1539 | pci_enable_pcie_error_reporting(pdev); | ||
1540 | |||
1538 | pci_set_master(pdev); | 1541 | pci_set_master(pdev); |
1539 | pci_save_state(pdev); | 1542 | pci_save_state(pdev); |
1540 | 1543 | ||
@@ -1661,14 +1664,232 @@ static void fm10k_remove(struct pci_dev *pdev) | |||
1661 | pci_release_selected_regions(pdev, | 1664 | pci_release_selected_regions(pdev, |
1662 | pci_select_bars(pdev, IORESOURCE_MEM)); | 1665 | pci_select_bars(pdev, IORESOURCE_MEM)); |
1663 | 1666 | ||
1667 | pci_disable_pcie_error_reporting(pdev); | ||
1668 | |||
1664 | pci_disable_device(pdev); | 1669 | pci_disable_device(pdev); |
1665 | } | 1670 | } |
1666 | 1671 | ||
1672 | #ifdef CONFIG_PM | ||
1673 | /** | ||
1674 | * fm10k_resume - Restore device to pre-sleep state | ||
1675 | * @pdev: PCI device information struct | ||
1676 | * | ||
1677 | * fm10k_resume is called after the system has powered back up from a sleep | ||
1678 | * state and is ready to resume operation. This function is meant to restore | ||
1679 | * the device back to its pre-sleep state. | ||
1680 | **/ | ||
1681 | static int fm10k_resume(struct pci_dev *pdev) | ||
1682 | { | ||
1683 | struct fm10k_intfc *interface = pci_get_drvdata(pdev); | ||
1684 | struct net_device *netdev = interface->netdev; | ||
1685 | struct fm10k_hw *hw = &interface->hw; | ||
1686 | u32 err; | ||
1687 | |||
1688 | pci_set_power_state(pdev, PCI_D0); | ||
1689 | pci_restore_state(pdev); | ||
1690 | |||
1691 | /* pci_restore_state clears dev->state_saved so call | ||
1692 | * pci_save_state to restore it. | ||
1693 | */ | ||
1694 | pci_save_state(pdev); | ||
1695 | |||
1696 | err = pci_enable_device_mem(pdev); | ||
1697 | if (err) { | ||
1698 | dev_err(&pdev->dev, "Cannot enable PCI device from suspend\n"); | ||
1699 | return err; | ||
1700 | } | ||
1701 | pci_set_master(pdev); | ||
1702 | |||
1703 | pci_wake_from_d3(pdev, false); | ||
1704 | |||
1705 | /* refresh hw_addr in case it was dropped */ | ||
1706 | hw->hw_addr = interface->uc_addr; | ||
1707 | |||
1708 | /* reset hardware to known state */ | ||
1709 | err = hw->mac.ops.init_hw(&interface->hw); | ||
1710 | if (err) | ||
1711 | return err; | ||
1712 | |||
1713 | /* reset statistics starting values */ | ||
1714 | hw->mac.ops.rebind_hw_stats(hw, &interface->stats); | ||
1715 | |||
1716 | rtnl_lock(); | ||
1717 | |||
1718 | err = fm10k_init_queueing_scheme(interface); | ||
1719 | if (!err) { | ||
1720 | fm10k_mbx_request_irq(interface); | ||
1721 | if (netif_running(netdev)) | ||
1722 | err = fm10k_open(netdev); | ||
1723 | } | ||
1724 | |||
1725 | rtnl_unlock(); | ||
1726 | |||
1727 | if (err) | ||
1728 | return err; | ||
1729 | |||
1730 | netif_device_attach(netdev); | ||
1731 | |||
1732 | return 0; | ||
1733 | } | ||
1734 | |||
1735 | /** | ||
1736 | * fm10k_suspend - Prepare the device for a system sleep state | ||
1737 | * @pdev: PCI device information struct | ||
1738 | * | ||
1739 | * fm10k_suspend is meant to shutdown the device prior to the system entering | ||
1740 | * a sleep state. The fm10k hardware does not support wake on lan so the | ||
1741 | * driver simply needs to shut down the device so it is in a low power state. | ||
1742 | **/ | ||
1743 | static int fm10k_suspend(struct pci_dev *pdev, pm_message_t state) | ||
1744 | { | ||
1745 | struct fm10k_intfc *interface = pci_get_drvdata(pdev); | ||
1746 | struct net_device *netdev = interface->netdev; | ||
1747 | int err = 0; | ||
1748 | |||
1749 | netif_device_detach(netdev); | ||
1750 | |||
1751 | rtnl_lock(); | ||
1752 | |||
1753 | if (netif_running(netdev)) | ||
1754 | fm10k_close(netdev); | ||
1755 | |||
1756 | fm10k_mbx_free_irq(interface); | ||
1757 | |||
1758 | fm10k_clear_queueing_scheme(interface); | ||
1759 | |||
1760 | rtnl_unlock(); | ||
1761 | |||
1762 | err = pci_save_state(pdev); | ||
1763 | if (err) | ||
1764 | return err; | ||
1765 | |||
1766 | pci_disable_device(pdev); | ||
1767 | pci_wake_from_d3(pdev, false); | ||
1768 | pci_set_power_state(pdev, PCI_D3hot); | ||
1769 | |||
1770 | return 0; | ||
1771 | } | ||
1772 | |||
1773 | #endif /* CONFIG_PM */ | ||
1774 | /** | ||
1775 | * fm10k_io_error_detected - called when PCI error is detected | ||
1776 | * @pdev: Pointer to PCI device | ||
1777 | * @state: The current pci connection state | ||
1778 | * | ||
1779 | * This function is called after a PCI bus error affecting | ||
1780 | * this device has been detected. | ||
1781 | */ | ||
1782 | static pci_ers_result_t fm10k_io_error_detected(struct pci_dev *pdev, | ||
1783 | pci_channel_state_t state) | ||
1784 | { | ||
1785 | struct fm10k_intfc *interface = pci_get_drvdata(pdev); | ||
1786 | struct net_device *netdev = interface->netdev; | ||
1787 | |||
1788 | netif_device_detach(netdev); | ||
1789 | |||
1790 | if (state == pci_channel_io_perm_failure) | ||
1791 | return PCI_ERS_RESULT_DISCONNECT; | ||
1792 | |||
1793 | if (netif_running(netdev)) | ||
1794 | fm10k_close(netdev); | ||
1795 | |||
1796 | fm10k_mbx_free_irq(interface); | ||
1797 | |||
1798 | pci_disable_device(pdev); | ||
1799 | |||
1800 | /* Request a slot reset. */ | ||
1801 | return PCI_ERS_RESULT_NEED_RESET; | ||
1802 | } | ||
1803 | |||
1804 | /** | ||
1805 | * fm10k_io_slot_reset - called after the pci bus has been reset. | ||
1806 | * @pdev: Pointer to PCI device | ||
1807 | * | ||
1808 | * Restart the card from scratch, as if from a cold-boot. | ||
1809 | */ | ||
1810 | static pci_ers_result_t fm10k_io_slot_reset(struct pci_dev *pdev) | ||
1811 | { | ||
1812 | struct fm10k_intfc *interface = pci_get_drvdata(pdev); | ||
1813 | pci_ers_result_t result; | ||
1814 | |||
1815 | if (pci_enable_device_mem(pdev)) { | ||
1816 | dev_err(&pdev->dev, | ||
1817 | "Cannot re-enable PCI device after reset.\n"); | ||
1818 | result = PCI_ERS_RESULT_DISCONNECT; | ||
1819 | } else { | ||
1820 | pci_set_master(pdev); | ||
1821 | pci_restore_state(pdev); | ||
1822 | |||
1823 | /* After second error pci->state_saved is false, this | ||
1824 | * resets it so EEH doesn't break. | ||
1825 | */ | ||
1826 | pci_save_state(pdev); | ||
1827 | |||
1828 | pci_wake_from_d3(pdev, false); | ||
1829 | |||
1830 | /* refresh hw_addr in case it was dropped */ | ||
1831 | interface->hw.hw_addr = interface->uc_addr; | ||
1832 | |||
1833 | interface->flags |= FM10K_FLAG_RESET_REQUESTED; | ||
1834 | fm10k_service_event_schedule(interface); | ||
1835 | |||
1836 | result = PCI_ERS_RESULT_RECOVERED; | ||
1837 | } | ||
1838 | |||
1839 | pci_cleanup_aer_uncorrect_error_status(pdev); | ||
1840 | |||
1841 | return result; | ||
1842 | } | ||
1843 | |||
1844 | /** | ||
1845 | * fm10k_io_resume - called when traffic can start flowing again. | ||
1846 | * @pdev: Pointer to PCI device | ||
1847 | * | ||
1848 | * This callback is called when the error recovery driver tells us that | ||
1849 | * its OK to resume normal operation. | ||
1850 | */ | ||
1851 | static void fm10k_io_resume(struct pci_dev *pdev) | ||
1852 | { | ||
1853 | struct fm10k_intfc *interface = pci_get_drvdata(pdev); | ||
1854 | struct net_device *netdev = interface->netdev; | ||
1855 | struct fm10k_hw *hw = &interface->hw; | ||
1856 | int err = 0; | ||
1857 | |||
1858 | /* reset hardware to known state */ | ||
1859 | hw->mac.ops.init_hw(&interface->hw); | ||
1860 | |||
1861 | /* reset statistics starting values */ | ||
1862 | hw->mac.ops.rebind_hw_stats(hw, &interface->stats); | ||
1863 | |||
1864 | /* reassociate interrupts */ | ||
1865 | fm10k_mbx_request_irq(interface); | ||
1866 | |||
1867 | if (netif_running(netdev)) | ||
1868 | err = fm10k_open(netdev); | ||
1869 | |||
1870 | /* final check of hardware state before registering the interface */ | ||
1871 | err = err ? : fm10k_hw_ready(interface); | ||
1872 | |||
1873 | if (!err) | ||
1874 | netif_device_attach(netdev); | ||
1875 | } | ||
1876 | |||
1877 | static const struct pci_error_handlers fm10k_err_handler = { | ||
1878 | .error_detected = fm10k_io_error_detected, | ||
1879 | .slot_reset = fm10k_io_slot_reset, | ||
1880 | .resume = fm10k_io_resume, | ||
1881 | }; | ||
1882 | |||
1667 | static struct pci_driver fm10k_driver = { | 1883 | static struct pci_driver fm10k_driver = { |
1668 | .name = fm10k_driver_name, | 1884 | .name = fm10k_driver_name, |
1669 | .id_table = fm10k_pci_tbl, | 1885 | .id_table = fm10k_pci_tbl, |
1670 | .probe = fm10k_probe, | 1886 | .probe = fm10k_probe, |
1671 | .remove = fm10k_remove, | 1887 | .remove = fm10k_remove, |
1888 | #ifdef CONFIG_PM | ||
1889 | .suspend = fm10k_suspend, | ||
1890 | .resume = fm10k_resume, | ||
1891 | #endif | ||
1892 | .err_handler = &fm10k_err_handler | ||
1672 | }; | 1893 | }; |
1673 | 1894 | ||
1674 | /** | 1895 | /** |