mirror of
https://gitee.com/bianbu-linux/linux-6.6
synced 2025-07-01 23:53:16 -04:00
PCI: imx6: Add support for i.MX8MQ
Add code needed to support i.MX8MQ variant. Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com> Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Reviewed-by: Lucas Stach <l.stach@pengutronix.de> Cc: Bjorn Helgaas <bhelgaas@google.com> Cc: Fabio Estevam <fabio.estevam@nxp.com> Cc: Chris Healy <cphealy@gmail.com> Cc: Lucas Stach <l.stach@pengutronix.de> Cc: Leonard Crestez <leonard.crestez@nxp.com> Cc: "A.s. Dong" <aisheng.dong@nxp.com> Cc: Richard Zhu <hongxing.zhu@nxp.com>
This commit is contained in:
parent
4c458bb347
commit
2d8ed461db
3 changed files with 79 additions and 5 deletions
|
@ -9,6 +9,7 @@ Required properties:
|
||||||
- "fsl,imx6sx-pcie",
|
- "fsl,imx6sx-pcie",
|
||||||
- "fsl,imx6qp-pcie"
|
- "fsl,imx6qp-pcie"
|
||||||
- "fsl,imx7d-pcie"
|
- "fsl,imx7d-pcie"
|
||||||
|
- "fsl,imx8mq-pcie"
|
||||||
- reg: base address and length of the PCIe controller
|
- reg: base address and length of the PCIe controller
|
||||||
- interrupts: A list of interrupt outputs of the controller. Must contain an
|
- interrupts: A list of interrupt outputs of the controller. Must contain an
|
||||||
entry for each entry in the interrupt-names property.
|
entry for each entry in the interrupt-names property.
|
||||||
|
@ -45,7 +46,7 @@ Additional required properties for imx6sx-pcie:
|
||||||
PCIE_PHY power domains
|
PCIE_PHY power domains
|
||||||
- power-domain-names: Must be "pcie", "pcie_phy"
|
- power-domain-names: Must be "pcie", "pcie_phy"
|
||||||
|
|
||||||
Additional required properties for imx7d-pcie:
|
Additional required properties for imx7d-pcie and imx8mq-pcie:
|
||||||
- power-domains: Must be set to a phandle pointing to PCIE_PHY power domain
|
- power-domains: Must be set to a phandle pointing to PCIE_PHY power domain
|
||||||
- resets: Must contain phandles to PCIe-related reset lines exposed by SRC
|
- resets: Must contain phandles to PCIe-related reset lines exposed by SRC
|
||||||
IP block
|
IP block
|
||||||
|
|
|
@ -89,8 +89,8 @@ config PCI_EXYNOS
|
||||||
select PCIE_DW_HOST
|
select PCIE_DW_HOST
|
||||||
|
|
||||||
config PCI_IMX6
|
config PCI_IMX6
|
||||||
bool "Freescale i.MX6/7 PCIe controller"
|
bool "Freescale i.MX6/7/8 PCIe controller"
|
||||||
depends on SOC_IMX6Q || SOC_IMX7D || (ARM && COMPILE_TEST)
|
depends on SOC_IMX6Q || SOC_IMX7D || (ARM64 && ARCH_MXC) || COMPILE_TEST
|
||||||
depends on PCI_MSI_IRQ_DOMAIN
|
depends on PCI_MSI_IRQ_DOMAIN
|
||||||
select PCIE_DW_HOST
|
select PCIE_DW_HOST
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
* Author: Sean Cross <xobs@kosagi.com>
|
* Author: Sean Cross <xobs@kosagi.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/bitfield.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
|
@ -32,6 +33,12 @@
|
||||||
|
|
||||||
#include "pcie-designware.h"
|
#include "pcie-designware.h"
|
||||||
|
|
||||||
|
#define IMX8MQ_GPR_PCIE_REF_USE_PAD BIT(9)
|
||||||
|
#define IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN BIT(10)
|
||||||
|
#define IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE BIT(11)
|
||||||
|
#define IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE GENMASK(11, 8)
|
||||||
|
#define IMX8MQ_PCIE2_BASE_ADDR 0x33c00000
|
||||||
|
|
||||||
#define to_imx6_pcie(x) dev_get_drvdata((x)->dev)
|
#define to_imx6_pcie(x) dev_get_drvdata((x)->dev)
|
||||||
|
|
||||||
enum imx6_pcie_variants {
|
enum imx6_pcie_variants {
|
||||||
|
@ -39,6 +46,7 @@ enum imx6_pcie_variants {
|
||||||
IMX6SX,
|
IMX6SX,
|
||||||
IMX6QP,
|
IMX6QP,
|
||||||
IMX7D,
|
IMX7D,
|
||||||
|
IMX8MQ,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define IMX6_PCIE_FLAG_IMX6_PHY BIT(0)
|
#define IMX6_PCIE_FLAG_IMX6_PHY BIT(0)
|
||||||
|
@ -58,6 +66,7 @@ struct imx6_pcie {
|
||||||
struct clk *pcie_inbound_axi;
|
struct clk *pcie_inbound_axi;
|
||||||
struct clk *pcie;
|
struct clk *pcie;
|
||||||
struct regmap *iomuxc_gpr;
|
struct regmap *iomuxc_gpr;
|
||||||
|
u32 controller_id;
|
||||||
struct reset_control *pciephy_reset;
|
struct reset_control *pciephy_reset;
|
||||||
struct reset_control *apps_reset;
|
struct reset_control *apps_reset;
|
||||||
struct reset_control *turnoff_reset;
|
struct reset_control *turnoff_reset;
|
||||||
|
@ -275,6 +284,7 @@ static void imx6_pcie_reset_phy(struct imx6_pcie *imx6_pcie)
|
||||||
pcie_phy_write(imx6_pcie, PHY_RX_OVRD_IN_LO, tmp);
|
pcie_phy_write(imx6_pcie, PHY_RX_OVRD_IN_LO, tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_ARM
|
||||||
/* Added for PCI abort handling */
|
/* Added for PCI abort handling */
|
||||||
static int imx6q_pcie_abort_handler(unsigned long addr,
|
static int imx6q_pcie_abort_handler(unsigned long addr,
|
||||||
unsigned int fsr, struct pt_regs *regs)
|
unsigned int fsr, struct pt_regs *regs)
|
||||||
|
@ -308,6 +318,7 @@ static int imx6q_pcie_abort_handler(unsigned long addr,
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int imx6_pcie_attach_pd(struct device *dev)
|
static int imx6_pcie_attach_pd(struct device *dev)
|
||||||
{
|
{
|
||||||
|
@ -352,6 +363,7 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
|
||||||
|
|
||||||
switch (imx6_pcie->drvdata->variant) {
|
switch (imx6_pcie->drvdata->variant) {
|
||||||
case IMX7D:
|
case IMX7D:
|
||||||
|
case IMX8MQ:
|
||||||
reset_control_assert(imx6_pcie->pciephy_reset);
|
reset_control_assert(imx6_pcie->pciephy_reset);
|
||||||
reset_control_assert(imx6_pcie->apps_reset);
|
reset_control_assert(imx6_pcie->apps_reset);
|
||||||
break;
|
break;
|
||||||
|
@ -386,10 +398,17 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned int imx6_pcie_grp_offset(const struct imx6_pcie *imx6_pcie)
|
||||||
|
{
|
||||||
|
WARN_ON(imx6_pcie->drvdata->variant != IMX8MQ);
|
||||||
|
return imx6_pcie->controller_id == 1 ? IOMUXC_GPR16 : IOMUXC_GPR14;
|
||||||
|
}
|
||||||
|
|
||||||
static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
|
static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
|
||||||
{
|
{
|
||||||
struct dw_pcie *pci = imx6_pcie->pci;
|
struct dw_pcie *pci = imx6_pcie->pci;
|
||||||
struct device *dev = pci->dev;
|
struct device *dev = pci->dev;
|
||||||
|
unsigned int offset;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
switch (imx6_pcie->drvdata->variant) {
|
switch (imx6_pcie->drvdata->variant) {
|
||||||
|
@ -420,6 +439,19 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
|
||||||
break;
|
break;
|
||||||
case IMX7D:
|
case IMX7D:
|
||||||
break;
|
break;
|
||||||
|
case IMX8MQ:
|
||||||
|
offset = imx6_pcie_grp_offset(imx6_pcie);
|
||||||
|
/*
|
||||||
|
* Set the over ride low and enabled
|
||||||
|
* make sure that REF_CLK is turned on.
|
||||||
|
*/
|
||||||
|
regmap_update_bits(imx6_pcie->iomuxc_gpr, offset,
|
||||||
|
IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE,
|
||||||
|
0);
|
||||||
|
regmap_update_bits(imx6_pcie->iomuxc_gpr, offset,
|
||||||
|
IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN,
|
||||||
|
IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -496,6 +528,9 @@ static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (imx6_pcie->drvdata->variant) {
|
switch (imx6_pcie->drvdata->variant) {
|
||||||
|
case IMX8MQ:
|
||||||
|
reset_control_deassert(imx6_pcie->pciephy_reset);
|
||||||
|
break;
|
||||||
case IMX7D:
|
case IMX7D:
|
||||||
reset_control_deassert(imx6_pcie->pciephy_reset);
|
reset_control_deassert(imx6_pcie->pciephy_reset);
|
||||||
imx7d_pcie_wait_for_phy_pll_lock(imx6_pcie);
|
imx7d_pcie_wait_for_phy_pll_lock(imx6_pcie);
|
||||||
|
@ -531,9 +566,37 @@ err_pcie_phy:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void imx6_pcie_configure_type(struct imx6_pcie *imx6_pcie)
|
||||||
|
{
|
||||||
|
unsigned int mask, val;
|
||||||
|
|
||||||
|
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 {
|
||||||
|
mask = IMX6Q_GPR12_DEVICE_TYPE;
|
||||||
|
val = FIELD_PREP(IMX6Q_GPR12_DEVICE_TYPE,
|
||||||
|
PCI_EXP_TYPE_ROOT_PORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, mask, val);
|
||||||
|
}
|
||||||
|
|
||||||
static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
|
static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
|
||||||
{
|
{
|
||||||
switch (imx6_pcie->drvdata->variant) {
|
switch (imx6_pcie->drvdata->variant) {
|
||||||
|
case IMX8MQ:
|
||||||
|
/*
|
||||||
|
* TODO: Currently this code assumes external
|
||||||
|
* oscillator is being used
|
||||||
|
*/
|
||||||
|
regmap_update_bits(imx6_pcie->iomuxc_gpr,
|
||||||
|
imx6_pcie_grp_offset(imx6_pcie),
|
||||||
|
IMX8MQ_GPR_PCIE_REF_USE_PAD,
|
||||||
|
IMX8MQ_GPR_PCIE_REF_USE_PAD);
|
||||||
|
break;
|
||||||
case IMX7D:
|
case IMX7D:
|
||||||
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
|
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
|
||||||
IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, 0);
|
IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, 0);
|
||||||
|
@ -569,8 +632,7 @@ static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
|
imx6_pcie_configure_type(imx6_pcie);
|
||||||
IMX6Q_GPR12_DEVICE_TYPE, PCI_EXP_TYPE_ROOT_PORT << 12);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int imx6_setup_phy_mpll(struct imx6_pcie *imx6_pcie)
|
static int imx6_setup_phy_mpll(struct imx6_pcie *imx6_pcie)
|
||||||
|
@ -667,6 +729,7 @@ static void imx6_pcie_ltssm_enable(struct device *dev)
|
||||||
IMX6Q_GPR12_PCIE_CTL_2);
|
IMX6Q_GPR12_PCIE_CTL_2);
|
||||||
break;
|
break;
|
||||||
case IMX7D:
|
case IMX7D:
|
||||||
|
case IMX8MQ:
|
||||||
reset_control_deassert(imx6_pcie->apps_reset);
|
reset_control_deassert(imx6_pcie->apps_reset);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1002,6 +1065,10 @@ static int imx6_pcie_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case IMX7D:
|
case IMX7D:
|
||||||
|
case IMX8MQ:
|
||||||
|
if (dbi_base->start == IMX8MQ_PCIE2_BASE_ADDR)
|
||||||
|
imx6_pcie->controller_id = 1;
|
||||||
|
|
||||||
imx6_pcie->pciephy_reset = devm_reset_control_get_exclusive(dev,
|
imx6_pcie->pciephy_reset = devm_reset_control_get_exclusive(dev,
|
||||||
"pciephy");
|
"pciephy");
|
||||||
if (IS_ERR(imx6_pcie->pciephy_reset)) {
|
if (IS_ERR(imx6_pcie->pciephy_reset)) {
|
||||||
|
@ -1117,6 +1184,9 @@ static const struct imx6_pcie_drvdata drvdata[] = {
|
||||||
[IMX7D] = {
|
[IMX7D] = {
|
||||||
.variant = IMX7D,
|
.variant = IMX7D,
|
||||||
},
|
},
|
||||||
|
[IMX8MQ] = {
|
||||||
|
.variant = IMX8MQ,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id imx6_pcie_of_match[] = {
|
static const struct of_device_id imx6_pcie_of_match[] = {
|
||||||
|
@ -1124,6 +1194,7 @@ static const struct of_device_id imx6_pcie_of_match[] = {
|
||||||
{ .compatible = "fsl,imx6sx-pcie", .data = &drvdata[IMX6SX], },
|
{ .compatible = "fsl,imx6sx-pcie", .data = &drvdata[IMX6SX], },
|
||||||
{ .compatible = "fsl,imx6qp-pcie", .data = &drvdata[IMX6QP], },
|
{ .compatible = "fsl,imx6qp-pcie", .data = &drvdata[IMX6QP], },
|
||||||
{ .compatible = "fsl,imx7d-pcie", .data = &drvdata[IMX7D], },
|
{ .compatible = "fsl,imx7d-pcie", .data = &drvdata[IMX7D], },
|
||||||
|
{ .compatible = "fsl,imx8mq-pcie", .data = &drvdata[IMX8MQ], } ,
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1140,6 +1211,7 @@ static struct platform_driver imx6_pcie_driver = {
|
||||||
|
|
||||||
static int __init imx6_pcie_init(void)
|
static int __init imx6_pcie_init(void)
|
||||||
{
|
{
|
||||||
|
#ifdef CONFIG_ARM
|
||||||
/*
|
/*
|
||||||
* Since probe() can be deferred we need to make sure that
|
* Since probe() can be deferred we need to make sure that
|
||||||
* hook_fault_code is not called after __init memory is freed
|
* hook_fault_code is not called after __init memory is freed
|
||||||
|
@ -1149,6 +1221,7 @@ static int __init imx6_pcie_init(void)
|
||||||
*/
|
*/
|
||||||
hook_fault_code(8, imx6q_pcie_abort_handler, SIGBUS, 0,
|
hook_fault_code(8, imx6q_pcie_abort_handler, SIGBUS, 0,
|
||||||
"external abort on non-linefetch");
|
"external abort on non-linefetch");
|
||||||
|
#endif
|
||||||
|
|
||||||
return platform_driver_register(&imx6_pcie_driver);
|
return platform_driver_register(&imx6_pcie_driver);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue