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

Merge branch 'pci/controller/imx6'

- Add i.MX8MM, i.MX8MQ, i.MX8MP endpoint mode DT binding and driver support
  (Richard Zhu)

* pci/controller/imx6:
  PCI: imx6: Add i.MX8MP PCIe EP support
  PCI: imx6: Add i.MX8MM PCIe EP support
  PCI: imx6: Add i.MX8MQ PCIe EP support
  PCI: imx6: Add i.MX PCIe EP mode support
  misc: pci_endpoint_test: Add i.MX8 PCIe EP device support
  dt-bindings: imx6q-pcie: Add i.MX8MP PCIe EP mode compatible string
  dt-bindings: imx6q-pcie: Add i.MX8MQ PCIe EP mode compatible string
  dt-bindings: imx6q-pcie: Add i.MX8MM PCIe EP mode compatible string
parents 5256d493 c435669a
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -24,6 +24,9 @@ properties:
      - fsl,imx8mq-pcie
      - fsl,imx8mm-pcie
      - fsl,imx8mp-pcie
      - fsl,imx8mm-pcie-ep
      - fsl,imx8mq-pcie-ep
      - fsl,imx8mp-pcie-ep

  reg:
    items:
+2 −0
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@
#define PCI_DEVICE_ID_TI_J7200			0xb00f
#define PCI_DEVICE_ID_TI_AM64			0xb010
#define PCI_DEVICE_ID_LS1088A			0x80c0
#define PCI_DEVICE_ID_IMX8			0x0808

#define is_am654_pci_dev(pdev)		\
		((pdev)->device == PCI_DEVICE_ID_TI_AM654)
@@ -980,6 +981,7 @@ static const struct pci_device_id pci_endpoint_test_tbl[] = {
	{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, 0x81c0),
	  .driver_data = (kernel_ulong_t)&default_data,
	},
	{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_IMX8),},
	{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_LS1088A),
	  .driver_data = (kernel_ulong_t)&default_data,
	},
+22 −1
Original line number Diff line number Diff line
@@ -92,10 +92,31 @@ config PCI_EXYNOS
	  functions to implement the driver.

config PCI_IMX6
	bool "Freescale i.MX6/7/8 PCIe controller"
	bool

config PCI_IMX6_HOST
	bool "Freescale i.MX6/7/8 PCIe controller host mode"
	depends on ARCH_MXC || COMPILE_TEST
	depends on PCI_MSI
	select PCIE_DW_HOST
	select PCI_IMX6
	help
	  Enables support for the PCIe controller in the i.MX SoCs to
	  work in Root Complex mode. The PCI controller on i.MX is based
	  on DesignWare hardware and therefore the driver re-uses the
	  DesignWare core functions to implement the driver.

config PCI_IMX6_EP
	bool "Freescale i.MX6/7/8 PCIe controller endpoint mode"
	depends on ARCH_MXC || COMPILE_TEST
	depends on PCI_ENDPOINT
	select PCIE_DW_EP
	select PCI_IMX6
	help
	  Enables support for the PCIe controller in the i.MX SoCs to
	  work in endpoint mode. The PCI controller on i.MX is based
	  on DesignWare hardware and therefore the driver re-uses the
	  DesignWare core functions to implement the driver.

config PCIE_SPEAR13XX
	bool "STMicroelectronics SPEAr PCIe controller"
+182 −18
Original line number Diff line number Diff line
@@ -52,6 +52,9 @@ enum imx6_pcie_variants {
	IMX8MQ,
	IMX8MM,
	IMX8MP,
	IMX8MQ_EP,
	IMX8MM_EP,
	IMX8MP_EP,
};

