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
This commit is contained in:
@@ -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
|
||||
|
@@ -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;
|
||||
@@ -298,6 +318,21 @@ static void advk_pcie_train_link(struct advk_pcie *pcie)
|
||||
struct device *dev = &pcie->pdev->dev;
|
||||
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");
|
||||
|
Reference in New Issue
Block a user