diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2020-10-21 17:58:37 +0300 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2020-10-21 17:58:37 +0300 |
commit | d1640a83073c08cfe237d54db1b251cf63a4ce90 (patch) | |
tree | 8a7ac4274aa239b7200b0e2cc1ca4c3ea536d8c6 /drivers/pci | |
parent | 5bedfdb2ff8b92e06fe575524f477a9143724f0b (diff) | |
parent | b0c6ae0f8948a2be6bf4e8b4bbab9ca1343289b6 (diff) | |
download | linux-d1640a83073c08cfe237d54db1b251cf63a4ce90.tar.xz |
Merge branch 'remotes/lorenzo/pci/aardvark'
- Fix s390 build error (Pali Rohár)
- Check for errors from pci_bridge_emul_init() (Pali Rohár)
- Export pci-bridge-emul functions for use by modules (Pali Rohár)
- Make aardvark driver modular (Pali Rohár)
- Move PCIe reset code to advk_pcie_train_link() (Pali Rohár)
- Convert internal SMCC firmware return codes to errno (Pali Rohár)
- Fix initialization with old Marvell's Arm Trusted Firmware (Pali Rohár)
* remotes/lorenzo/pci/aardvark:
PCI: aardvark: Fix initialization with old Marvell's Arm Trusted Firmware
phy: marvell: comphy: Convert internal SMCC firmware return codes to errno
PCI: aardvark: Move PCIe reset card code to advk_pcie_train_link()
PCI: aardvark: Implement driver 'remove' function and allow to build it as module
PCI: pci-bridge-emul: Export API functions
PCI: aardvark: Check for errors from pci_bridge_emul_init() call
PCI: aardvark: Fix compilation on s390
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/controller/Kconfig | 2 | ||||
-rw-r--r-- | drivers/pci/controller/pci-aardvark.c | 108 | ||||
-rw-r--r-- | drivers/pci/pci-bridge-emul.c | 4 |
3 files changed, 74 insertions, 40 deletions
diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index 42dbc167765c..2260fe09c395 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -12,7 +12,7 @@ config PCI_MVEBU select PCI_BRIDGE_EMUL config PCI_AARDVARK - bool "Aardvark PCIe controller" + tristate "Aardvark PCIe controller" depends on (ARCH_MVEBU && ARM64) || COMPILE_TEST depends on OF depends on PCI_MSI_IRQ_DOMAIN diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index 1559f79e63b6..0be485a25327 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -9,11 +9,12 @@ */ #include <linux/delay.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/irqdomain.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/pci.h> #include <linux/init.h> #include <linux/phy/phy.h> @@ -251,6 +252,25 @@ static void advk_pcie_wait_for_retrain(struct advk_pcie *pcie) } } +static void advk_pcie_issue_perst(struct advk_pcie *pcie) +{ + u32 reg; + + if (!pcie->reset_gpio) + return; + + /* PERST does not work for some cards when link training is enabled */ + reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); + reg &= ~LINK_TRAINING_EN; + advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); + + /* 10ms delay is needed for some cards */ + dev_info(&pcie->pdev->dev, "issuing PERST via reset GPIO for 10ms\n"); + gpiod_set_value_cansleep(pcie->reset_gpio, 1); + usleep_range(10000, 11000); + gpiod_set_value_cansleep(pcie->reset_gpio, 0); +} + static int advk_pcie_train_at_gen(struct advk_pcie *pcie, int gen) { int ret, neg_gen; @@ -299,6 +319,21 @@ static void advk_pcie_train_link(struct advk_pcie *pcie) int neg_gen = -1, gen; /* + * Reset PCIe card via PERST# signal. Some cards are not detected + * during link training when they are in some non-initial state. + */ + advk_pcie_issue_perst(pcie); + + /* + * PERST# signal could have been asserted by pinctrl subsystem before + * probe() callback has been called or issued explicitly by reset gpio + * function advk_pcie_issue_perst(), making the endpoint going into + * fundamental reset. As required by PCI Express spec a delay for at + * least 100ms after such a reset before link training is needed. + */ + msleep(PCI_PM_D3COLD_WAIT); + + /* * Try link training at link gen specified by device tree property * 'max-link-speed'. If this fails, iteratively train at lower gen. */ @@ -330,31 +365,10 @@ err: dev_err(dev, "link never came up\n"); } -static void advk_pcie_issue_perst(struct advk_pcie *pcie) -{ - u32 reg; - - if (!pcie->reset_gpio) - return; - - /* PERST does not work for some cards when link training is enabled */ - reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); - reg &= ~LINK_TRAINING_EN; - advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); - - /* 10ms delay is needed for some cards */ - dev_info(&pcie->pdev->dev, "issuing PERST via reset GPIO for 10ms\n"); - gpiod_set_value_cansleep(pcie->reset_gpio, 1); - usleep_range(10000, 11000); - gpiod_set_value_cansleep(pcie->reset_gpio, 0); -} - static void advk_pcie_setup_hw(struct advk_pcie *pcie) { u32 reg; - advk_pcie_issue_perst(pcie); - /* Enable TX */ reg = advk_readl(pcie, PCIE_CORE_REF_CLK_REG); reg |= PCIE_CORE_REF_CLK_TX_ENABLE; @@ -431,15 +445,6 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie) reg |= PIO_CTRL_ADDR_WIN_DISABLE; advk_writel(pcie, reg, PIO_CTRL); - /* - * PERST# signal could have been asserted by pinctrl subsystem before - * probe() callback has been called or issued explicitly by reset gpio - * function advk_pcie_issue_perst(), making the endpoint going into - * fundamental reset. As required by PCI Express spec a delay for at - * least 100ms after such a reset before link training is needed. - */ - msleep(PCI_PM_D3COLD_WAIT); - advk_pcie_train_link(pcie); /* @@ -607,7 +612,7 @@ static struct pci_bridge_emul_ops advk_pci_bridge_emul_ops = { * Initialize the configuration space of the PCI-to-PCI bridge * associated with the given PCIe interface. */ -static void advk_sw_pci_bridge_init(struct advk_pcie *pcie) +static int advk_sw_pci_bridge_init(struct advk_pcie *pcie) { struct pci_bridge_emul *bridge = &pcie->bridge; @@ -633,8 +638,7 @@ static void advk_sw_pci_bridge_init(struct advk_pcie *pcie) bridge->data = pcie; bridge->ops = &advk_pci_bridge_emul_ops; - pci_bridge_emul_init(bridge, 0); - + return pci_bridge_emul_init(bridge, 0); } static bool advk_pcie_valid_device(struct advk_pcie *pcie, struct pci_bus *bus, @@ -1077,7 +1081,9 @@ static int advk_pcie_enable_phy(struct advk_pcie *pcie) } ret = phy_power_on(pcie->phy); - if (ret) { + if (ret == -EOPNOTSUPP) { + dev_warn(&pcie->pdev->dev, "PHY unsupported by firmware\n"); + } else if (ret) { phy_exit(pcie->phy); return ret; } @@ -1122,6 +1128,7 @@ static int advk_pcie_probe(struct platform_device *pdev) pcie = pci_host_bridge_priv(bridge); pcie->pdev = pdev; + platform_set_drvdata(pdev, pcie); pcie->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pcie->base)) @@ -1167,7 +1174,11 @@ static int advk_pcie_probe(struct platform_device *pdev) advk_pcie_setup_hw(pcie); - advk_sw_pci_bridge_init(pcie); + ret = advk_sw_pci_bridge_init(pcie); + if (ret) { + dev_err(dev, "Failed to register emulated root PCI bridge\n"); + return ret; + } ret = advk_pcie_init_irq_domain(pcie); if (ret) { @@ -1195,18 +1206,37 @@ static int advk_pcie_probe(struct platform_device *pdev) return 0; } +static int advk_pcie_remove(struct platform_device *pdev) +{ + struct advk_pcie *pcie = platform_get_drvdata(pdev); + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); + + pci_lock_rescan_remove(); + pci_stop_root_bus(bridge->bus); + pci_remove_root_bus(bridge->bus); + pci_unlock_rescan_remove(); + + advk_pcie_remove_msi_irq_domain(pcie); + advk_pcie_remove_irq_domain(pcie); + + return 0; +} + static const struct of_device_id advk_pcie_of_match_table[] = { { .compatible = "marvell,armada-3700-pcie", }, {}, }; +MODULE_DEVICE_TABLE(of, advk_pcie_of_match_table); static struct platform_driver advk_pcie_driver = { .driver = { .name = "advk-pcie", .of_match_table = advk_pcie_of_match_table, - /* Driver unloading/unbinding currently not supported */ - .suppress_bind_attrs = true, }, .probe = advk_pcie_probe, + .remove = advk_pcie_remove, }; -builtin_platform_driver(advk_pcie_driver); +module_platform_driver(advk_pcie_driver); + +MODULE_DESCRIPTION("Aardvark PCIe controller"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c index ccf26d12ec61..139869d50eb2 100644 --- a/drivers/pci/pci-bridge-emul.c +++ b/drivers/pci/pci-bridge-emul.c @@ -294,6 +294,7 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge, return 0; } +EXPORT_SYMBOL_GPL(pci_bridge_emul_init); /* * Cleanup a pci_bridge_emul structure that was previously initialized @@ -305,6 +306,7 @@ void pci_bridge_emul_cleanup(struct pci_bridge_emul *bridge) kfree(bridge->pcie_cap_regs_behavior); kfree(bridge->pci_regs_behavior); } +EXPORT_SYMBOL_GPL(pci_bridge_emul_cleanup); /* * Should be called by the PCI controller driver when reading the PCI @@ -366,6 +368,7 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, return PCIBIOS_SUCCESSFUL; } +EXPORT_SYMBOL_GPL(pci_bridge_emul_conf_read); /* * Should be called by the PCI controller driver when writing the PCI @@ -430,3 +433,4 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, return PCIBIOS_SUCCESSFUL; } +EXPORT_SYMBOL_GPL(pci_bridge_emul_conf_write); |