Loading drivers/pci/host/pci-tegra.c +278 −76 Original line number Diff line number Diff line Loading @@ -18,10 +18,12 @@ #include <linux/delay.h> #include <linux/export.h> #include <linux/interrupt.h> #include <linux/iopoll.h> #include <linux/irq.h> #include <linux/irqdomain.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> #include <linux/msi.h> #include <linux/of_address.h> #include <linux/of_pci.h> Loading Loading @@ -139,6 +141,8 @@ #define AFI_INTR_EN_FPCI_TIMEOUT (1 << 7) #define AFI_INTR_EN_PRSNT_SENSE (1 << 8) #define AFI_PCIE_PME 0xf0 #define AFI_PCIE_CONFIG 0x0f8 #define AFI_PCIE_CONFIG_PCIE_DISABLE(x) (1 << ((x) + 1)) #define AFI_PCIE_CONFIG_PCIE_DISABLE_ALL 0xe Loading Loading @@ -219,6 +223,8 @@ #define PADS_REFCLK_CFG_PREDI_SHIFT 8 /* 11:8 */ #define PADS_REFCLK_CFG_DRVI_SHIFT 12 /* 15:12 */ #define PME_ACK_TIMEOUT 10000 struct tegra_msi { struct msi_controller chip; DECLARE_BITMAP(used, INT_PCI_MSI_NR); Loading @@ -230,8 +236,16 @@ struct tegra_msi { }; /* used to differentiate between Tegra SoC generations */ struct tegra_pcie_port_soc { struct { u8 turnoff_bit; u8 ack_bit; } pme; }; struct tegra_pcie_soc { unsigned int num_ports; const struct tegra_pcie_port_soc *ports; unsigned int msi_base_shift; u32 pads_pll_ctl; u32 tx_ref_sel; Loading Loading @@ -549,14 +563,25 @@ static int tegra_pcie_request_resources(struct tegra_pcie *pcie) pci_add_resource(windows, &pcie->busn); err = devm_request_pci_bus_resources(dev, windows); if (err < 0) if (err < 0) { pci_free_resource_list(windows); return err; } pci_remap_iospace(&pcie->pio, pcie->io.start); return 0; } static void tegra_pcie_free_resources(struct tegra_pcie *pcie) { struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); struct list_head *windows = &host->windows; pci_unmap_iospace(&pcie->pio); pci_free_resource_list(windows); } static int tegra_pcie_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin) { struct tegra_pcie *pcie = pdev->bus->sysdata; Loading Loading @@ -966,24 +991,35 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) return 0; } static void tegra_pcie_power_off(struct tegra_pcie *pcie) static void tegra_pcie_disable_controller(struct tegra_pcie *pcie) { struct device *dev = pcie->dev; const struct tegra_pcie_soc *soc = pcie->soc; int err; /* TODO: disable and unprepare clocks? */ reset_control_assert(pcie->pcie_xrst); if (soc->program_uphy) { if (pcie->soc->program_uphy) { err = tegra_pcie_phy_power_off(pcie); if (err < 0) dev_err(dev, "failed to power off PHY(s): %d\n", err); dev_err(pcie->dev, "failed to power off PHY(s): %d\n", err); } } reset_control_assert(pcie->pcie_xrst); static void tegra_pcie_power_off(struct tegra_pcie *pcie) { struct device *dev = pcie->dev; const struct tegra_pcie_soc *soc = pcie->soc; int err; reset_control_assert(pcie->afi_rst); reset_control_assert(pcie->pex_rst); clk_disable_unprepare(pcie->pll_e); if (soc->has_cml_clk) clk_disable_unprepare(pcie->cml_clk); clk_disable_unprepare(pcie->afi_clk); clk_disable_unprepare(pcie->pex_clk); if (!dev->pm_domain) tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); Loading Loading @@ -1192,6 +1228,30 @@ static int tegra_pcie_phys_get(struct tegra_pcie *pcie) return 0; } static void tegra_pcie_phys_put(struct tegra_pcie *pcie) { struct tegra_pcie_port *port; struct device *dev = pcie->dev; int err, i; if (pcie->legacy_phy) { err = phy_exit(pcie->phy); if (err < 0) dev_err(dev, "failed to teardown PHY: %d\n", err); return; } list_for_each_entry(port, &pcie->ports, list) { for (i = 0; i < port->lanes; i++) { err = phy_exit(port->phys[i]); if (err < 0) dev_err(dev, "failed to teardown PHY#%u: %d\n", i, err); } } } static int tegra_pcie_get_resources(struct tegra_pcie *pcie) { struct device *dev = pcie->dev; Loading Loading @@ -1220,31 +1280,25 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) } } err = tegra_pcie_power_on(pcie); if (err) { dev_err(dev, "failed to power up: %d\n", err); return err; } pads = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pads"); pcie->pads = devm_ioremap_resource(dev, pads); if (IS_ERR(pcie->pads)) { err = PTR_ERR(pcie->pads); goto poweroff; goto phys_put; } afi = platform_get_resource_byname(pdev, IORESOURCE_MEM, "afi"); pcie->afi = devm_ioremap_resource(dev, afi); if (IS_ERR(pcie->afi)) { err = PTR_ERR(pcie->afi); goto poweroff; goto phys_put; } /* request configuration space, but remap later, on demand */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs"); if (!res) { err = -EADDRNOTAVAIL; goto poweroff; goto phys_put; } pcie->cs = *res; Loading @@ -1255,14 +1309,14 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) pcie->cfg = devm_ioremap_resource(dev, &pcie->cs); if (IS_ERR(pcie->cfg)) { err = PTR_ERR(pcie->cfg); goto poweroff; goto phys_put; } /* request interrupt */ err = platform_get_irq_byname(pdev, "intr"); if (err < 0) { dev_err(dev, "failed to get IRQ: %d\n", err); goto poweroff; goto phys_put; } pcie->irq = err; Loading @@ -1270,34 +1324,54 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) err = request_irq(pcie->irq, tegra_pcie_isr, IRQF_SHARED, "PCIE", pcie); if (err) { dev_err(dev, "failed to register IRQ: %d\n", err); goto poweroff; goto phys_put; } return 0; poweroff: tegra_pcie_power_off(pcie); phys_put: if (soc->program_uphy) tegra_pcie_phys_put(pcie); return err; } static int tegra_pcie_put_resources(struct tegra_pcie *pcie) { struct device *dev = pcie->dev; const struct tegra_pcie_soc *soc = pcie->soc; int err; if (pcie->irq > 0) free_irq(pcie->irq, pcie); tegra_pcie_power_off(pcie); if (soc->program_uphy) tegra_pcie_phys_put(pcie); if (soc->program_uphy) { err = phy_exit(pcie->phy); if (err < 0) dev_err(dev, "failed to teardown PHY: %d\n", err); return 0; } return 0; static void tegra_pcie_pme_turnoff(struct tegra_pcie_port *port) { struct tegra_pcie *pcie = port->pcie; const struct tegra_pcie_soc *soc = pcie->soc; int err; u32 val; u8 ack_bit; val = afi_readl(pcie, AFI_PCIE_PME); val |= (0x1 << soc->ports[port->index].pme.turnoff_bit); afi_writel(pcie, val, AFI_PCIE_PME); ack_bit = soc->ports[port->index].pme.ack_bit; err = readl_poll_timeout(pcie->afi + AFI_PCIE_PME, val, val & (0x1 << ack_bit), 1, PME_ACK_TIMEOUT); if (err) dev_err(pcie->dev, "PME Ack is not received on port: %d\n", port->index); usleep_range(10000, 11000); val = afi_readl(pcie, AFI_PCIE_PME); val &= ~(0x1 << soc->ports[port->index].pme.turnoff_bit); afi_writel(pcie, val, AFI_PCIE_PME); } static int tegra_msi_alloc(struct tegra_msi *chip) Loading Loading @@ -1436,15 +1510,13 @@ static const struct irq_domain_ops msi_domain_ops = { .map = tegra_msi_map, }; static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) static int tegra_pcie_msi_setup(struct tegra_pcie *pcie) { struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); struct platform_device *pdev = to_platform_device(pcie->dev); const struct tegra_pcie_soc *soc = pcie->soc; struct tegra_msi *msi = &pcie->msi; struct device *dev = pcie->dev; int err; u32 reg; mutex_init(&msi->lock); Loading Loading @@ -1477,6 +1549,20 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) /* setup AFI/FPCI range */ msi->pages = __get_free_pages(GFP_KERNEL, 0); msi->phys = virt_to_phys((void *)msi->pages); host->msi = &msi->chip; return 0; err: irq_domain_remove(msi->domain); return err; } static void tegra_pcie_enable_msi(struct tegra_pcie *pcie) { const struct tegra_pcie_soc *soc = pcie->soc; struct tegra_msi *msi = &pcie->msi; u32 reg; afi_writel(pcie, msi->phys >> soc->msi_base_shift, AFI_MSI_FPCI_BAR_ST); afi_writel(pcie, msi->phys, AFI_MSI_AXI_BAR_ST); Loading @@ -1497,20 +1583,29 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) reg = afi_readl(pcie, AFI_INTR_MASK); reg |= AFI_INTR_MASK_MSI_MASK; afi_writel(pcie, reg, AFI_INTR_MASK); } host->msi = &msi->chip; static void tegra_pcie_msi_teardown(struct tegra_pcie *pcie) { struct tegra_msi *msi = &pcie->msi; unsigned int i, irq; return 0; free_pages(msi->pages, 0); if (msi->irq > 0) free_irq(msi->irq, pcie); for (i = 0; i < INT_PCI_MSI_NR; i++) { irq = irq_find_mapping(msi->domain, i); if (irq > 0) irq_dispose_mapping(irq); } err: irq_domain_remove(msi->domain); return err; } static int tegra_pcie_disable_msi(struct tegra_pcie *pcie) { struct tegra_msi *msi = &pcie->msi; unsigned int i, irq; u32 value; /* mask the MSI interrupt */ Loading @@ -1528,19 +1623,6 @@ static int tegra_pcie_disable_msi(struct tegra_pcie *pcie) afi_writel(pcie, 0, AFI_MSI_EN_VEC6); afi_writel(pcie, 0, AFI_MSI_EN_VEC7); free_pages(msi->pages, 0); if (msi->irq > 0) free_irq(msi->irq, pcie); for (i = 0; i < INT_PCI_MSI_NR; i++) { irq = irq_find_mapping(msi->domain, i); if (irq > 0) irq_dispose_mapping(irq); } irq_domain_remove(msi->domain); return 0; } Loading Loading @@ -2035,8 +2117,22 @@ static void tegra_pcie_enable_ports(struct tegra_pcie *pcie) } } static void tegra_pcie_disable_ports(struct tegra_pcie *pcie) { struct tegra_pcie_port *port, *tmp; list_for_each_entry_safe(port, tmp, &pcie->ports, list) tegra_pcie_port_disable(port); } static const struct tegra_pcie_port_soc tegra20_pcie_ports[] = { { .pme.turnoff_bit = 0, .pme.ack_bit = 5 }, { .pme.turnoff_bit = 8, .pme.ack_bit = 10 }, }; static const struct tegra_pcie_soc tegra20_pcie = { .num_ports = 2, .ports = tegra20_pcie_ports, .msi_base_shift = 0, .pads_pll_ctl = PADS_PLL_CTL_TEGRA20, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_DIV10, Loading @@ -2050,8 +2146,15 @@ static const struct tegra_pcie_soc tegra20_pcie = { .program_uphy = true, }; static const struct tegra_pcie_port_soc tegra30_pcie_ports[] = { { .pme.turnoff_bit = 0, .pme.ack_bit = 5 }, { .pme.turnoff_bit = 8, .pme.ack_bit = 10 }, { .pme.turnoff_bit = 16, .pme.ack_bit = 18 }, }; static const struct tegra_pcie_soc tegra30_pcie = { .num_ports = 3, .ports = tegra30_pcie_ports, .msi_base_shift = 8, .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, Loading @@ -2068,6 +2171,7 @@ static const struct tegra_pcie_soc tegra30_pcie = { static const struct tegra_pcie_soc tegra124_pcie = { .num_ports = 2, .ports = tegra20_pcie_ports, .msi_base_shift = 8, .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, Loading @@ -2083,6 +2187,7 @@ static const struct tegra_pcie_soc tegra124_pcie = { static const struct tegra_pcie_soc tegra210_pcie = { .num_ports = 2, .ports = tegra20_pcie_ports, .msi_base_shift = 8, .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, Loading @@ -2096,8 +2201,15 @@ static const struct tegra_pcie_soc tegra210_pcie = { .program_uphy = true, }; static const struct tegra_pcie_port_soc tegra186_pcie_ports[] = { { .pme.turnoff_bit = 0, .pme.ack_bit = 5 }, { .pme.turnoff_bit = 8, .pme.ack_bit = 10 }, { .pme.turnoff_bit = 12, .pme.ack_bit = 14 }, }; static const struct tegra_pcie_soc tegra186_pcie = { .num_ports = 3, .ports = tegra186_pcie_ports, .msi_base_shift = 8, .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, Loading Loading @@ -2209,6 +2321,12 @@ static const struct file_operations tegra_pcie_ports_ops = { .release = seq_release, }; static void tegra_pcie_debugfs_exit(struct tegra_pcie *pcie) { debugfs_remove_recursive(pcie->debugfs); pcie->debugfs = NULL; } static int tegra_pcie_debugfs_init(struct tegra_pcie *pcie) { struct dentry *file; Loading @@ -2225,8 +2343,7 @@ static int tegra_pcie_debugfs_init(struct tegra_pcie *pcie) return 0; remove: debugfs_remove_recursive(pcie->debugfs); pcie->debugfs = NULL; tegra_pcie_debugfs_exit(pcie); return -ENOMEM; } Loading @@ -2244,6 +2361,7 @@ static int tegra_pcie_probe(struct platform_device *pdev) pcie = pci_host_bridge_priv(host); host->sysdata = pcie; platform_set_drvdata(pdev, pcie); pcie->soc = of_device_get_match_data(dev); INIT_LIST_HEAD(&pcie->ports); Loading @@ -2259,26 +2377,22 @@ static int tegra_pcie_probe(struct platform_device *pdev) return err; } err = tegra_pcie_enable_controller(pcie); if (err) goto put_resources; err = tegra_pcie_request_resources(pcie); if (err) goto put_resources; /* setup the AFI address translations */ tegra_pcie_setup_translations(pcie); if (IS_ENABLED(CONFIG_PCI_MSI)) { err = tegra_pcie_enable_msi(pcie); err = tegra_pcie_msi_setup(pcie); if (err < 0) { dev_err(dev, "failed to enable MSI support: %d\n", err); goto put_resources; } pm_runtime_enable(pcie->dev); err = pm_runtime_get_sync(pcie->dev); if (err) { dev_err(dev, "fail to enable pcie controller: %d\n", err); goto teardown_msi; } tegra_pcie_enable_ports(pcie); err = tegra_pcie_request_resources(pcie); if (err) goto pm_runtime_put; host->busnr = pcie->busn.start; host->dev.parent = &pdev->dev; Loading @@ -2289,7 +2403,7 @@ static int tegra_pcie_probe(struct platform_device *pdev) err = pci_scan_root_bus_bridge(host); if (err < 0) { dev_err(dev, "failed to register host: %d\n", err); goto disable_msi; goto free_resources; } pci_bus_size_bridges(host->bus); Loading @@ -2308,20 +2422,108 @@ static int tegra_pcie_probe(struct platform_device *pdev) return 0; disable_msi: if (IS_ENABLED(CONFIG_PCI_MSI)) tegra_pcie_disable_msi(pcie); free_resources: tegra_pcie_free_resources(pcie); pm_runtime_put: pm_runtime_put_sync(pcie->dev); pm_runtime_disable(pcie->dev); teardown_msi: tegra_pcie_msi_teardown(pcie); put_resources: tegra_pcie_put_resources(pcie); return err; } static int tegra_pcie_remove(struct platform_device *pdev) { struct tegra_pcie *pcie = platform_get_drvdata(pdev); struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); struct tegra_pcie_port *port, *tmp; if (IS_ENABLED(CONFIG_DEBUG_FS)) tegra_pcie_debugfs_exit(pcie); pci_stop_root_bus(host->bus); pci_remove_root_bus(host->bus); tegra_pcie_free_resources(pcie); pm_runtime_put_sync(pcie->dev); pm_runtime_disable(pcie->dev); if (IS_ENABLED(CONFIG_PCI_MSI)) tegra_pcie_msi_teardown(pcie); tegra_pcie_put_resources(pcie); list_for_each_entry_safe(port, tmp, &pcie->ports, list) tegra_pcie_port_free(port); return 0; } static int __maybe_unused tegra_pcie_pm_suspend(struct device *dev) { struct tegra_pcie *pcie = dev_get_drvdata(dev); struct tegra_pcie_port *port; list_for_each_entry(port, &pcie->ports, list) tegra_pcie_pme_turnoff(port); tegra_pcie_disable_ports(pcie); if (IS_ENABLED(CONFIG_PCI_MSI)) tegra_pcie_disable_msi(pcie); tegra_pcie_disable_controller(pcie); tegra_pcie_power_off(pcie); return 0; } static int __maybe_unused tegra_pcie_pm_resume(struct device *dev) { struct tegra_pcie *pcie = dev_get_drvdata(dev); int err; err = tegra_pcie_power_on(pcie); if (err) { dev_err(dev, "tegra pcie power on fail: %d\n", err); return err; } err = tegra_pcie_enable_controller(pcie); if (err) { dev_err(dev, "tegra pcie controller enable fail: %d\n", err); goto poweroff; } tegra_pcie_setup_translations(pcie); if (IS_ENABLED(CONFIG_PCI_MSI)) tegra_pcie_enable_msi(pcie); tegra_pcie_enable_ports(pcie); return 0; poweroff: tegra_pcie_power_off(pcie); return err; } static const struct dev_pm_ops tegra_pcie_pm_ops = { SET_RUNTIME_PM_OPS(tegra_pcie_pm_suspend, tegra_pcie_pm_resume, NULL) SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_pcie_pm_suspend, tegra_pcie_pm_resume) }; static struct platform_driver tegra_pcie_driver = { .driver = { .name = "tegra-pcie", .of_match_table = tegra_pcie_of_match, .suppress_bind_attrs = true, .pm = &tegra_pcie_pm_ops, }, .probe = tegra_pcie_probe, .remove = tegra_pcie_remove, }; builtin_platform_driver(tegra_pcie_driver); module_platform_driver(tegra_pcie_driver); MODULE_LICENSE("GPL"); Loading
drivers/pci/host/pci-tegra.c +278 −76 Original line number Diff line number Diff line Loading @@ -18,10 +18,12 @@ #include <linux/delay.h> #include <linux/export.h> #include <linux/interrupt.h> #include <linux/iopoll.h> #include <linux/irq.h> #include <linux/irqdomain.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> #include <linux/msi.h> #include <linux/of_address.h> #include <linux/of_pci.h> Loading Loading @@ -139,6 +141,8 @@ #define AFI_INTR_EN_FPCI_TIMEOUT (1 << 7) #define AFI_INTR_EN_PRSNT_SENSE (1 << 8) #define AFI_PCIE_PME 0xf0 #define AFI_PCIE_CONFIG 0x0f8 #define AFI_PCIE_CONFIG_PCIE_DISABLE(x) (1 << ((x) + 1)) #define AFI_PCIE_CONFIG_PCIE_DISABLE_ALL 0xe Loading Loading @@ -219,6 +223,8 @@ #define PADS_REFCLK_CFG_PREDI_SHIFT 8 /* 11:8 */ #define PADS_REFCLK_CFG_DRVI_SHIFT 12 /* 15:12 */ #define PME_ACK_TIMEOUT 10000 struct tegra_msi { struct msi_controller chip; DECLARE_BITMAP(used, INT_PCI_MSI_NR); Loading @@ -230,8 +236,16 @@ struct tegra_msi { }; /* used to differentiate between Tegra SoC generations */ struct tegra_pcie_port_soc { struct { u8 turnoff_bit; u8 ack_bit; } pme; }; struct tegra_pcie_soc { unsigned int num_ports; const struct tegra_pcie_port_soc *ports; unsigned int msi_base_shift; u32 pads_pll_ctl; u32 tx_ref_sel; Loading Loading @@ -549,14 +563,25 @@ static int tegra_pcie_request_resources(struct tegra_pcie *pcie) pci_add_resource(windows, &pcie->busn); err = devm_request_pci_bus_resources(dev, windows); if (err < 0) if (err < 0) { pci_free_resource_list(windows); return err; } pci_remap_iospace(&pcie->pio, pcie->io.start); return 0; } static void tegra_pcie_free_resources(struct tegra_pcie *pcie) { struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); struct list_head *windows = &host->windows; pci_unmap_iospace(&pcie->pio); pci_free_resource_list(windows); } static int tegra_pcie_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin) { struct tegra_pcie *pcie = pdev->bus->sysdata; Loading Loading @@ -966,24 +991,35 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) return 0; } static void tegra_pcie_power_off(struct tegra_pcie *pcie) static void tegra_pcie_disable_controller(struct tegra_pcie *pcie) { struct device *dev = pcie->dev; const struct tegra_pcie_soc *soc = pcie->soc; int err; /* TODO: disable and unprepare clocks? */ reset_control_assert(pcie->pcie_xrst); if (soc->program_uphy) { if (pcie->soc->program_uphy) { err = tegra_pcie_phy_power_off(pcie); if (err < 0) dev_err(dev, "failed to power off PHY(s): %d\n", err); dev_err(pcie->dev, "failed to power off PHY(s): %d\n", err); } } reset_control_assert(pcie->pcie_xrst); static void tegra_pcie_power_off(struct tegra_pcie *pcie) { struct device *dev = pcie->dev; const struct tegra_pcie_soc *soc = pcie->soc; int err; reset_control_assert(pcie->afi_rst); reset_control_assert(pcie->pex_rst); clk_disable_unprepare(pcie->pll_e); if (soc->has_cml_clk) clk_disable_unprepare(pcie->cml_clk); clk_disable_unprepare(pcie->afi_clk); clk_disable_unprepare(pcie->pex_clk); if (!dev->pm_domain) tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); Loading Loading @@ -1192,6 +1228,30 @@ static int tegra_pcie_phys_get(struct tegra_pcie *pcie) return 0; } static void tegra_pcie_phys_put(struct tegra_pcie *pcie) { struct tegra_pcie_port *port; struct device *dev = pcie->dev; int err, i; if (pcie->legacy_phy) { err = phy_exit(pcie->phy); if (err < 0) dev_err(dev, "failed to teardown PHY: %d\n", err); return; } list_for_each_entry(port, &pcie->ports, list) { for (i = 0; i < port->lanes; i++) { err = phy_exit(port->phys[i]); if (err < 0) dev_err(dev, "failed to teardown PHY#%u: %d\n", i, err); } } } static int tegra_pcie_get_resources(struct tegra_pcie *pcie) { struct device *dev = pcie->dev; Loading Loading @@ -1220,31 +1280,25 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) } } err = tegra_pcie_power_on(pcie); if (err) { dev_err(dev, "failed to power up: %d\n", err); return err; } pads = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pads"); pcie->pads = devm_ioremap_resource(dev, pads); if (IS_ERR(pcie->pads)) { err = PTR_ERR(pcie->pads); goto poweroff; goto phys_put; } afi = platform_get_resource_byname(pdev, IORESOURCE_MEM, "afi"); pcie->afi = devm_ioremap_resource(dev, afi); if (IS_ERR(pcie->afi)) { err = PTR_ERR(pcie->afi); goto poweroff; goto phys_put; } /* request configuration space, but remap later, on demand */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs"); if (!res) { err = -EADDRNOTAVAIL; goto poweroff; goto phys_put; } pcie->cs = *res; Loading @@ -1255,14 +1309,14 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) pcie->cfg = devm_ioremap_resource(dev, &pcie->cs); if (IS_ERR(pcie->cfg)) { err = PTR_ERR(pcie->cfg); goto poweroff; goto phys_put; } /* request interrupt */ err = platform_get_irq_byname(pdev, "intr"); if (err < 0) { dev_err(dev, "failed to get IRQ: %d\n", err); goto poweroff; goto phys_put; } pcie->irq = err; Loading @@ -1270,34 +1324,54 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) err = request_irq(pcie->irq, tegra_pcie_isr, IRQF_SHARED, "PCIE", pcie); if (err) { dev_err(dev, "failed to register IRQ: %d\n", err); goto poweroff; goto phys_put; } return 0; poweroff: tegra_pcie_power_off(pcie); phys_put: if (soc->program_uphy) tegra_pcie_phys_put(pcie); return err; } static int tegra_pcie_put_resources(struct tegra_pcie *pcie) { struct device *dev = pcie->dev; const struct tegra_pcie_soc *soc = pcie->soc; int err; if (pcie->irq > 0) free_irq(pcie->irq, pcie); tegra_pcie_power_off(pcie); if (soc->program_uphy) tegra_pcie_phys_put(pcie); if (soc->program_uphy) { err = phy_exit(pcie->phy); if (err < 0) dev_err(dev, "failed to teardown PHY: %d\n", err); return 0; } return 0; static void tegra_pcie_pme_turnoff(struct tegra_pcie_port *port) { struct tegra_pcie *pcie = port->pcie; const struct tegra_pcie_soc *soc = pcie->soc; int err; u32 val; u8 ack_bit; val = afi_readl(pcie, AFI_PCIE_PME); val |= (0x1 << soc->ports[port->index].pme.turnoff_bit); afi_writel(pcie, val, AFI_PCIE_PME); ack_bit = soc->ports[port->index].pme.ack_bit; err = readl_poll_timeout(pcie->afi + AFI_PCIE_PME, val, val & (0x1 << ack_bit), 1, PME_ACK_TIMEOUT); if (err) dev_err(pcie->dev, "PME Ack is not received on port: %d\n", port->index); usleep_range(10000, 11000); val = afi_readl(pcie, AFI_PCIE_PME); val &= ~(0x1 << soc->ports[port->index].pme.turnoff_bit); afi_writel(pcie, val, AFI_PCIE_PME); } static int tegra_msi_alloc(struct tegra_msi *chip) Loading Loading @@ -1436,15 +1510,13 @@ static const struct irq_domain_ops msi_domain_ops = { .map = tegra_msi_map, }; static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) static int tegra_pcie_msi_setup(struct tegra_pcie *pcie) { struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); struct platform_device *pdev = to_platform_device(pcie->dev); const struct tegra_pcie_soc *soc = pcie->soc; struct tegra_msi *msi = &pcie->msi; struct device *dev = pcie->dev; int err; u32 reg; mutex_init(&msi->lock); Loading Loading @@ -1477,6 +1549,20 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) /* setup AFI/FPCI range */ msi->pages = __get_free_pages(GFP_KERNEL, 0); msi->phys = virt_to_phys((void *)msi->pages); host->msi = &msi->chip; return 0; err: irq_domain_remove(msi->domain); return err; } static void tegra_pcie_enable_msi(struct tegra_pcie *pcie) { const struct tegra_pcie_soc *soc = pcie->soc; struct tegra_msi *msi = &pcie->msi; u32 reg; afi_writel(pcie, msi->phys >> soc->msi_base_shift, AFI_MSI_FPCI_BAR_ST); afi_writel(pcie, msi->phys, AFI_MSI_AXI_BAR_ST); Loading @@ -1497,20 +1583,29 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) reg = afi_readl(pcie, AFI_INTR_MASK); reg |= AFI_INTR_MASK_MSI_MASK; afi_writel(pcie, reg, AFI_INTR_MASK); } host->msi = &msi->chip; static void tegra_pcie_msi_teardown(struct tegra_pcie *pcie) { struct tegra_msi *msi = &pcie->msi; unsigned int i, irq; return 0; free_pages(msi->pages, 0); if (msi->irq > 0) free_irq(msi->irq, pcie); for (i = 0; i < INT_PCI_MSI_NR; i++) { irq = irq_find_mapping(msi->domain, i); if (irq > 0) irq_dispose_mapping(irq); } err: irq_domain_remove(msi->domain); return err; } static int tegra_pcie_disable_msi(struct tegra_pcie *pcie) { struct tegra_msi *msi = &pcie->msi; unsigned int i, irq; u32 value; /* mask the MSI interrupt */ Loading @@ -1528,19 +1623,6 @@ static int tegra_pcie_disable_msi(struct tegra_pcie *pcie) afi_writel(pcie, 0, AFI_MSI_EN_VEC6); afi_writel(pcie, 0, AFI_MSI_EN_VEC7); free_pages(msi->pages, 0); if (msi->irq > 0) free_irq(msi->irq, pcie); for (i = 0; i < INT_PCI_MSI_NR; i++) { irq = irq_find_mapping(msi->domain, i); if (irq > 0) irq_dispose_mapping(irq); } irq_domain_remove(msi->domain); return 0; } Loading Loading @@ -2035,8 +2117,22 @@ static void tegra_pcie_enable_ports(struct tegra_pcie *pcie) } } static void tegra_pcie_disable_ports(struct tegra_pcie *pcie) { struct tegra_pcie_port *port, *tmp; list_for_each_entry_safe(port, tmp, &pcie->ports, list) tegra_pcie_port_disable(port); } static const struct tegra_pcie_port_soc tegra20_pcie_ports[] = { { .pme.turnoff_bit = 0, .pme.ack_bit = 5 }, { .pme.turnoff_bit = 8, .pme.ack_bit = 10 }, }; static const struct tegra_pcie_soc tegra20_pcie = { .num_ports = 2, .ports = tegra20_pcie_ports, .msi_base_shift = 0, .pads_pll_ctl = PADS_PLL_CTL_TEGRA20, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_DIV10, Loading @@ -2050,8 +2146,15 @@ static const struct tegra_pcie_soc tegra20_pcie = { .program_uphy = true, }; static const struct tegra_pcie_port_soc tegra30_pcie_ports[] = { { .pme.turnoff_bit = 0, .pme.ack_bit = 5 }, { .pme.turnoff_bit = 8, .pme.ack_bit = 10 }, { .pme.turnoff_bit = 16, .pme.ack_bit = 18 }, }; static const struct tegra_pcie_soc tegra30_pcie = { .num_ports = 3, .ports = tegra30_pcie_ports, .msi_base_shift = 8, .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, Loading @@ -2068,6 +2171,7 @@ static const struct tegra_pcie_soc tegra30_pcie = { static const struct tegra_pcie_soc tegra124_pcie = { .num_ports = 2, .ports = tegra20_pcie_ports, .msi_base_shift = 8, .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, Loading @@ -2083,6 +2187,7 @@ static const struct tegra_pcie_soc tegra124_pcie = { static const struct tegra_pcie_soc tegra210_pcie = { .num_ports = 2, .ports = tegra20_pcie_ports, .msi_base_shift = 8, .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, Loading @@ -2096,8 +2201,15 @@ static const struct tegra_pcie_soc tegra210_pcie = { .program_uphy = true, }; static const struct tegra_pcie_port_soc tegra186_pcie_ports[] = { { .pme.turnoff_bit = 0, .pme.ack_bit = 5 }, { .pme.turnoff_bit = 8, .pme.ack_bit = 10 }, { .pme.turnoff_bit = 12, .pme.ack_bit = 14 }, }; static const struct tegra_pcie_soc tegra186_pcie = { .num_ports = 3, .ports = tegra186_pcie_ports, .msi_base_shift = 8, .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, Loading Loading @@ -2209,6 +2321,12 @@ static const struct file_operations tegra_pcie_ports_ops = { .release = seq_release, }; static void tegra_pcie_debugfs_exit(struct tegra_pcie *pcie) { debugfs_remove_recursive(pcie->debugfs); pcie->debugfs = NULL; } static int tegra_pcie_debugfs_init(struct tegra_pcie *pcie) { struct dentry *file; Loading @@ -2225,8 +2343,7 @@ static int tegra_pcie_debugfs_init(struct tegra_pcie *pcie) return 0; remove: debugfs_remove_recursive(pcie->debugfs); pcie->debugfs = NULL; tegra_pcie_debugfs_exit(pcie); return -ENOMEM; } Loading @@ -2244,6 +2361,7 @@ static int tegra_pcie_probe(struct platform_device *pdev) pcie = pci_host_bridge_priv(host); host->sysdata = pcie; platform_set_drvdata(pdev, pcie); pcie->soc = of_device_get_match_data(dev); INIT_LIST_HEAD(&pcie->ports); Loading @@ -2259,26 +2377,22 @@ static int tegra_pcie_probe(struct platform_device *pdev) return err; } err = tegra_pcie_enable_controller(pcie); if (err) goto put_resources; err = tegra_pcie_request_resources(pcie); if (err) goto put_resources; /* setup the AFI address translations */ tegra_pcie_setup_translations(pcie); if (IS_ENABLED(CONFIG_PCI_MSI)) { err = tegra_pcie_enable_msi(pcie); err = tegra_pcie_msi_setup(pcie); if (err < 0) { dev_err(dev, "failed to enable MSI support: %d\n", err); goto put_resources; } pm_runtime_enable(pcie->dev); err = pm_runtime_get_sync(pcie->dev); if (err) { dev_err(dev, "fail to enable pcie controller: %d\n", err); goto teardown_msi; } tegra_pcie_enable_ports(pcie); err = tegra_pcie_request_resources(pcie); if (err) goto pm_runtime_put; host->busnr = pcie->busn.start; host->dev.parent = &pdev->dev; Loading @@ -2289,7 +2403,7 @@ static int tegra_pcie_probe(struct platform_device *pdev) err = pci_scan_root_bus_bridge(host); if (err < 0) { dev_err(dev, "failed to register host: %d\n", err); goto disable_msi; goto free_resources; } pci_bus_size_bridges(host->bus); Loading @@ -2308,20 +2422,108 @@ static int tegra_pcie_probe(struct platform_device *pdev) return 0; disable_msi: if (IS_ENABLED(CONFIG_PCI_MSI)) tegra_pcie_disable_msi(pcie); free_resources: tegra_pcie_free_resources(pcie); pm_runtime_put: pm_runtime_put_sync(pcie->dev); pm_runtime_disable(pcie->dev); teardown_msi: tegra_pcie_msi_teardown(pcie); put_resources: tegra_pcie_put_resources(pcie); return err; } static int tegra_pcie_remove(struct platform_device *pdev) { struct tegra_pcie *pcie = platform_get_drvdata(pdev); struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); struct tegra_pcie_port *port, *tmp; if (IS_ENABLED(CONFIG_DEBUG_FS)) tegra_pcie_debugfs_exit(pcie); pci_stop_root_bus(host->bus); pci_remove_root_bus(host->bus); tegra_pcie_free_resources(pcie); pm_runtime_put_sync(pcie->dev); pm_runtime_disable(pcie->dev); if (IS_ENABLED(CONFIG_PCI_MSI)) tegra_pcie_msi_teardown(pcie); tegra_pcie_put_resources(pcie); list_for_each_entry_safe(port, tmp, &pcie->ports, list) tegra_pcie_port_free(port); return 0; } static int __maybe_unused tegra_pcie_pm_suspend(struct device *dev) { struct tegra_pcie *pcie = dev_get_drvdata(dev); struct tegra_pcie_port *port; list_for_each_entry(port, &pcie->ports, list) tegra_pcie_pme_turnoff(port); tegra_pcie_disable_ports(pcie); if (IS_ENABLED(CONFIG_PCI_MSI)) tegra_pcie_disable_msi(pcie); tegra_pcie_disable_controller(pcie); tegra_pcie_power_off(pcie); return 0; } static int __maybe_unused tegra_pcie_pm_resume(struct device *dev) { struct tegra_pcie *pcie = dev_get_drvdata(dev); int err; err = tegra_pcie_power_on(pcie); if (err) { dev_err(dev, "tegra pcie power on fail: %d\n", err); return err; } err = tegra_pcie_enable_controller(pcie); if (err) { dev_err(dev, "tegra pcie controller enable fail: %d\n", err); goto poweroff; } tegra_pcie_setup_translations(pcie); if (IS_ENABLED(CONFIG_PCI_MSI)) tegra_pcie_enable_msi(pcie); tegra_pcie_enable_ports(pcie); return 0; poweroff: tegra_pcie_power_off(pcie); return err; } static const struct dev_pm_ops tegra_pcie_pm_ops = { SET_RUNTIME_PM_OPS(tegra_pcie_pm_suspend, tegra_pcie_pm_resume, NULL) SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_pcie_pm_suspend, tegra_pcie_pm_resume) }; static struct platform_driver tegra_pcie_driver = { .driver = { .name = "tegra-pcie", .of_match_table = tegra_pcie_of_match, .suppress_bind_attrs = true, .pm = &tegra_pcie_pm_ops, }, .probe = tegra_pcie_probe, .remove = tegra_pcie_remove, }; builtin_platform_driver(tegra_pcie_driver); module_platform_driver(tegra_pcie_driver); MODULE_LICENSE("GPL");