Commit 4dd4d996 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'remotes/lorenzo/pci/mediatek'

  - Fix Mediatek unchecked return value from devm_pci_remap_iospace()
    (Gustavo A. R. Silva)

  - Fix Mediatek endpoint/port matching logic (Honghui Zhang)

  - Change Mediatek Root Port Class Code to PCI_CLASS_BRIDGE_PCI (Honghui
    Zhang)

  - Remove redundant Mediatek PM domain check (Honghui Zhang)

  - Convert Mediatek to pci_host_probe() (Honghui Zhang)

  - Fix Mediatek MSI enablement (Honghui Zhang)

  - Add Mediatek system PM support for MT2712 and MT7622 (Honghui Zhang)

  - Add Mediatek loadable module support (Honghui Zhang)

* remotes/lorenzo/pci/mediatek:
  PCI: mediatek: Add loadable kernel module support
  PCI: mediatek: Add system PM support for MT2712 and MT7622
  PCI: mediatek: Fixup MSI enablement logic by enabling MSI before clocks
  PCI: mediatek: Convert to use pci_host_probe()
  PCI: mediatek: Remove the redundant dev->pm_domain check
  PCI: mediatek: Fix class type for MT7622 to PCI_CLASS_BRIDGE_PCI
  PCI: mediatek: Fix mtk_pcie_find_port() endpoint/port matching logic
  PCI: mediatek: Fix unchecked return value
parents fc23af0c 031337ac
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -233,7 +233,7 @@ config PCIE_ROCKCHIP_EP
	  available to support GEN2 with 4 slots.

config PCIE_MEDIATEK
	bool "MediaTek PCIe controller"
	tristate "MediaTek PCIe controller"
	depends on ARCH_MEDIATEK || COMPILE_TEST
	depends on OF
	depends on PCI_MSI_IRQ_DOMAIN
