Commit d1640a83 authored by Bjorn Helgaas's avatar Bjorn Helgaas

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
parents 5bedfdb2 b0c6ae0f
...@@ -12,7 +12,7 @@ config PCI_MVEBU ...@@ -12,7 +12,7 @@ config PCI_MVEBU
select PCI_BRIDGE_EMUL select PCI_BRIDGE_EMUL
config PCI_AARDVARK config PCI_AARDVARK
bool "Aardvark PCIe controller" tristate "Aardvark PCIe controller"
depends on (ARCH_MVEBU && ARM64) || COMPILE_TEST depends on (ARCH_MVEBU && ARM64) || COMPILE_TEST
depends on OF depends on OF
depends on PCI_MSI_IRQ_DOMAIN depends on PCI_MSI_IRQ_DOMAIN
......
...@@ -9,11 +9,12 @@ ...@@ -9,11 +9,12 @@
*/ */
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/gpio.h> #include <linux/gpio/consumer.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/irqdomain.h> #include <linux/irqdomain.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/phy/phy.h> #include <linux/phy/phy.h>
...@@ -251,6 +252,25 @@ static void advk_pcie_wait_for_retrain(struct advk_pcie *pcie) ...@@ -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) static int advk_pcie_train_at_gen(struct advk_pcie *pcie, int gen)
{ {
int ret, neg_gen; int ret, neg_gen;
...@@ -298,6 +318,21 @@ static void advk_pcie_train_link(struct advk_pcie *pcie) ...@@ -298,6 +318,21 @@ static void advk_pcie_train_link(struct advk_pcie *pcie)
struct device *dev = &pcie->pdev->dev; struct device *dev = &pcie->pdev->dev;
int neg_gen = -1, gen; 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 * Try link training at link gen specified by device tree property
* 'max-link-speed'. If this fails, iteratively train at lower gen. * 'max-link-speed'. If this fails, iteratively train at lower gen.
...@@ -330,31 +365,10 @@ static void advk_pcie_train_link(struct advk_pcie *pcie) ...@@ -330,31 +365,10 @@ static void advk_pcie_train_link(struct advk_pcie *pcie)
dev_err(dev, "link never came up\n"); 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) static void advk_pcie_setup_hw(struct advk_pcie *pcie)
{ {
u32 reg; u32 reg;
advk_pcie_issue_perst(pcie);
/* Enable TX */ /* Enable TX */
reg = advk_readl(pcie, PCIE_CORE_REF_CLK_REG); reg = advk_readl(pcie, PCIE_CORE_REF_CLK_REG);
reg |= PCIE_CORE_REF_CLK_TX_ENABLE; reg |= PCIE_CORE_REF_CLK_TX_ENABLE;
...@@ -431,15 +445,6 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie) ...@@ -431,15 +445,6 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie)
reg |= PIO_CTRL_ADDR_WIN_DISABLE; reg |= PIO_CTRL_ADDR_WIN_DISABLE;
advk_writel(pcie, reg, PIO_CTRL); 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); advk_pcie_train_link(pcie);
/* /*
...@@ -607,7 +612,7 @@ static struct pci_bridge_emul_ops advk_pci_bridge_emul_ops = { ...@@ -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 * Initialize the configuration space of the PCI-to-PCI bridge
* associated with the given PCIe interface. * 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; struct pci_bridge_emul *bridge = &pcie->bridge;
...@@ -633,8 +638,7 @@ static void advk_sw_pci_bridge_init(struct advk_pcie *pcie) ...@@ -633,8 +638,7 @@ static void advk_sw_pci_bridge_init(struct advk_pcie *pcie)
bridge->data = pcie; bridge->data = pcie;
bridge->ops = &advk_pci_bridge_emul_ops; 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, 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) ...@@ -1077,7 +1081,9 @@ static int advk_pcie_enable_phy(struct advk_pcie *pcie)
} }
ret = phy_power_on(pcie->phy); 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); phy_exit(pcie->phy);
return ret; return ret;
} }
...@@ -1122,6 +1128,7 @@ static int advk_pcie_probe(struct platform_device *pdev) ...@@ -1122,6 +1128,7 @@ static int advk_pcie_probe(struct platform_device *pdev)
pcie = pci_host_bridge_priv(bridge); pcie = pci_host_bridge_priv(bridge);
pcie->pdev = pdev; pcie->pdev = pdev;
platform_set_drvdata(pdev, pcie);
pcie->base = devm_platform_ioremap_resource(pdev, 0); pcie->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(pcie->base)) if (IS_ERR(pcie->base))
...@@ -1167,7 +1174,11 @@ static int advk_pcie_probe(struct platform_device *pdev) ...@@ -1167,7 +1174,11 @@ static int advk_pcie_probe(struct platform_device *pdev)
advk_pcie_setup_hw(pcie); 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); ret = advk_pcie_init_irq_domain(pcie);
if (ret) { if (ret) {
...@@ -1195,18 +1206,37 @@ static int advk_pcie_probe(struct platform_device *pdev) ...@@ -1195,18 +1206,37 @@ static int advk_pcie_probe(struct platform_device *pdev)
return 0; 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[] = { static const struct of_device_id advk_pcie_of_match_table[] = {
{ .compatible = "marvell,armada-3700-pcie", }, { .compatible = "marvell,armada-3700-pcie", },
{}, {},
}; };
MODULE_DEVICE_TABLE(of, advk_pcie_of_match_table);
static struct platform_driver advk_pcie_driver = { static struct platform_driver advk_pcie_driver = {
.driver = { .driver = {
.name = "advk-pcie", .name = "advk-pcie",
.of_match_table = advk_pcie_of_match_table, .of_match_table = advk_pcie_of_match_table,
/* Driver unloading/unbinding currently not supported */
.suppress_bind_attrs = true,
}, },
.probe = advk_pcie_probe, .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");
...@@ -294,6 +294,7 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge, ...@@ -294,6 +294,7 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge,
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(pci_bridge_emul_init);
/* /*
* Cleanup a pci_bridge_emul structure that was previously initialized * Cleanup a pci_bridge_emul structure that was previously initialized
...@@ -305,6 +306,7 @@ void pci_bridge_emul_cleanup(struct pci_bridge_emul *bridge) ...@@ -305,6 +306,7 @@ void pci_bridge_emul_cleanup(struct pci_bridge_emul *bridge)
kfree(bridge->pcie_cap_regs_behavior); kfree(bridge->pcie_cap_regs_behavior);
kfree(bridge->pci_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 * 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, ...@@ -366,6 +368,7 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where,
return PCIBIOS_SUCCESSFUL; return PCIBIOS_SUCCESSFUL;
} }
EXPORT_SYMBOL_GPL(pci_bridge_emul_conf_read);
/* /*
* Should be called by the PCI controller driver when writing the PCI * 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, ...@@ -430,3 +433,4 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,
return PCIBIOS_SUCCESSFUL; return PCIBIOS_SUCCESSFUL;
} }
EXPORT_SYMBOL_GPL(pci_bridge_emul_conf_write);
...@@ -26,7 +26,6 @@ ...@@ -26,7 +26,6 @@
#define COMPHY_SIP_POWER_ON 0x82000001 #define COMPHY_SIP_POWER_ON 0x82000001
#define COMPHY_SIP_POWER_OFF 0x82000002 #define COMPHY_SIP_POWER_OFF 0x82000002
#define COMPHY_SIP_PLL_LOCK 0x82000003 #define COMPHY_SIP_PLL_LOCK 0x82000003
#define COMPHY_FW_NOT_SUPPORTED (-1)
#define COMPHY_FW_MODE_SATA 0x1 #define COMPHY_FW_MODE_SATA 0x1
#define COMPHY_FW_MODE_SGMII 0x2 #define COMPHY_FW_MODE_SGMII 0x2
...@@ -112,10 +111,19 @@ static int mvebu_a3700_comphy_smc(unsigned long function, unsigned long lane, ...@@ -112,10 +111,19 @@ static int mvebu_a3700_comphy_smc(unsigned long function, unsigned long lane,
unsigned long mode) unsigned long mode)
{ {
struct arm_smccc_res res; struct arm_smccc_res res;
s32 ret;
arm_smccc_smc(function, lane, mode, 0, 0, 0, 0, 0, &res); arm_smccc_smc(function, lane, mode, 0, 0, 0, 0, 0, &res);
ret = res.a0;
return res.a0; switch (ret) {
case SMCCC_RET_SUCCESS:
return 0;
case SMCCC_RET_NOT_SUPPORTED:
return -EOPNOTSUPP;
default:
return -EINVAL;
}
} }
static int mvebu_a3700_comphy_get_fw_mode(int lane, int port, static int mvebu_a3700_comphy_get_fw_mode(int lane, int port,
...@@ -220,7 +228,7 @@ static int mvebu_a3700_comphy_power_on(struct phy *phy) ...@@ -220,7 +228,7 @@ static int mvebu_a3700_comphy_power_on(struct phy *phy)
} }
ret = mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_ON, lane->id, fw_param); ret = mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_ON, lane->id, fw_param);
if (ret == COMPHY_FW_NOT_SUPPORTED) if (ret == -EOPNOTSUPP)
dev_err(lane->dev, dev_err(lane->dev,
"unsupported SMC call, try updating your firmware\n"); "unsupported SMC call, try updating your firmware\n");
......
...@@ -123,7 +123,6 @@ ...@@ -123,7 +123,6 @@
#define COMPHY_SIP_POWER_ON 0x82000001 #define COMPHY_SIP_POWER_ON 0x82000001
#define COMPHY_SIP_POWER_OFF 0x82000002 #define COMPHY_SIP_POWER_OFF 0x82000002
#define COMPHY_FW_NOT_SUPPORTED (-1)
/* /*
* A lane is described by the following bitfields: * A lane is described by the following bitfields:
...@@ -273,10 +272,19 @@ static int mvebu_comphy_smc(unsigned long function, unsigned long phys, ...@@ -273,10 +272,19 @@ static int mvebu_comphy_smc(unsigned long function, unsigned long phys,
unsigned long lane, unsigned long mode) unsigned long lane, unsigned long mode)
{ {
struct arm_smccc_res res; struct arm_smccc_res res;
s32 ret;
arm_smccc_smc(function, phys, lane, mode, 0, 0, 0, 0, &res); arm_smccc_smc(function, phys, lane, mode, 0, 0, 0, 0, &res);
ret = res.a0;
return res.a0; switch (ret) {
case SMCCC_RET_SUCCESS:
return 0;
case SMCCC_RET_NOT_SUPPORTED:
return -EOPNOTSUPP;
default:
return -EINVAL;
}
} }
static int mvebu_comphy_get_mode(bool fw_mode, int lane, int port, static int mvebu_comphy_get_mode(bool fw_mode, int lane, int port,
...@@ -819,7 +827,7 @@ static int mvebu_comphy_power_on(struct phy *phy) ...@@ -819,7 +827,7 @@ static int mvebu_comphy_power_on(struct phy *phy)
if (!ret) if (!ret)
return ret; return ret;
if (ret == COMPHY_FW_NOT_SUPPORTED) if (ret == -EOPNOTSUPP)
dev_err(priv->dev, dev_err(priv->dev,
"unsupported SMC call, try updating your firmware\n"); "unsupported SMC call, try updating your firmware\n");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment