Commit d1640a83 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

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
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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
+69 −39
Original line number Diff line number Diff line
@@ -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");
+4 −0
Original line number Diff line number Diff line
@@ -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);
+11 −3
Original line number Diff line number Diff line
@@ -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");

+11 −3
Original line number Diff line number Diff line
@@ -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");