diff options
author | Andrey Smirnov <andrew.smirnov@gmail.com> | 2017-03-28 11:42:50 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2017-04-04 18:54:21 -0400 |
commit | bde4a5a00e761f55be92f62378cf5024ced79ee3 (patch) | |
tree | 43ad74ab81da00a4bd69d3e3dad521ff1806aa20 | |
parent | 9b3fe6796d7c0e0c2b87243ce0c7f4744c54efad (diff) |
PCI: imx6: Allow probe deferral by reset GPIO
Some designs implement reset GPIO via a GPIO expander connected to a
peripheral bus. One such example would be i.MX7 Sabre board where said
GPIO is provided by SPI shift register connected to a bitbanged SPI bus.
To support such designs, allow reset GPIO request to defer probing of the
driver.
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Lucas Stach <l.stach@pengutronix.de>
Cc: yurovsky@gmail.com
Cc: Fabio Estevam <fabio.estevam@nxp.com>
Cc: Dong Aisheng <dongas86@gmail.com>
Cc: linux-arm-kernel@lists.infradead.org
-rw-r--r-- | drivers/pci/dwc/pci-imx6.c | 25 |
1 files changed, 17 insertions, 8 deletions
diff --git a/drivers/pci/dwc/pci-imx6.c b/drivers/pci/dwc/pci-imx6.c index e0c36d6f5a21..d62ee3553cfe 100644 --- a/drivers/pci/dwc/pci-imx6.c +++ b/drivers/pci/dwc/pci-imx6.c | |||
@@ -595,8 +595,8 @@ static struct dw_pcie_host_ops imx6_pcie_host_ops = { | |||
595 | .host_init = imx6_pcie_host_init, | 595 | .host_init = imx6_pcie_host_init, |
596 | }; | 596 | }; |
597 | 597 | ||
598 | static int __init imx6_add_pcie_port(struct imx6_pcie *imx6_pcie, | 598 | static int imx6_add_pcie_port(struct imx6_pcie *imx6_pcie, |
599 | struct platform_device *pdev) | 599 | struct platform_device *pdev) |
600 | { | 600 | { |
601 | struct dw_pcie *pci = imx6_pcie->pci; | 601 | struct dw_pcie *pci = imx6_pcie->pci; |
602 | struct pcie_port *pp = &pci->pp; | 602 | struct pcie_port *pp = &pci->pp; |
@@ -636,7 +636,7 @@ static const struct dw_pcie_ops dw_pcie_ops = { | |||
636 | .link_up = imx6_pcie_link_up, | 636 | .link_up = imx6_pcie_link_up, |
637 | }; | 637 | }; |
638 | 638 | ||
639 | static int __init imx6_pcie_probe(struct platform_device *pdev) | 639 | static int imx6_pcie_probe(struct platform_device *pdev) |
640 | { | 640 | { |
641 | struct device *dev = &pdev->dev; | 641 | struct device *dev = &pdev->dev; |
642 | struct dw_pcie *pci; | 642 | struct dw_pcie *pci; |
@@ -660,10 +660,6 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) | |||
660 | imx6_pcie->variant = | 660 | imx6_pcie->variant = |
661 | (enum imx6_pcie_variants)of_device_get_match_data(dev); | 661 | (enum imx6_pcie_variants)of_device_get_match_data(dev); |
662 | 662 | ||
663 | /* Added for PCI abort handling */ | ||
664 | hook_fault_code(16 + 6, imx6q_pcie_abort_handler, SIGBUS, 0, | ||
665 | "imprecise external abort"); | ||
666 | |||
667 | dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 663 | dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
668 | pci->dbi_base = devm_ioremap_resource(dev, dbi_base); | 664 | pci->dbi_base = devm_ioremap_resource(dev, dbi_base); |
669 | if (IS_ERR(pci->dbi_base)) | 665 | if (IS_ERR(pci->dbi_base)) |
@@ -683,6 +679,8 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) | |||
683 | dev_err(dev, "unable to get reset gpio\n"); | 679 | dev_err(dev, "unable to get reset gpio\n"); |
684 | return ret; | 680 | return ret; |
685 | } | 681 | } |
682 | } else if (imx6_pcie->reset_gpio == -EPROBE_DEFER) { | ||
683 | return imx6_pcie->reset_gpio; | ||
686 | } | 684 | } |
687 | 685 | ||
688 | /* Fetch clocks */ | 686 | /* Fetch clocks */ |
@@ -796,11 +794,22 @@ static struct platform_driver imx6_pcie_driver = { | |||
796 | .name = "imx6q-pcie", | 794 | .name = "imx6q-pcie", |
797 | .of_match_table = imx6_pcie_of_match, | 795 | .of_match_table = imx6_pcie_of_match, |
798 | }, | 796 | }, |
797 | .probe = imx6_pcie_probe, | ||
799 | .shutdown = imx6_pcie_shutdown, | 798 | .shutdown = imx6_pcie_shutdown, |
800 | }; | 799 | }; |
801 | 800 | ||
802 | static int __init imx6_pcie_init(void) | 801 | static int __init imx6_pcie_init(void) |
803 | { | 802 | { |
804 | return platform_driver_probe(&imx6_pcie_driver, imx6_pcie_probe); | 803 | /* |
804 | * Since probe() can be deferred we need to make sure that | ||
805 | * hook_fault_code is not called after __init memory is freed | ||
806 | * by kernel and since imx6q_pcie_abort_handler() is a no-op, | ||
807 | * we can install the handler here without risking it | ||
808 | * accessing some uninitialized driver state. | ||
809 | */ | ||
810 | hook_fault_code(16 + 6, imx6q_pcie_abort_handler, SIGBUS, 0, | ||
811 | "imprecise external abort"); | ||
812 | |||
813 | return platform_driver_register(&imx6_pcie_driver); | ||
805 | } | 814 | } |
806 | device_initcall(imx6_pcie_init); | 815 | device_initcall(imx6_pcie_init); |