diff options
author | Manikanta Maddireddy <mmaddireddy@nvidia.com> | 2018-02-28 05:00:34 -0500 |
---|---|---|
committer | Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> | 2018-03-19 05:37:46 -0400 |
commit | da76ba50963b81413ffd3613f84ee9e592220b3d (patch) | |
tree | 165dd961ba46d473b56f46493e2778c0d3ea5264 | |
parent | 662b94c3195654c225174c680094555c0d750d41 (diff) |
PCI: tegra: Add power management support
Tegra186 powergate driver is implemented as power domain driver, power
partition ungate/gate are registered as power_on/power_off callback
functions. There are no direct functions to power gate/ungate host
controller in Tegra186. Host controller driver should add "power-domains"
property in device tree and implement runtime suspend and resume
callback functons. Power gate and ungate is taken care by power domain
driver when host controller driver calls pm_runtime_put_sync and
pm_runtime_get_sync respectively.
Register suspend_noirq & resume_noirq callback functions to allow PCIe to
come up after resume from RAM. Both runtime and noirq pm ops share same
callback functions.
Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
[lorenzo.pieralisi@arm.com: squashed patch to fix compilation]
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Acked-by: Thierry Reding <treding@nvidia.com>
Tested-by: Thierry Reding <treding@nvidia.com>
-rw-r--r-- | drivers/pci/host/pci-tegra.c | 180 |
1 files changed, 113 insertions, 67 deletions
diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index ab057f6f5153..389e74be846c 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c | |||
@@ -1280,31 +1280,25 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) | |||
1280 | } | 1280 | } |
1281 | } | 1281 | } |
1282 | 1282 | ||
1283 | err = tegra_pcie_power_on(pcie); | ||
1284 | if (err) { | ||
1285 | dev_err(dev, "failed to power up: %d\n", err); | ||
1286 | goto phys_put; | ||
1287 | } | ||
1288 | |||
1289 | pads = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pads"); | 1283 | pads = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pads"); |
1290 | pcie->pads = devm_ioremap_resource(dev, pads); | 1284 | pcie->pads = devm_ioremap_resource(dev, pads); |
1291 | if (IS_ERR(pcie->pads)) { | 1285 | if (IS_ERR(pcie->pads)) { |
1292 | err = PTR_ERR(pcie->pads); | 1286 | err = PTR_ERR(pcie->pads); |
1293 | goto poweroff; | 1287 | goto phys_put; |
1294 | } | 1288 | } |
1295 | 1289 | ||
1296 | afi = platform_get_resource_byname(pdev, IORESOURCE_MEM, "afi"); | 1290 | afi = platform_get_resource_byname(pdev, IORESOURCE_MEM, "afi"); |
1297 | pcie->afi = devm_ioremap_resource(dev, afi); | 1291 | pcie->afi = devm_ioremap_resource(dev, afi); |
1298 | if (IS_ERR(pcie->afi)) { | 1292 | if (IS_ERR(pcie->afi)) { |
1299 | err = PTR_ERR(pcie->afi); | 1293 | err = PTR_ERR(pcie->afi); |
1300 | goto poweroff; | 1294 | goto phys_put; |
1301 | } | 1295 | } |
1302 | 1296 | ||
1303 | /* request configuration space, but remap later, on demand */ | 1297 | /* request configuration space, but remap later, on demand */ |
1304 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs"); | 1298 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs"); |
1305 | if (!res) { | 1299 | if (!res) { |
1306 | err = -EADDRNOTAVAIL; | 1300 | err = -EADDRNOTAVAIL; |
1307 | goto poweroff; | 1301 | goto phys_put; |
1308 | } | 1302 | } |
1309 | 1303 | ||
1310 | pcie->cs = *res; | 1304 | pcie->cs = *res; |
@@ -1315,14 +1309,14 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) | |||
1315 | pcie->cfg = devm_ioremap_resource(dev, &pcie->cs); | 1309 | pcie->cfg = devm_ioremap_resource(dev, &pcie->cs); |
1316 | if (IS_ERR(pcie->cfg)) { | 1310 | if (IS_ERR(pcie->cfg)) { |
1317 | err = PTR_ERR(pcie->cfg); | 1311 | err = PTR_ERR(pcie->cfg); |
1318 | goto poweroff; | 1312 | goto phys_put; |
1319 | } | 1313 | } |
1320 | 1314 | ||
1321 | /* request interrupt */ | 1315 | /* request interrupt */ |
1322 | err = platform_get_irq_byname(pdev, "intr"); | 1316 | err = platform_get_irq_byname(pdev, "intr"); |
1323 | if (err < 0) { | 1317 | if (err < 0) { |
1324 | dev_err(dev, "failed to get IRQ: %d\n", err); | 1318 | dev_err(dev, "failed to get IRQ: %d\n", err); |
1325 | goto poweroff; | 1319 | goto phys_put; |
1326 | } | 1320 | } |
1327 | 1321 | ||
1328 | pcie->irq = err; | 1322 | pcie->irq = err; |
@@ -1330,7 +1324,7 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) | |||
1330 | err = request_irq(pcie->irq, tegra_pcie_isr, IRQF_SHARED, "PCIE", pcie); | 1324 | err = request_irq(pcie->irq, tegra_pcie_isr, IRQF_SHARED, "PCIE", pcie); |
1331 | if (err) { | 1325 | if (err) { |
1332 | dev_err(dev, "failed to register IRQ: %d\n", err); | 1326 | dev_err(dev, "failed to register IRQ: %d\n", err); |
1333 | goto poweroff; | 1327 | goto phys_put; |
1334 | } | 1328 | } |
1335 | 1329 | ||
1336 | return 0; | 1330 | return 0; |
@@ -1338,8 +1332,6 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) | |||
1338 | phys_put: | 1332 | phys_put: |
1339 | if (soc->program_uphy) | 1333 | if (soc->program_uphy) |
1340 | tegra_pcie_phys_put(pcie); | 1334 | tegra_pcie_phys_put(pcie); |
1341 | poweroff: | ||
1342 | tegra_pcie_power_off(pcie); | ||
1343 | return err; | 1335 | return err; |
1344 | } | 1336 | } |
1345 | 1337 | ||
@@ -1350,8 +1342,6 @@ static int tegra_pcie_put_resources(struct tegra_pcie *pcie) | |||
1350 | if (pcie->irq > 0) | 1342 | if (pcie->irq > 0) |
1351 | free_irq(pcie->irq, pcie); | 1343 | free_irq(pcie->irq, pcie); |
1352 | 1344 | ||
1353 | tegra_pcie_power_off(pcie); | ||
1354 | |||
1355 | if (soc->program_uphy) | 1345 | if (soc->program_uphy) |
1356 | tegra_pcie_phys_put(pcie); | 1346 | tegra_pcie_phys_put(pcie); |
1357 | 1347 | ||
@@ -1520,15 +1510,13 @@ static const struct irq_domain_ops msi_domain_ops = { | |||
1520 | .map = tegra_msi_map, | 1510 | .map = tegra_msi_map, |
1521 | }; | 1511 | }; |
1522 | 1512 | ||
1523 | static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) | 1513 | static int tegra_pcie_msi_setup(struct tegra_pcie *pcie) |
1524 | { | 1514 | { |
1525 | struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); | 1515 | struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); |
1526 | struct platform_device *pdev = to_platform_device(pcie->dev); | 1516 | struct platform_device *pdev = to_platform_device(pcie->dev); |
1527 | const struct tegra_pcie_soc *soc = pcie->soc; | ||
1528 | struct tegra_msi *msi = &pcie->msi; | 1517 | struct tegra_msi *msi = &pcie->msi; |
1529 | struct device *dev = pcie->dev; | 1518 | struct device *dev = pcie->dev; |
1530 | int err; | 1519 | int err; |
1531 | u32 reg; | ||
1532 | 1520 | ||
1533 | mutex_init(&msi->lock); | 1521 | mutex_init(&msi->lock); |
1534 | 1522 | ||
@@ -1561,6 +1549,20 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) | |||
1561 | /* setup AFI/FPCI range */ | 1549 | /* setup AFI/FPCI range */ |
1562 | msi->pages = __get_free_pages(GFP_KERNEL, 0); | 1550 | msi->pages = __get_free_pages(GFP_KERNEL, 0); |
1563 | msi->phys = virt_to_phys((void *)msi->pages); | 1551 | msi->phys = virt_to_phys((void *)msi->pages); |
1552 | host->msi = &msi->chip; | ||
1553 | |||
1554 | return 0; | ||
1555 | |||
1556 | err: | ||
1557 | irq_domain_remove(msi->domain); | ||
1558 | return err; | ||
1559 | } | ||
1560 | |||
1561 | static void tegra_pcie_enable_msi(struct tegra_pcie *pcie) | ||
1562 | { | ||
1563 | const struct tegra_pcie_soc *soc = pcie->soc; | ||
1564 | struct tegra_msi *msi = &pcie->msi; | ||
1565 | u32 reg; | ||
1564 | 1566 | ||
1565 | afi_writel(pcie, msi->phys >> soc->msi_base_shift, AFI_MSI_FPCI_BAR_ST); | 1567 | afi_writel(pcie, msi->phys >> soc->msi_base_shift, AFI_MSI_FPCI_BAR_ST); |
1566 | afi_writel(pcie, msi->phys, AFI_MSI_AXI_BAR_ST); | 1568 | afi_writel(pcie, msi->phys, AFI_MSI_AXI_BAR_ST); |
@@ -1581,20 +1583,29 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) | |||
1581 | reg = afi_readl(pcie, AFI_INTR_MASK); | 1583 | reg = afi_readl(pcie, AFI_INTR_MASK); |
1582 | reg |= AFI_INTR_MASK_MSI_MASK; | 1584 | reg |= AFI_INTR_MASK_MSI_MASK; |
1583 | afi_writel(pcie, reg, AFI_INTR_MASK); | 1585 | afi_writel(pcie, reg, AFI_INTR_MASK); |
1586 | } | ||
1584 | 1587 | ||
1585 | host->msi = &msi->chip; | 1588 | static void tegra_pcie_msi_teardown(struct tegra_pcie *pcie) |
1589 | { | ||
1590 | struct tegra_msi *msi = &pcie->msi; | ||
1591 | unsigned int i, irq; | ||
1586 | 1592 | ||
1587 | return 0; | 1593 | free_pages(msi->pages, 0); |
1594 | |||
1595 | if (msi->irq > 0) | ||
1596 | free_irq(msi->irq, pcie); | ||
1597 | |||
1598 | for (i = 0; i < INT_PCI_MSI_NR; i++) { | ||
1599 | irq = irq_find_mapping(msi->domain, i); | ||
1600 | if (irq > 0) | ||
1601 | irq_dispose_mapping(irq); | ||
1602 | } | ||
1588 | 1603 | ||
1589 | err: | ||
1590 | irq_domain_remove(msi->domain); | 1604 | irq_domain_remove(msi->domain); |
1591 | return err; | ||
1592 | } | 1605 | } |
1593 | 1606 | ||
1594 | static int tegra_pcie_disable_msi(struct tegra_pcie *pcie) | 1607 | static int tegra_pcie_disable_msi(struct tegra_pcie *pcie) |
1595 | { | 1608 | { |
1596 | struct tegra_msi *msi = &pcie->msi; | ||
1597 | unsigned int i, irq; | ||
1598 | u32 value; | 1609 | u32 value; |
1599 | 1610 | ||
1600 | /* mask the MSI interrupt */ | 1611 | /* mask the MSI interrupt */ |
@@ -1612,19 +1623,6 @@ static int tegra_pcie_disable_msi(struct tegra_pcie *pcie) | |||
1612 | afi_writel(pcie, 0, AFI_MSI_EN_VEC6); | 1623 | afi_writel(pcie, 0, AFI_MSI_EN_VEC6); |
1613 | afi_writel(pcie, 0, AFI_MSI_EN_VEC7); | 1624 | afi_writel(pcie, 0, AFI_MSI_EN_VEC7); |
1614 | 1625 | ||
1615 | free_pages(msi->pages, 0); | ||
1616 | |||
1617 | if (msi->irq > 0) | ||
1618 | free_irq(msi->irq, pcie); | ||
1619 | |||
1620 | for (i = 0; i < INT_PCI_MSI_NR; i++) { | ||
1621 | irq = irq_find_mapping(msi->domain, i); | ||
1622 | if (irq > 0) | ||
1623 | irq_dispose_mapping(irq); | ||
1624 | } | ||
1625 | |||
1626 | irq_domain_remove(msi->domain); | ||
1627 | |||
1628 | return 0; | 1626 | return 0; |
1629 | } | 1627 | } |
1630 | 1628 | ||
@@ -2123,10 +2121,8 @@ static void tegra_pcie_disable_ports(struct tegra_pcie *pcie) | |||
2123 | { | 2121 | { |
2124 | struct tegra_pcie_port *port, *tmp; | 2122 | struct tegra_pcie_port *port, *tmp; |
2125 | 2123 | ||
2126 | list_for_each_entry_safe(port, tmp, &pcie->ports, list) { | 2124 | list_for_each_entry_safe(port, tmp, &pcie->ports, list) |
2127 | tegra_pcie_port_disable(port); | 2125 | tegra_pcie_port_disable(port); |
2128 | tegra_pcie_port_free(port); | ||
2129 | } | ||
2130 | } | 2126 | } |
2131 | 2127 | ||
2132 | static const struct tegra_pcie_port_soc tegra20_pcie_ports[] = { | 2128 | static const struct tegra_pcie_port_soc tegra20_pcie_ports[] = { |
@@ -2381,26 +2377,22 @@ static int tegra_pcie_probe(struct platform_device *pdev) | |||
2381 | return err; | 2377 | return err; |
2382 | } | 2378 | } |
2383 | 2379 | ||
2384 | err = tegra_pcie_enable_controller(pcie); | 2380 | err = tegra_pcie_msi_setup(pcie); |
2385 | if (err) | 2381 | if (err < 0) { |
2382 | dev_err(dev, "failed to enable MSI support: %d\n", err); | ||
2386 | goto put_resources; | 2383 | goto put_resources; |
2384 | } | ||
2387 | 2385 | ||
2388 | err = tegra_pcie_request_resources(pcie); | 2386 | pm_runtime_enable(pcie->dev); |
2389 | if (err) | 2387 | err = pm_runtime_get_sync(pcie->dev); |
2390 | goto disable_controller; | 2388 | if (err) { |
2391 | 2389 | dev_err(dev, "fail to enable pcie controller: %d\n", err); | |
2392 | /* setup the AFI address translations */ | 2390 | goto teardown_msi; |
2393 | tegra_pcie_setup_translations(pcie); | ||
2394 | |||
2395 | if (IS_ENABLED(CONFIG_PCI_MSI)) { | ||
2396 | err = tegra_pcie_enable_msi(pcie); | ||
2397 | if (err < 0) { | ||
2398 | dev_err(dev, "failed to enable MSI support: %d\n", err); | ||
2399 | goto free_resources; | ||
2400 | } | ||
2401 | } | 2391 | } |
2402 | 2392 | ||
2403 | tegra_pcie_enable_ports(pcie); | 2393 | err = tegra_pcie_request_resources(pcie); |
2394 | if (err) | ||
2395 | goto pm_runtime_put; | ||
2404 | 2396 | ||
2405 | host->busnr = pcie->busn.start; | 2397 | host->busnr = pcie->busn.start; |
2406 | host->dev.parent = &pdev->dev; | 2398 | host->dev.parent = &pdev->dev; |
@@ -2411,7 +2403,7 @@ static int tegra_pcie_probe(struct platform_device *pdev) | |||
2411 | err = pci_scan_root_bus_bridge(host); | 2403 | err = pci_scan_root_bus_bridge(host); |
2412 | if (err < 0) { | 2404 | if (err < 0) { |
2413 | dev_err(dev, "failed to register host: %d\n", err); | 2405 | dev_err(dev, "failed to register host: %d\n", err); |
2414 | goto disable_ports; | 2406 | goto free_resources; |
2415 | } | 2407 | } |
2416 | 2408 | ||
2417 | pci_bus_size_bridges(host->bus); | 2409 | pci_bus_size_bridges(host->bus); |
@@ -2430,14 +2422,13 @@ static int tegra_pcie_probe(struct platform_device *pdev) | |||
2430 | 2422 | ||
2431 | return 0; | 2423 | return 0; |
2432 | 2424 | ||
2433 | disable_ports: | ||
2434 | tegra_pcie_disable_ports(pcie); | ||
2435 | if (IS_ENABLED(CONFIG_PCI_MSI)) | ||
2436 | tegra_pcie_disable_msi(pcie); | ||
2437 | free_resources: | 2425 | free_resources: |
2438 | tegra_pcie_free_resources(pcie); | 2426 | tegra_pcie_free_resources(pcie); |
2439 | disable_controller: | 2427 | pm_runtime_put: |
2440 | tegra_pcie_disable_controller(pcie); | 2428 | pm_runtime_put_sync(pcie->dev); |
2429 | pm_runtime_disable(pcie->dev); | ||
2430 | teardown_msi: | ||
2431 | tegra_pcie_msi_teardown(pcie); | ||
2441 | put_resources: | 2432 | put_resources: |
2442 | tegra_pcie_put_resources(pcie); | 2433 | tegra_pcie_put_resources(pcie); |
2443 | return err; | 2434 | return err; |
@@ -2447,13 +2438,32 @@ static int tegra_pcie_remove(struct platform_device *pdev) | |||
2447 | { | 2438 | { |
2448 | struct tegra_pcie *pcie = platform_get_drvdata(pdev); | 2439 | struct tegra_pcie *pcie = platform_get_drvdata(pdev); |
2449 | struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); | 2440 | struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); |
2450 | struct tegra_pcie_port *port; | 2441 | struct tegra_pcie_port *port, *tmp; |
2451 | 2442 | ||
2452 | if (IS_ENABLED(CONFIG_DEBUG_FS)) | 2443 | if (IS_ENABLED(CONFIG_DEBUG_FS)) |
2453 | tegra_pcie_debugfs_exit(pcie); | 2444 | tegra_pcie_debugfs_exit(pcie); |
2454 | 2445 | ||
2455 | pci_stop_root_bus(host->bus); | 2446 | pci_stop_root_bus(host->bus); |
2456 | pci_remove_root_bus(host->bus); | 2447 | pci_remove_root_bus(host->bus); |
2448 | tegra_pcie_free_resources(pcie); | ||
2449 | pm_runtime_put_sync(pcie->dev); | ||
2450 | pm_runtime_disable(pcie->dev); | ||
2451 | |||
2452 | if (IS_ENABLED(CONFIG_PCI_MSI)) | ||
2453 | tegra_pcie_msi_teardown(pcie); | ||
2454 | |||
2455 | tegra_pcie_put_resources(pcie); | ||
2456 | |||
2457 | list_for_each_entry_safe(port, tmp, &pcie->ports, list) | ||
2458 | tegra_pcie_port_free(port); | ||
2459 | |||
2460 | return 0; | ||
2461 | } | ||
2462 | |||
2463 | static int __maybe_unused tegra_pcie_pm_suspend(struct device *dev) | ||
2464 | { | ||
2465 | struct tegra_pcie *pcie = dev_get_drvdata(dev); | ||
2466 | struct tegra_pcie_port *port; | ||
2457 | 2467 | ||
2458 | list_for_each_entry(port, &pcie->ports, list) | 2468 | list_for_each_entry(port, &pcie->ports, list) |
2459 | tegra_pcie_pme_turnoff(port); | 2469 | tegra_pcie_pme_turnoff(port); |
@@ -2463,18 +2473,54 @@ static int tegra_pcie_remove(struct platform_device *pdev) | |||
2463 | if (IS_ENABLED(CONFIG_PCI_MSI)) | 2473 | if (IS_ENABLED(CONFIG_PCI_MSI)) |
2464 | tegra_pcie_disable_msi(pcie); | 2474 | tegra_pcie_disable_msi(pcie); |
2465 | 2475 | ||
2466 | tegra_pcie_free_resources(pcie); | ||
2467 | tegra_pcie_disable_controller(pcie); | 2476 | tegra_pcie_disable_controller(pcie); |
2468 | tegra_pcie_put_resources(pcie); | 2477 | tegra_pcie_power_off(pcie); |
2478 | |||
2479 | return 0; | ||
2480 | } | ||
2481 | |||
2482 | static int __maybe_unused tegra_pcie_pm_resume(struct device *dev) | ||
2483 | { | ||
2484 | struct tegra_pcie *pcie = dev_get_drvdata(dev); | ||
2485 | int err; | ||
2486 | |||
2487 | err = tegra_pcie_power_on(pcie); | ||
2488 | if (err) { | ||
2489 | dev_err(dev, "tegra pcie power on fail: %d\n", err); | ||
2490 | return err; | ||
2491 | } | ||
2492 | err = tegra_pcie_enable_controller(pcie); | ||
2493 | if (err) { | ||
2494 | dev_err(dev, "tegra pcie controller enable fail: %d\n", err); | ||
2495 | goto poweroff; | ||
2496 | } | ||
2497 | tegra_pcie_setup_translations(pcie); | ||
2498 | |||
2499 | if (IS_ENABLED(CONFIG_PCI_MSI)) | ||
2500 | tegra_pcie_enable_msi(pcie); | ||
2501 | |||
2502 | tegra_pcie_enable_ports(pcie); | ||
2469 | 2503 | ||
2470 | return 0; | 2504 | return 0; |
2505 | |||
2506 | poweroff: | ||
2507 | tegra_pcie_power_off(pcie); | ||
2508 | |||
2509 | return err; | ||
2471 | } | 2510 | } |
2472 | 2511 | ||
2512 | static const struct dev_pm_ops tegra_pcie_pm_ops = { | ||
2513 | SET_RUNTIME_PM_OPS(tegra_pcie_pm_suspend, tegra_pcie_pm_resume, NULL) | ||
2514 | SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_pcie_pm_suspend, | ||
2515 | tegra_pcie_pm_resume) | ||
2516 | }; | ||
2517 | |||
2473 | static struct platform_driver tegra_pcie_driver = { | 2518 | static struct platform_driver tegra_pcie_driver = { |
2474 | .driver = { | 2519 | .driver = { |
2475 | .name = "tegra-pcie", | 2520 | .name = "tegra-pcie", |
2476 | .of_match_table = tegra_pcie_of_match, | 2521 | .of_match_table = tegra_pcie_of_match, |
2477 | .suppress_bind_attrs = true, | 2522 | .suppress_bind_attrs = true, |
2523 | .pm = &tegra_pcie_pm_ops, | ||
2478 | }, | 2524 | }, |
2479 | .probe = tegra_pcie_probe, | 2525 | .probe = tegra_pcie_probe, |
2480 | .remove = tegra_pcie_remove, | 2526 | .remove = tegra_pcie_remove, |