+205 −116
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/msi.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_pci.h>
#include <linux/of_platform.h>
@@ -162,6 +163,7 @@ struct mtk_pcie_soc {
 * @phy: pointer to PHY control block
 * @lane: lane count
 * @slot: port slot
 * @irq: GIC irq
 * @irq_domain: legacy INTx IRQ domain
 * @inner_domain: inner IRQ domain
 * @msi_domain: MSI IRQ domain
@@ -182,6 +184,7 @@ struct mtk_pcie_port {
	struct phy *phy;
	u32 lane;
	u32 slot;
	int irq;
	struct irq_domain *irq_domain;
	struct irq_domain *inner_domain;
	struct irq_domain *msi_domain;
@@ -225,11 +228,9 @@ static void mtk_pcie_subsys_powerdown(struct mtk_pcie *pcie)

	clk_disable_unprepare(pcie->free_ck);

	if (dev->pm_domain) {
	pm_runtime_put_sync(dev);
	pm_runtime_disable(dev);
}
}

static void mtk_pcie_port_free(struct mtk_pcie_port *port)
{
@@ -337,6 +338,17 @@ static struct mtk_pcie_port *mtk_pcie_find_port(struct pci_bus *bus,
{
	struct mtk_pcie *pcie = bus->sysdata;
	struct mtk_pcie_port *port;
	struct pci_dev *dev = NULL;

	/*
	 * Walk the bus hierarchy to get the devfn value
	 * of the port in the root bus.
	 */
	while (bus && bus->number) {
		dev = bus->self;
		bus = dev->bus;
		devfn = dev->devfn;
	}

	list_for_each_entry(port, &pcie->ports, list)
		if (port->slot == PCI_SLOT(devfn))
@@ -383,75 +395,6 @@ static struct pci_ops mtk_pcie_ops_v2 = {
	.write = mtk_pcie_config_write,
};

static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port)
{
	struct mtk_pcie *pcie = port->pcie;
	struct resource *mem = &pcie->mem;
	const struct mtk_pcie_soc *soc = port->pcie->soc;
	u32 val;
	size_t size;
	int err;

	/* MT7622 platforms need to enable LTSSM and ASPM from PCIe subsys */
	if (pcie->base) {
		val = readl(pcie->base + PCIE_SYS_CFG_V2);
		val |= PCIE_CSR_LTSSM_EN(port->slot) |
		       PCIE_CSR_ASPM_L1_EN(port->slot);
		writel(val, pcie->base + PCIE_SYS_CFG_V2);
	}

	/* Assert all reset signals */
	writel(0, port->base + PCIE_RST_CTRL);

	/*
	 * Enable PCIe link down reset, if link status changed from link up to
	 * link down, this will reset MAC control registers and configuration
	 * space.
	 */
	writel(PCIE_LINKDOWN_RST_EN, port->base + PCIE_RST_CTRL);

	/* De-assert PHY, PE, PIPE, MAC and configuration reset	*/
	val = readl(port->base + PCIE_RST_CTRL);
	val |= PCIE_PHY_RSTB | PCIE_PERSTB | PCIE_PIPE_SRSTB |
	       PCIE_MAC_SRSTB | PCIE_CRSTB;
	writel(val, port->base + PCIE_RST_CTRL);

	/* Set up vendor ID and class code */
	if (soc->need_fix_class_id) {
		val = PCI_VENDOR_ID_MEDIATEK;
		writew(val, port->base + PCIE_CONF_VEND_ID);

		val = PCI_CLASS_BRIDGE_HOST;
		writew(val, port->base + PCIE_CONF_CLASS_ID);
	}

	/* 100ms timeout value should be enough for Gen1/2 training */
	err = readl_poll_timeout(port->base + PCIE_LINK_STATUS_V2, val,
				 !!(val & PCIE_PORT_LINKUP_V2), 20,
				 100 * USEC_PER_MSEC);
	if (err)
		return -ETIMEDOUT;

	/* Set INTx mask */
	val = readl(port->base + PCIE_INT_MASK);
	val &= ~INTX_MASK;
	writel(val, port->base + PCIE_INT_MASK);

	/* Set AHB to PCIe translation windows */
	size = mem->end - mem->start;
	val = lower_32_bits(mem->start) | AHB2PCIE_SIZE(fls(size));
	writel(val, port->base + PCIE_AHB_TRANS_BASE0_L);

	val = upper_32_bits(mem->start);
	writel(val, port->base + PCIE_AHB_TRANS_BASE0_H);

	/* Set PCIe to AXI translation memory space.*/
	val = fls(0xffffffff) | WIN_ENABLE;
	writel(val, port->base + PCIE_AXI_WINDOW0);

	return 0;
}

static void mtk_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
{
	struct mtk_pcie_port *port = irq_data_get_irq_chip_data(data);
@@ -590,6 +533,27 @@ static void mtk_pcie_enable_msi(struct mtk_pcie_port *port)
	writel(val, port->base + PCIE_INT_MASK);
}

static void mtk_pcie_irq_teardown(struct mtk_pcie *pcie)
{
	struct mtk_pcie_port *port, *tmp;

	list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
		irq_set_chained_handler_and_data(port->irq, NULL, NULL);

		if (port->irq_domain)
			irq_domain_remove(port->irq_domain);

		if (IS_ENABLED(CONFIG_PCI_MSI)) {
			if (port->msi_domain)
				irq_domain_remove(port->msi_domain);
			if (port->inner_domain)
				irq_domain_remove(port->inner_domain);
		}

		irq_dispose_mapping(port->irq);
	}
}

static int mtk_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
			     irq_hw_number_t hwirq)
{
@@ -628,8 +592,6 @@ static int mtk_pcie_init_irq_domain(struct mtk_pcie_port *port,
		ret = mtk_pcie_allocate_msi_domains(port);
		if (ret)
			return ret;

		mtk_pcie_enable_msi(port);
	}

	return 0;
@@ -682,7 +644,7 @@ static int mtk_pcie_setup_irq(struct mtk_pcie_port *port,
	struct mtk_pcie *pcie = port->pcie;
	struct device *dev = pcie->dev;
	struct platform_device *pdev = to_platform_device(dev);
	int err, irq;
	int err;

	err = mtk_pcie_init_irq_domain(port, node);
	if (err) {
@@ -690,8 +652,81 @@ static int mtk_pcie_setup_irq(struct mtk_pcie_port *port,
		return err;
	}

	irq = platform_get_irq(pdev, port->slot);
	irq_set_chained_handler_and_data(irq, mtk_pcie_intr_handler, port);
	port->irq = platform_get_irq(pdev, port->slot);
	irq_set_chained_handler_and_data(port->irq,
					 mtk_pcie_intr_handler, port);

	return 0;
}

static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port)
{
	struct mtk_pcie *pcie = port->pcie;
	struct resource *mem = &pcie->mem;
	const struct mtk_pcie_soc *soc = port->pcie->soc;
	u32 val;
	size_t size;
	int err;

	/* MT7622 platforms need to enable LTSSM and ASPM from PCIe subsys */
	if (pcie->base) {
		val = readl(pcie->base + PCIE_SYS_CFG_V2);
		val |= PCIE_CSR_LTSSM_EN(port->slot) |
		       PCIE_CSR_ASPM_L1_EN(port->slot);
		writel(val, pcie->base + PCIE_SYS_CFG_V2);
	}

	/* Assert all reset signals */
	writel(0, port->base + PCIE_RST_CTRL);

	/*
	 * Enable PCIe link down reset, if link status changed from link up to
	 * link down, this will reset MAC control registers and configuration
	 * space.
	 */
	writel(PCIE_LINKDOWN_RST_EN, port->base + PCIE_RST_CTRL);

	/* De-assert PHY, PE, PIPE, MAC and configuration reset	*/
	val = readl(port->base + PCIE_RST_CTRL);
	val |= PCIE_PHY_RSTB | PCIE_PERSTB | PCIE_PIPE_SRSTB |
	       PCIE_MAC_SRSTB | PCIE_CRSTB;
	writel(val, port->base + PCIE_RST_CTRL);

	/* Set up vendor ID and class code */
	if (soc->need_fix_class_id) {
		val = PCI_VENDOR_ID_MEDIATEK;
		writew(val, port->base + PCIE_CONF_VEND_ID);

		val = PCI_CLASS_BRIDGE_PCI;
		writew(val, port->base + PCIE_CONF_CLASS_ID);
	}

	/* 100ms timeout value should be enough for Gen1/2 training */
	err = readl_poll_timeout(port->base + PCIE_LINK_STATUS_V2, val,
				 !!(val & PCIE_PORT_LINKUP_V2), 20,
				 100 * USEC_PER_MSEC);
	if (err)
		return -ETIMEDOUT;

	/* Set INTx mask */
	val = readl(port->base + PCIE_INT_MASK);
	val &= ~INTX_MASK;
	writel(val, port->base + PCIE_INT_MASK);

	if (IS_ENABLED(CONFIG_PCI_MSI))
		mtk_pcie_enable_msi(port);

	/* Set AHB to PCIe translation windows */
	size = mem->end - mem->start;
	val = lower_32_bits(mem->start) | AHB2PCIE_SIZE(fls(size));
	writel(val, port->base + PCIE_AHB_TRANS_BASE0_L);

	val = upper_32_bits(mem->start);
	writel(val, port->base + PCIE_AHB_TRANS_BASE0_H);

	/* Set PCIe to AXI translation memory space.*/
	val = fls(0xffffffff) | WIN_ENABLE;
	writel(val, port->base + PCIE_AXI_WINDOW0);

	return 0;
}
@@ -987,10 +1022,8 @@ static int mtk_pcie_subsys_powerup(struct mtk_pcie *pcie)
		pcie->free_ck = NULL;
	}

	if (dev->pm_domain) {
	pm_runtime_enable(dev);
	pm_runtime_get_sync(dev);
	}

	/* enable top level clock */
	err = clk_prepare_enable(pcie->free_ck);
@@ -1002,10 +1035,8 @@ static int mtk_pcie_subsys_powerup(struct mtk_pcie *pcie)
	return 0;

err_free_ck:
	if (dev->pm_domain) {
	pm_runtime_put_sync(dev);
	pm_runtime_disable(dev);
	}

	return err;
}
@@ -1109,36 +1140,10 @@ static int mtk_pcie_request_resources(struct mtk_pcie *pcie)
	if (err < 0)
		return err;

	devm_pci_remap_iospace(dev, &pcie->pio, pcie->io.start);

	return 0;
}

