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

Merge branch 'pci/controller/mediatek-gen3'

- Use dev_err_probe() to simplify error paths and make deferred probe
  messages visible in /sys/kernel/debug/devices_deferred (Chen-Yu Tsai)

- Initialize IRQ domains earlier to remove need for cleanup if it fails
  (Chen-Yu Tsai)

- Set up controller windows and MSI before bringing the link up to separate
  controller init and things related to downstream devices (Chen-Yu Tsai)

- Split out device power up and down helpers (Chen-Yu Tsai)

- Power off device if setup fails (Chen-Yu Tsai)

- Integrate new pwrctrl API to enable power control for WiFi/BT adapters on
  mainboard or in PCIe or M.2 slots (Chen-Yu Tsai)

- Prevent leaking IRQ domains when IRQ not found (Chen-Yu Tsai)

* pci/controller/mediatek-gen3:
  PCI: mediatek-gen3: Prevent leaking IRQ domains when IRQ not found
  PCI: mediatek-gen3: Integrate new pwrctrl API
  PCI: mediatek-gen3: Disable device if further setup fails
  PCI: mediatek-gen3: Split out device power helpers
  PCI: mediatek-gen3: Add error path for resume driver callbacks
  PCI: mediatek-gen3: Move controller setup steps before PERST# control
  PCI: mediatek-gen3: Move mtk_pcie_setup_irq() out of mtk_pcie_setup()
  PCI: mediatek-gen3: Clean up mtk_pcie_parse_port() with dev_err_probe()
parents 8a1c7eff 5573c44c
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -222,6 +222,7 @@ config PCIE_MEDIATEK_GEN3
	depends on ARCH_AIROHA || ARCH_MEDIATEK || COMPILE_TEST
	depends on PCI_MSI
	select IRQ_MSI_LIB
	select PCI_PWRCTRL_GENERIC
	help
	  Adds support for PCIe Gen3 MAC controller for MediaTek SoCs.
	  This PCIe controller is compatible with Gen3, Gen2 and Gen1 speed,
+132 −99
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include <linux/of_device.h>
#include <linux/of_pci.h>
#include <linux/pci.h>
#include <linux/pci-pwrctrl.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
@@ -403,6 +404,64 @@ static void mtk_pcie_enable_msi(struct mtk_gen3_pcie *pcie)
	writel_relaxed(val, pcie->base + PCIE_INT_ENABLE_REG);
}

static int mtk_pcie_devices_power_up(struct mtk_gen3_pcie *pcie)
{
	int err;
	u32 val;

	/*
	 * Airoha EN7581 has a hw bug asserting/releasing PCIE_PE_RSTB signal
	 * causing occasional PCIe link down. In order to overcome the issue,
	 * PCIE_RSTB signals are not asserted/released at this stage and the
	 * PCIe block is reset using en7523_reset_assert() and
	 * en7581_pci_enable().
	 */
	if (!(pcie->soc->flags & SKIP_PCIE_RSTB)) {
		/* Assert all reset signals */
		val = readl_relaxed(pcie->base + PCIE_RST_CTRL_REG);
		val |= PCIE_MAC_RSTB | PCIE_PHY_RSTB | PCIE_BRG_RSTB |
		       PCIE_PE_RSTB;
		writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG);
	}

	err = pci_pwrctrl_power_on_devices(pcie->dev);
	if (err) {
		dev_err(pcie->dev, "Failed to power on devices: %pe\n", ERR_PTR(err));
		return err;
	}

	/*
	 * Described in PCIe CEM specification revision 6.0.
	 *
	 * The deassertion of PERST# should be delayed 100ms (TPVPERL)
	 * for the power and clock to become stable.
	 */
	msleep(PCIE_T_PVPERL_MS);

	if (!(pcie->soc->flags & SKIP_PCIE_RSTB)) {
		/* De-assert reset signals */
		val &= ~(PCIE_MAC_RSTB | PCIE_PHY_RSTB | PCIE_BRG_RSTB |
			 PCIE_PE_RSTB);
		writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG);
	}

	return 0;
}

static void mtk_pcie_devices_power_down(struct mtk_gen3_pcie *pcie)
{
	u32 val;

	if (!(pcie->soc->flags & SKIP_PCIE_RSTB)) {
		/* Assert the PERST# pin */
		val = readl_relaxed(pcie->base + PCIE_RST_CTRL_REG);
		val |= PCIE_PE_RSTB;
		writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG);
	}

	pci_pwrctrl_power_off_devices(pcie->dev);
}

