diff options
| author | Vidya Sagar <vidyas@nvidia.com> | 2018-09-11 05:23:34 -0400 |
|---|---|---|
| committer | mobile promotions <svcmobile_promotions@nvidia.com> | 2018-09-12 10:17:54 -0400 |
| commit | 2df159b5cfcea2fa4af6be6848c0e2ca0f917761 (patch) | |
| tree | f215871f425096e9212405475fa464f1fad96a97 | |
| parent | 03e4b12b8a900dba405858cfb5e6256b3a99568a (diff) | |
PCI: tegra: add hot-plug/unplug support
Adds hot-plug/unplug APIs to support dynamic
creation and destruction of PCIe hierarchy of a
respective host controller
Bug 2312016
Bug 200427921
Change-Id: Id8ecef5ab6b2c55543a3c43de2561f47a80fe282
Signed-off-by: Vidya Sagar <vidyas@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/1819382
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
| -rw-r--r-- | drivers/pci/dwc/pcie-tegra.c | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/drivers/pci/dwc/pcie-tegra.c b/drivers/pci/dwc/pcie-tegra.c index 90d9b7100..49b353171 100644 --- a/drivers/pci/dwc/pcie-tegra.c +++ b/drivers/pci/dwc/pcie-tegra.c | |||
| @@ -566,6 +566,9 @@ static unsigned int pcie_gen_freq[] = { | |||
| 566 | 566 | ||
| 567 | static void tegra_pcie_downstream_dev_to_D0(struct tegra_pcie_dw *pcie); | 567 | static void tegra_pcie_downstream_dev_to_D0(struct tegra_pcie_dw *pcie); |
| 568 | static int tegra_pcie_dw_pme_turnoff(struct tegra_pcie_dw *pcie); | 568 | static int tegra_pcie_dw_pme_turnoff(struct tegra_pcie_dw *pcie); |
| 569 | static int tegra_pcie_dw_runtime_suspend(struct device *dev); | ||
| 570 | static int tegra_pcie_dw_runtime_resume(struct device *dev); | ||
| 571 | static int tegra_pcie_dw_link_up(struct dw_pcie *pci); | ||
| 569 | 572 | ||
| 570 | static inline void dma_common_wr16(void __iomem *p, u32 val, u32 offset) | 573 | static inline void dma_common_wr16(void __iomem *p, u32 val, u32 offset) |
| 571 | { | 574 | { |
| @@ -1773,6 +1776,80 @@ static int verify_voltage_margin(struct seq_file *s, void *data) | |||
| 1773 | return 0; | 1776 | return 0; |
| 1774 | } | 1777 | } |
| 1775 | 1778 | ||
| 1779 | static int __attach_controller(struct tegra_pcie_dw *pcie) | ||
| 1780 | { | ||
| 1781 | int ret; | ||
| 1782 | |||
| 1783 | if (!pcie->link_state && !pcie->power_down_en) { | ||
| 1784 | ret = pm_runtime_put_sync(pcie->dev); | ||
| 1785 | if (ret) | ||
| 1786 | return ret; | ||
| 1787 | } | ||
| 1788 | |||
| 1789 | ret = pm_runtime_get_sync(pcie->dev); | ||
| 1790 | if (ret) | ||
| 1791 | return ret; | ||
| 1792 | pcie->link_state = tegra_pcie_dw_link_up(&pcie->pci); | ||
| 1793 | |||
| 1794 | return 0; | ||
| 1795 | } | ||
| 1796 | |||
| 1797 | static int __detach_controller(struct tegra_pcie_dw *pcie) | ||
| 1798 | { | ||
| 1799 | if (!pcie->link_state && pcie->power_down_en) | ||
| 1800 | return 0; | ||
| 1801 | |||
| 1802 | return pm_runtime_put_sync(pcie->dev); | ||
| 1803 | } | ||
| 1804 | |||
| 1805 | /* Enables root port controller and attempts PCIe link up with the device | ||
| 1806 | * connected downstream. If link is up, registers host controller with | ||
| 1807 | * PCIe sub-system. | ||
| 1808 | * @cookie : opaque pointer returned by tegra_pcie_detach_controller() API | ||
| 1809 | */ | ||
| 1810 | int tegra_pcie_attach_controller(void *cookie) | ||
| 1811 | { | ||
| 1812 | struct tegra_pcie_dw *pcie = (struct tegra_pcie_dw *)cookie; | ||
| 1813 | |||
| 1814 | return __attach_controller(pcie); | ||
| 1815 | } | ||
| 1816 | EXPORT_SYMBOL(tegra_pcie_attach_controller); | ||
| 1817 | |||
| 1818 | /* Removes PCIe hierarchy of the respective host controller and brings PCIe | ||
| 1819 | * link down in a safe way | ||
| 1820 | * @pdev: pointer to end point's pci_dev structure | ||
| 1821 | * returns a cookie which needs to be passed to | ||
| 1822 | * tegra_pcie_attach_controller() API | ||
| 1823 | */ | ||
| 1824 | void *tegra_pcie_detach_controller(struct pci_dev *pdev) | ||
| 1825 | { | ||
| 1826 | struct pcie_port *pp = pdev->sysdata; | ||
| 1827 | struct dw_pcie *pci = to_dw_pcie_from_pp(pp); | ||
| 1828 | struct tegra_pcie_dw *pcie = dw_pcie_to_tegra_pcie(pci); | ||
| 1829 | int ret; | ||
| 1830 | |||
| 1831 | ret = __detach_controller(pcie); | ||
| 1832 | if (ret) | ||
| 1833 | return ERR_PTR(ret); | ||
| 1834 | |||
| 1835 | return (void *)pcie; | ||
| 1836 | } | ||
| 1837 | EXPORT_SYMBOL(tegra_pcie_detach_controller); | ||
| 1838 | |||
| 1839 | static int hot_plug(struct seq_file *s, void *data) | ||
| 1840 | { | ||
| 1841 | struct tegra_pcie_dw *pcie = (struct tegra_pcie_dw *)(s->private); | ||
| 1842 | |||
| 1843 | return __attach_controller(pcie); | ||
| 1844 | } | ||
| 1845 | |||
| 1846 | static int hot_unplug(struct seq_file *s, void *data) | ||
| 1847 | { | ||
| 1848 | struct tegra_pcie_dw *pcie = (struct tegra_pcie_dw *)(s->private); | ||
| 1849 | |||
| 1850 | return __detach_controller(pcie); | ||
| 1851 | } | ||
| 1852 | |||
| 1776 | #define DEFINE_ENTRY(__name) \ | 1853 | #define DEFINE_ENTRY(__name) \ |
| 1777 | static int __name ## _open(struct inode *inode, struct file *file) \ | 1854 | static int __name ## _open(struct inode *inode, struct file *file) \ |
| 1778 | { \ | 1855 | { \ |
| @@ -1798,6 +1875,8 @@ DEFINE_ENTRY(apply_sbr); | |||
| 1798 | DEFINE_ENTRY(aspm_state_cnt); | 1875 | DEFINE_ENTRY(aspm_state_cnt); |
| 1799 | DEFINE_ENTRY(verify_timing_margin); | 1876 | DEFINE_ENTRY(verify_timing_margin); |
| 1800 | DEFINE_ENTRY(verify_voltage_margin); | 1877 | DEFINE_ENTRY(verify_voltage_margin); |
| 1878 | DEFINE_ENTRY(hot_plug); | ||
| 1879 | DEFINE_ENTRY(hot_unplug); | ||
| 1801 | 1880 | ||
| 1802 | #ifdef CONFIG_PCIE_TEGRA_DW_DMA_TEST | 1881 | #ifdef CONFIG_PCIE_TEGRA_DW_DMA_TEST |
| 1803 | static void init_dma_test_debugfs(struct tegra_pcie_dw *pcie) | 1882 | static void init_dma_test_debugfs(struct tegra_pcie_dw *pcie) |
| @@ -1925,6 +2004,16 @@ static int init_debugfs(struct tegra_pcie_dw *pcie) | |||
| 1925 | if (!d) | 2004 | if (!d) |
| 1926 | dev_err(pcie->dev, "debugfs for verify_voltage_margin failed\n"); | 2005 | dev_err(pcie->dev, "debugfs for verify_voltage_margin failed\n"); |
| 1927 | 2006 | ||
| 2007 | d = debugfs_create_file("hot_plug", 0444, pcie->debugfs, | ||
| 2008 | (void *)pcie, &hot_plug_fops); | ||
| 2009 | if (!d) | ||
| 2010 | dev_err(pcie->dev, "debugfs for hot_plug failed\n"); | ||
| 2011 | |||
| 2012 | d = debugfs_create_file("hot_unplug", 0444, pcie->debugfs, | ||
| 2013 | (void *)pcie, &hot_unplug_fops); | ||
| 2014 | if (!d) | ||
| 2015 | dev_err(pcie->dev, "debugfs for hot_unplug failed\n"); | ||
| 2016 | |||
| 1928 | init_dma_test_debugfs(pcie); | 2017 | init_dma_test_debugfs(pcie); |
| 1929 | 2018 | ||
| 1930 | return 0; | 2019 | return 0; |