static int mtk_pcie_register_host(struct pci_host_bridge *host)
{
	struct mtk_pcie *pcie = pci_host_bridge_priv(host);
	struct pci_bus *child;
	int err;

	host->busnr = pcie->busn.start;
	host->dev.parent = pcie->dev;
	host->ops = pcie->soc->ops;
	host->map_irq = of_irq_parse_and_map_pci;
	host->swizzle_irq = pci_common_swizzle;
	host->sysdata = pcie;

	err = pci_scan_root_bus_bridge(host);
	if (err < 0)
	err = devm_pci_remap_iospace(dev, &pcie->pio, pcie->io.start);
	if (err)
		return err;

	pci_bus_size_bridges(host->bus);
	pci_bus_assign_resources(host->bus);

	list_for_each_entry(child, &host->bus->children, node)
		pcie_bus_configure_settings(child);

	pci_bus_add_devices(host->bus);

	return 0;
}

@@ -1168,7 +1173,14 @@ static int mtk_pcie_probe(struct platform_device *pdev)
	if (err)
		goto put_resources;

	err = mtk_pcie_register_host(host);
	host->busnr = pcie->busn.start;
	host->dev.parent = pcie->dev;
	host->ops = pcie->soc->ops;
	host->map_irq = of_irq_parse_and_map_pci;
	host->swizzle_irq = pci_common_swizzle;
	host->sysdata = pcie;

	err = pci_host_probe(host);
	if (err)
		goto put_resources;