static int mtk_pcie_startup_port(struct mtk_gen3_pcie *pcie)
{
	struct resource_entry *entry;
@@ -464,52 +523,6 @@ static int mtk_pcie_startup_port(struct mtk_gen3_pcie *pcie)
	val |= PCIE_DISABLE_DVFSRC_VLT_REQ;
	writel_relaxed(val, pcie->base + PCIE_MISC_CTRL_REG);

	/*
	 * Airoha EN7581 has a hw bug asserting/releasing PCIE_PE_RSTB signal
	 * causing occasional PCIe link down. In order to overcome the issue,
	 * PCIE_RSTB signals are not asserted/released at this stage and the
	 * PCIe block is reset using en7523_reset_assert() and
	 * en7581_pci_enable().
	 */
	if (!(pcie->soc->flags & SKIP_PCIE_RSTB)) {
		/* Assert all reset signals */
		val = readl_relaxed(pcie->base + PCIE_RST_CTRL_REG);
		val |= PCIE_MAC_RSTB | PCIE_PHY_RSTB | PCIE_BRG_RSTB |
		       PCIE_PE_RSTB;
		writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG);

		/*
		 * Described in PCIe CEM specification revision 6.0.
		 *
		 * The deassertion of PERST# should be delayed 100ms (TPVPERL)
		 * for the power and clock to become stable.
		 */
		msleep(PCIE_T_PVPERL_MS);

		/* De-assert reset signals */
		val &= ~(PCIE_MAC_RSTB | PCIE_PHY_RSTB | PCIE_BRG_RSTB |
			 PCIE_PE_RSTB);
		writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG);
	}

	/* Check if the link is up or not */
	err = readl_poll_timeout(pcie->base + PCIE_LINK_STATUS_REG, val,
				 !!(val & PCIE_PORT_LINKUP), 20,
				 PCI_PM_D3COLD_WAIT * USEC_PER_MSEC);
	if (err) {
		const char *ltssm_state;
		int ltssm_index;

		val = readl_relaxed(pcie->base + PCIE_LTSSM_STATUS_REG);
		ltssm_index = PCIE_LTSSM_STATE(val);
		ltssm_state = ltssm_index >= ARRAY_SIZE(ltssm_str) ?
			      "Unknown state" : ltssm_str[ltssm_index];
		dev_err(pcie->dev,
			"PCIe link down, current LTSSM state: %s (%#x)\n",
			ltssm_state, val);
		return err;
	}

	mtk_pcie_enable_msi(pcie);

	/* Set PCIe translation windows */
@@ -535,7 +548,33 @@ static int mtk_pcie_startup_port(struct mtk_gen3_pcie *pcie)
			return err;
	}

	err = mtk_pcie_devices_power_up(pcie);
	if (err)
		return err;

	/* Check if the link is up or not */
	err = readl_poll_timeout(pcie->base + PCIE_LINK_STATUS_REG, val,
				 !!(val & PCIE_PORT_LINKUP), 20,
				 PCI_PM_D3COLD_WAIT * USEC_PER_MSEC);
	if (err) {
		const char *ltssm_state;
		int ltssm_index;

		val = readl_relaxed(pcie->base + PCIE_LTSSM_STATUS_REG);
		ltssm_index = PCIE_LTSSM_STATE(val);
		ltssm_state = ltssm_index >= ARRAY_SIZE(ltssm_str) ?
			      "Unknown state" : ltssm_str[ltssm_index];
		dev_err(pcie->dev,
			"PCIe link down, current LTSSM state: %s (%#x)\n",
			ltssm_state, val);
		goto err_power_down_device;
	}

	return 0;

err_power_down_device:
	mtk_pcie_devices_power_down(pcie);
	return err;
}

#define MTK_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS	| \
@@ -851,14 +890,14 @@ static int mtk_pcie_setup_irq(struct mtk_gen3_pcie *pcie)
	struct platform_device *pdev = to_platform_device(dev);
	int err;

	err = mtk_pcie_init_irq_domains(pcie);
	if (err)
		return err;

	pcie->irq = platform_get_irq(pdev, 0);
	if (pcie->irq < 0)
		return pcie->irq;

	err = mtk_pcie_init_irq_domains(pcie);
	if (err)
		return err;

	irq_set_chained_handler_and_data(pcie->irq, mtk_pcie_irq_handler, pcie);

	return 0;
@@ -876,10 +915,8 @@ static int mtk_pcie_parse_port(struct mtk_gen3_pcie *pcie)
	if (!regs)
		return -EINVAL;
	pcie->base = devm_ioremap_resource(dev, regs);
	if (IS_ERR(pcie->base)) {
		dev_err(dev, "failed to map register base\n");
		return PTR_ERR(pcie->base);
	}
	if (IS_ERR(pcie->base))
		return dev_err_probe(dev, PTR_ERR(pcie->base), "failed to map register base\n");

	pcie->reg_base = regs->start;

