mirror of
https://github.com/torvalds/linux.git
synced 2026-05-25 23:52:08 +02:00
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
This commit is contained in:
commit
a9cd360245
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
mask = IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE;
|
||||
val = FIELD_PREP(IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE,
|
||||
PCI_EXP_TYPE_ROOT_PORT);
|
||||
} else {
|
||||
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,
|
||||
mode);
|
||||
} else {
|
||||
mask = IMX6Q_GPR12_DEVICE_TYPE;
|
||||
val = FIELD_PREP(IMX6Q_GPR12_DEVICE_TYPE, mode);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
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;
|
||||
}
|
||||
|
||||
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,15 +1418,22 @@ static int imx6_pcie_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dw_pcie_host_init(&pci->pp);
|
||||
if (ret < 0)
|
||||
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);
|
||||
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], },
|
||||
{},
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user