#define IMX6_PCIE_FLAG_IMX6_PHY			BIT(0)
@@ -60,6 +63,7 @@ enum imx6_pcie_variants {

struct imx6_pcie_drvdata {
	enum imx6_pcie_variants variant;
	enum dw_pcie_device_mode mode;
	u32 flags;
	int dbi_length;
	const char *gpr;
@@ -152,24 +156,39 @@ struct imx6_pcie {
static unsigned int imx6_pcie_grp_offset(const struct imx6_pcie *imx6_pcie)
{
	WARN_ON(imx6_pcie->drvdata->variant != IMX8MQ &&
		imx6_pcie->drvdata->variant != IMX8MQ_EP &&
		imx6_pcie->drvdata->variant != IMX8MM &&
		imx6_pcie->drvdata->variant != IMX8MP);
		imx6_pcie->drvdata->variant != IMX8MM_EP &&
		imx6_pcie->drvdata->variant != IMX8MP &&
		imx6_pcie->drvdata->variant != IMX8MP_EP);
	return imx6_pcie->controller_id == 1 ? IOMUXC_GPR16 : IOMUXC_GPR14;
}

static void imx6_pcie_configure_type(struct imx6_pcie *imx6_pcie)
{
	unsigned int mask, val;
	unsigned int mask, val, mode;

	if (imx6_pcie->drvdata->variant == IMX8MQ &&
	    imx6_pcie->controller_id == 1) {
	if (imx6_pcie->drvdata->mode == DW_PCIE_EP_TYPE)
		mode = PCI_EXP_TYPE_ENDPOINT;
	else
		mode = PCI_EXP_TYPE_ROOT_PORT;

	switch (imx6_pcie->drvdata->variant) {
	case IMX8MQ:
	case IMX8MQ_EP:
		if (imx6_pcie->controller_id == 1) {
			mask = IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE;
			val  = FIELD_PREP(IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE,
				  PCI_EXP_TYPE_ROOT_PORT);
					  mode);
		} else {
			mask = IMX6Q_GPR12_DEVICE_TYPE;
		val  = FIELD_PREP(IMX6Q_GPR12_DEVICE_TYPE,
				  PCI_EXP_TYPE_ROOT_PORT);
			val  = FIELD_PREP(IMX6Q_GPR12_DEVICE_TYPE, mode);
		}
		break;
	default:
		mask = IMX6Q_GPR12_DEVICE_TYPE;
		val  = FIELD_PREP(IMX6Q_GPR12_DEVICE_TYPE, mode);
		break;
	}

	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, mask, val);
@@ -304,13 +323,16 @@ static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
{
	switch (imx6_pcie->drvdata->variant) {
	case IMX8MM:
	case IMX8MM_EP:
	case IMX8MP:
	case IMX8MP_EP:
		/*
		 * The PHY initialization had been done in the PHY
		 * driver, break here directly.
		 */
		break;
	case IMX8MQ:
	case IMX8MQ_EP:
		/*
		 * TODO: Currently this code assumes external
		 * oscillator is being used
@@ -561,8 +583,11 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
	case IMX7D:
		break;
	case IMX8MM:
	case IMX8MM_EP:
	case IMX8MQ:
	case IMX8MQ_EP:
	case IMX8MP:
	case IMX8MP_EP:
		ret = clk_prepare_enable(imx6_pcie->pcie_aux);
		if (ret) {
			dev_err(dev, "unable to enable pcie_aux clock\n");
@@ -606,8 +631,11 @@ static void imx6_pcie_disable_ref_clk(struct imx6_pcie *imx6_pcie)
				   IMX7D_GPR12_PCIE_PHY_REFCLK_SEL);
		break;
	case IMX8MM:
	case IMX8MM_EP:
	case IMX8MQ:
	case IMX8MQ_EP:
	case IMX8MP:
	case IMX8MP_EP:
		clk_disable_unprepare(imx6_pcie->pcie_aux);
		break;
	default:
@@ -672,10 +700,13 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
	switch (imx6_pcie->drvdata->variant) {
	case IMX7D:
	case IMX8MQ:
	case IMX8MQ_EP:
		reset_control_assert(imx6_pcie->pciephy_reset);
		fallthrough;
	case IMX8MM:
	case IMX8MM_EP:
	case IMX8MP:
	case IMX8MP_EP:
		reset_control_assert(imx6_pcie->apps_reset);
		break;
	case IMX6SX:
@@ -713,6 +744,7 @@ static int imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)

	switch (imx6_pcie->drvdata->variant) {
	case IMX8MQ:
	case IMX8MQ_EP:
		reset_control_deassert(imx6_pcie->pciephy_reset);
		break;
	case IMX7D:
@@ -751,7 +783,9 @@ static int imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
		break;
	case IMX6Q:		/* Nothing to do */
	case IMX8MM:
	case IMX8MM_EP:
	case IMX8MP:
	case IMX8MP_EP:
		break;
	}

@@ -800,8 +834,11 @@ static void imx6_pcie_ltssm_enable(struct device *dev)
		break;
	case IMX7D:
	case IMX8MQ:
	case IMX8MQ_EP:
	case IMX8MM:
	case IMX8MM_EP:
	case IMX8MP:
	case IMX8MP_EP:
		reset_control_deassert(imx6_pcie->apps_reset);
		break;
	}
@@ -820,8 +857,11 @@ static void imx6_pcie_ltssm_disable(struct device *dev)
		break;
	case IMX7D:
	case IMX8MQ:
	case IMX8MQ_EP:
	case IMX8MM:
	case IMX8MM_EP:
	case IMX8MP:
	case IMX8MP_EP:
		reset_control_assert(imx6_pcie->apps_reset);
		break;
	}
@@ -1003,8 +1043,104 @@ static const struct dw_pcie_host_ops imx6_pcie_host_ops = {

static const struct dw_pcie_ops dw_pcie_ops = {
	.start_link = imx6_pcie_start_link,
	.stop_link = imx6_pcie_stop_link,
};

static void imx6_pcie_ep_init(struct dw_pcie_ep *ep)
{
	enum pci_barno bar;
	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);

	for (bar = BAR_0; bar <= BAR_5; bar++)
		dw_pcie_ep_reset_bar(pci, bar);
}

static int imx6_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
				  enum pci_epc_irq_type type,
				  u16 interrupt_num)
{
	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);

	switch (type) {
	case PCI_EPC_IRQ_LEGACY:
		return dw_pcie_ep_raise_legacy_irq(ep, func_no);
	case PCI_EPC_IRQ_MSI:
		return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
	case PCI_EPC_IRQ_MSIX:
		return dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num);
	default:
		dev_err(pci->dev, "UNKNOWN IRQ type\n");
		return -EINVAL;
	}

	return 0;
}