@@ -888,34 +925,20 @@ static int mtk_pcie_parse_port(struct mtk_gen3_pcie *pcie)

	ret = devm_reset_control_bulk_get_optional_shared(dev, num_resets,
							  pcie->phy_resets);
	if (ret) {
		dev_err(dev, "failed to get PHY bulk reset\n");
		return ret;
	}
	if (ret)
		return dev_err_probe(dev, ret, "failed to get PHY bulk reset\n");

	pcie->mac_reset = devm_reset_control_get_optional_exclusive(dev, "mac");
	if (IS_ERR(pcie->mac_reset)) {
		ret = PTR_ERR(pcie->mac_reset);
		if (ret != -EPROBE_DEFER)
			dev_err(dev, "failed to get MAC reset\n");

		return ret;
	}
	if (IS_ERR(pcie->mac_reset))
		return dev_err_probe(dev, PTR_ERR(pcie->mac_reset), "failed to get MAC reset\n");

	pcie->phy = devm_phy_optional_get(dev, "pcie-phy");
	if (IS_ERR(pcie->phy)) {
		ret = PTR_ERR(pcie->phy);
		if (ret != -EPROBE_DEFER)
			dev_err(dev, "failed to get PHY\n");

		return ret;
	}
	if (IS_ERR(pcie->phy))
		return dev_err_probe(dev, PTR_ERR(pcie->phy), "failed to get PHY\n");

	pcie->num_clks = devm_clk_bulk_get_all(dev, &pcie->clks);
	if (pcie->num_clks < 0) {
		dev_err(dev, "failed to get clocks\n");
		return pcie->num_clks;
	}
	if (pcie->num_clks < 0)
		return dev_err_probe(dev, pcie->num_clks, "failed to get clocks\n");

	ret = of_property_read_u32(dev->of_node, "num-lanes", &num_lanes);
	if (ret == 0) {
@@ -1168,10 +1191,6 @@ static int mtk_pcie_setup(struct mtk_gen3_pcie *pcie)
	if (err)
		goto err_setup;

	err = mtk_pcie_setup_irq(pcie);
	if (err)
		goto err_setup;

	return 0;

err_setup:
@@ -1197,21 +1216,38 @@ static int mtk_pcie_probe(struct platform_device *pdev)
	pcie->soc = device_get_match_data(dev);
	platform_set_drvdata(pdev, pcie);

	err = mtk_pcie_setup_irq(pcie);
	if (err)
		return dev_err_probe(dev, err, "Failed to setup IRQ domains\n");

	err = pci_pwrctrl_create_devices(pcie->dev);
	if (err) {
		goto err_tear_down_irq;
		dev_err_probe(dev, err, "failed to create pwrctrl devices\n");
	}

	err = mtk_pcie_setup(pcie);
	if (err)
		return err;
		goto err_destroy_pwrctrl;

	host->ops = &mtk_pcie_ops;
	host->sysdata = pcie;

	err = pci_host_probe(host);
	if (err) {
		mtk_pcie_irq_teardown(pcie);
		mtk_pcie_power_down(pcie);
		return err;
	}
	if (err)
		goto err_power_down_pcie;

	return 0;

err_power_down_pcie:
	mtk_pcie_devices_power_down(pcie);
	mtk_pcie_power_down(pcie);
err_destroy_pwrctrl:
	if (err != -EPROBE_DEFER)
		pci_pwrctrl_destroy_devices(pcie->dev);
err_tear_down_irq:
	mtk_pcie_irq_teardown(pcie);
	return err;
}

static void mtk_pcie_remove(struct platform_device *pdev)
@@ -1224,8 +1260,10 @@ static void mtk_pcie_remove(struct platform_device *pdev)
	pci_remove_root_bus(host->bus);
	pci_unlock_rescan_remove();

	mtk_pcie_irq_teardown(pcie);
	pci_pwrctrl_power_off_devices(pcie->dev);
	mtk_pcie_power_down(pcie);
	pci_pwrctrl_destroy_devices(pcie->dev);
	mtk_pcie_irq_teardown(pcie);
}

static void mtk_pcie_irq_save(struct mtk_gen3_pcie *pcie)
@@ -1283,7 +1321,6 @@ static int mtk_pcie_suspend_noirq(struct device *dev)
{
	struct mtk_gen3_pcie *pcie = dev_get_drvdata(dev);
	int err;
	u32 val;

	/* Trigger link to L2 state */
	err = mtk_pcie_turn_off_link(pcie);
@@ -1292,13 +1329,7 @@ static int mtk_pcie_suspend_noirq(struct device *dev)
		return err;
	}

	if (!(pcie->soc->flags & SKIP_PCIE_RSTB)) {
		/* Assert the PERST# pin */
		val = readl_relaxed(pcie->base + PCIE_RST_CTRL_REG);
		val |= PCIE_PE_RSTB;
		writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG);
	}

	mtk_pcie_devices_power_down(pcie);
	dev_dbg(pcie->dev, "entered L2 states successfully");

	mtk_pcie_irq_save(pcie);
@@ -1317,14 +1348,16 @@ static int mtk_pcie_resume_noirq(struct device *dev)
		return err;

	err = mtk_pcie_startup_port(pcie);
	if (err) {
		mtk_pcie_power_down(pcie);
		return err;
	}
	if (err)
		goto err_power_down;

	mtk_pcie_irq_restore(pcie);

	return 0;

err_power_down:
	mtk_pcie_power_down(pcie);
	return err;
}

static const struct dev_pm_ops mtk_pcie_pm_ops = {