@@ -1181,6 +1193,80 @@ put_resources:
	return err;
}


static void mtk_pcie_free_resources(struct mtk_pcie *pcie)
{
	struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie);
	struct list_head *windows = &host->windows;

	pci_free_resource_list(windows);
}

static int mtk_pcie_remove(struct platform_device *pdev)
{
	struct mtk_pcie *pcie = platform_get_drvdata(pdev);
	struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie);

	pci_stop_root_bus(host->bus);
	pci_remove_root_bus(host->bus);
	mtk_pcie_free_resources(pcie);

	mtk_pcie_irq_teardown(pcie);

	mtk_pcie_put_resources(pcie);

	return 0;
}

static int __maybe_unused mtk_pcie_suspend_noirq(struct device *dev)
{
	struct mtk_pcie *pcie = dev_get_drvdata(dev);
	struct mtk_pcie_port *port;

	if (list_empty(&pcie->ports))
		return 0;

	list_for_each_entry(port, &pcie->ports, list) {
		clk_disable_unprepare(port->pipe_ck);
		clk_disable_unprepare(port->obff_ck);
		clk_disable_unprepare(port->axi_ck);
		clk_disable_unprepare(port->aux_ck);
		clk_disable_unprepare(port->ahb_ck);
		clk_disable_unprepare(port->sys_ck);
		phy_power_off(port->phy);
		phy_exit(port->phy);
	}

	clk_disable_unprepare(pcie->free_ck);

	return 0;
}

static int __maybe_unused mtk_pcie_resume_noirq(struct device *dev)
{
	struct mtk_pcie *pcie = dev_get_drvdata(dev);
	struct mtk_pcie_port *port, *tmp;

	if (list_empty(&pcie->ports))
		return 0;

	clk_prepare_enable(pcie->free_ck);

	list_for_each_entry_safe(port, tmp, &pcie->ports, list)
		mtk_pcie_enable_port(port);

	/* In case of EP was removed while system suspend. */
	if (list_empty(&pcie->ports))
		clk_disable_unprepare(pcie->free_ck);

	return 0;
}

static const struct dev_pm_ops mtk_pcie_pm_ops = {
	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mtk_pcie_suspend_noirq,
				      mtk_pcie_resume_noirq)
};

static const struct mtk_pcie_soc mtk_pcie_soc_v1 = {
	.ops = &mtk_pcie_ops,
	.startup = mtk_pcie_startup_port,
@@ -1209,10 +1295,13 @@ static const struct of_device_id mtk_pcie_ids[] = {

static struct platform_driver mtk_pcie_driver = {
	.probe = mtk_pcie_probe,
	.remove = mtk_pcie_remove,
	.driver = {
		.name = "mtk-pcie",
		.of_match_table = mtk_pcie_ids,
		.suppress_bind_attrs = true,
		.pm = &mtk_pcie_pm_ops,
	},
};
builtin_platform_driver(mtk_pcie_driver);
module_platform_driver(mtk_pcie_driver);
MODULE_LICENSE("GPL v2");