static const struct pci_epc_features imx8m_pcie_epc_features = {
	.linkup_notifier = false,
	.msi_capable = true,
	.msix_capable = false,
	.reserved_bar = 1 << BAR_1 | 1 << BAR_3,
	.align = SZ_64K,
};

static const struct pci_epc_features*
imx6_pcie_ep_get_features(struct dw_pcie_ep *ep)
{
	return &imx8m_pcie_epc_features;
}

static const struct dw_pcie_ep_ops pcie_ep_ops = {
	.ep_init = imx6_pcie_ep_init,
	.raise_irq = imx6_pcie_ep_raise_irq,
	.get_features = imx6_pcie_ep_get_features,
};

static int imx6_add_pcie_ep(struct imx6_pcie *imx6_pcie,
			   struct platform_device *pdev)
{
	int ret;
	unsigned int pcie_dbi2_offset;
	struct dw_pcie_ep *ep;
	struct resource *res;
	struct dw_pcie *pci = imx6_pcie->pci;
	struct dw_pcie_rp *pp = &pci->pp;
	struct device *dev = pci->dev;

	imx6_pcie_host_init(pp);
	ep = &pci->ep;
	ep->ops = &pcie_ep_ops;

	switch (imx6_pcie->drvdata->variant) {
	case IMX8MQ_EP:
	case IMX8MM_EP:
	case IMX8MP_EP:
		pcie_dbi2_offset = SZ_1M;
		break;
	default:
		pcie_dbi2_offset = SZ_4K;
		break;
	}
	pci->dbi_base2 = pci->dbi_base + pcie_dbi2_offset;
	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space");
	if (!res)
		return -EINVAL;

	ep->phys_base = res->start;
	ep->addr_size = resource_size(res);
	ep->page_size = SZ_64K;

	ret = dw_pcie_ep_init(ep);
	if (ret) {
		dev_err(dev, "failed to initialize endpoint\n");
		return ret;
	}
	/* Start LTSSM. */
	imx6_pcie_ltssm_enable(dev);

	return 0;
}

static void imx6_pcie_pm_turnoff(struct imx6_pcie *imx6_pcie)
{
	struct device *dev = imx6_pcie->pci->dev;
@@ -1166,6 +1302,7 @@ static int imx6_pcie_probe(struct platform_device *pdev)
					     "pcie_inbound_axi clock missing or invalid\n");
		break;
	case IMX8MQ:
	case IMX8MQ_EP:
		imx6_pcie->pcie_aux = devm_clk_get(dev, "pcie_aux");
		if (IS_ERR(imx6_pcie->pcie_aux))
			return dev_err_probe(dev, PTR_ERR(imx6_pcie->pcie_aux),
@@ -1190,7 +1327,9 @@ static int imx6_pcie_probe(struct platform_device *pdev)
		}
		break;
	case IMX8MM:
	case IMX8MM_EP:
	case IMX8MP:
	case IMX8MP_EP:
		imx6_pcie->pcie_aux = devm_clk_get(dev, "pcie_aux");
		if (IS_ERR(imx6_pcie->pcie_aux))
			return dev_err_probe(dev, PTR_ERR(imx6_pcie->pcie_aux),
@@ -1279,16 +1418,23 @@ static int imx6_pcie_probe(struct platform_device *pdev)
	if (ret)
		return ret;

	if (imx6_pcie->drvdata->mode == DW_PCIE_EP_TYPE) {
		ret = imx6_add_pcie_ep(imx6_pcie, pdev);
		if (ret < 0)
			return ret;
	} else {
		ret = dw_pcie_host_init(&pci->pp);
		if (ret < 0)
			return ret;

		if (pci_msi_enabled()) {
			u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_MSI);

			val = dw_pcie_readw_dbi(pci, offset + PCI_MSI_FLAGS);
			val |= PCI_MSI_FLAGS_ENABLE;
			dw_pcie_writew_dbi(pci, offset + PCI_MSI_FLAGS, val);
		}
	}

	return 0;
}
@@ -1343,6 +1489,21 @@ static const struct imx6_pcie_drvdata drvdata[] = {
		.flags = IMX6_PCIE_FLAG_SUPPORTS_SUSPEND,
		.gpr = "fsl,imx8mp-iomuxc-gpr",
	},
	[IMX8MQ_EP] = {
		.variant = IMX8MQ_EP,
		.mode = DW_PCIE_EP_TYPE,
		.gpr = "fsl,imx8mq-iomuxc-gpr",
	},
	[IMX8MM_EP] = {
		.variant = IMX8MM_EP,
		.mode = DW_PCIE_EP_TYPE,
		.gpr = "fsl,imx8mm-iomuxc-gpr",
	},
	[IMX8MP_EP] = {
		.variant = IMX8MP_EP,
		.mode = DW_PCIE_EP_TYPE,
		.gpr = "fsl,imx8mp-iomuxc-gpr",
	},
};

static const struct of_device_id imx6_pcie_of_match[] = {
@@ -1353,6 +1514,9 @@ static const struct of_device_id imx6_pcie_of_match[] = {
	{ .compatible = "fsl,imx8mq-pcie", .data = &drvdata[IMX8MQ], },
	{ .compatible = "fsl,imx8mm-pcie", .data = &drvdata[IMX8MM], },
	{ .compatible = "fsl,imx8mp-pcie", .data = &drvdata[IMX8MP], },
	{ .compatible = "fsl,imx8mq-pcie-ep", .data = &drvdata[IMX8MQ_EP], },
	{ .compatible = "fsl,imx8mm-pcie-ep", .data = &drvdata[IMX8MM_EP], },
	{ .compatible = "fsl,imx8mp-pcie-ep", .data = &drvdata[IMX8MP_EP], },
	{},
};