mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-29 07:04:10 +08:00
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:
commit
d1640a8307
@ -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");
|
||||
|
@ -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);
|
||||
|
@ -26,7 +26,6 @@
|
||||
#define COMPHY_SIP_POWER_ON 0x82000001
|
||||
#define COMPHY_SIP_POWER_OFF 0x82000002
|
||||
#define COMPHY_SIP_PLL_LOCK 0x82000003
|
||||
#define COMPHY_FW_NOT_SUPPORTED (-1)
|
||||
|
||||
#define COMPHY_FW_MODE_SATA 0x1
|
||||
#define COMPHY_FW_MODE_SGMII 0x2
|
||||
@ -112,10 +111,19 @@ static int mvebu_a3700_comphy_smc(unsigned long function, unsigned long lane,
|
||||
unsigned long mode)
|
||||
{
|
||||
struct arm_smccc_res res;
|
||||
s32 ret;
|
||||
|
||||
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,
|
||||
@ -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);
|
||||
if (ret == COMPHY_FW_NOT_SUPPORTED)
|
||||
if (ret == -EOPNOTSUPP)
|
||||
dev_err(lane->dev,
|
||||
"unsupported SMC call, try updating your firmware\n");
|
||||
|
||||
|
@ -123,7 +123,6 @@
|
||||
|
||||
#define COMPHY_SIP_POWER_ON 0x82000001
|
||||
#define COMPHY_SIP_POWER_OFF 0x82000002
|
||||
#define COMPHY_FW_NOT_SUPPORTED (-1)
|
||||
|
||||
/*
|
||||
* A lane is described by the following bitfields:
|
||||
@ -273,10 +272,19 @@ static int mvebu_comphy_smc(unsigned long function, unsigned long phys,
|
||||
unsigned long lane, unsigned long mode)
|
||||
{
|
||||
struct arm_smccc_res res;
|
||||
s32 ret;
|
||||
|
||||
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,
|
||||
@ -819,7 +827,7 @@ static int mvebu_comphy_power_on(struct phy *phy)
|
||||
if (!ret)
|
||||
return ret;
|
||||
|
||||
if (ret == COMPHY_FW_NOT_SUPPORTED)
|
||||
if (ret == -EOPNOTSUPP)
|
||||
dev_err(priv->dev,
|
||||
"unsupported SMC call, try updating your firmware\n");
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user