Merge branch 'remotes/lorenzo/pci/dwc'

- Support 100MHz/200MHz refclocks for i.MX6 (Lucas Stach)

  - Add initial power management for i.MX7 (Leonard Crestez)

  - Add PME_Turn_Off support for i.MX7 (Leonard Crestez)

  - Fix qcom runtime power management error handling (Bjorn Andersson)

  - Update TI dra7xx unaligned access errata workaround for host mode as
    well as endpoint mode (Vignesh R)

  - Fix kirin section mismatch warning (Nathan Chancellor)

* remotes/lorenzo/pci/dwc:
  PCI: imx: Add PME_Turn_Off support
  ARM: dts: imx7d: Add turnoff reset
  dt-bindings: imx6q-pcie: Add turnoff reset for imx7d
  reset: imx7: Add PCIE_CTRL_APPS_TURNOFF
  PCI: kirin: Fix section mismatch warning
  PCI: dwc: pci-dra7xx: Enable errata i870 for both EP and RC mode
  dt-bindings: PCI: dra7xx: Add bindings for unaligned access in host mode
  PCI: qcom: Fix error handling in runtime PM support
  PCI: imx: Initial imx7d pm support
  PCI: imx6: Support MPLL reconfiguration for 100MHz and 200MHz refclock
This commit is contained in:
Bjorn Helgaas 2018-10-20 11:45:49 -05:00
commit 525fde0750
9 changed files with 233 additions and 30 deletions

View File

@ -50,6 +50,7 @@ Additional required properties for imx7d-pcie:
- reset-names: Must contain the following entires:
- "pciephy"
- "apps"
- "turnoff"
Example:

View File

@ -26,6 +26,11 @@ HOST MODE
ranges,
interrupt-map-mask,
interrupt-map : as specified in ../designware-pcie.txt
- ti,syscon-unaligned-access: phandle to the syscon DT node. The 1st argument
should contain the register offset within syscon
and the 2nd argument should contain the bit field
for setting the bit to enable unaligned
access.
DEVICE MODE
===========

View File

@ -146,8 +146,9 @@
fsl,max-link-speed = <2>;
power-domains = <&pgc_pcie_phy>;
resets = <&src IMX7_RESET_PCIEPHY>,
<&src IMX7_RESET_PCIE_CTRL_APPS_EN>;
reset-names = "pciephy", "apps";
<&src IMX7_RESET_PCIE_CTRL_APPS_EN>,
<&src IMX7_RESET_PCIE_CTRL_APPS_TURNOFF>;
reset-names = "pciephy", "apps", "turnoff";
status = "disabled";
};
};

View File

@ -542,7 +542,7 @@ static const struct of_device_id of_dra7xx_pcie_match[] = {
};
/*
* dra7xx_pcie_ep_unaligned_memaccess: workaround for AM572x/AM571x Errata i870
* dra7xx_pcie_unaligned_memaccess: workaround for AM572x/AM571x Errata i870
* @dra7xx: the dra7xx device where the workaround should be applied
*
* Access to the PCIe slave port that are not 32-bit aligned will result
@ -552,7 +552,7 @@ static const struct of_device_id of_dra7xx_pcie_match[] = {
*
* To avoid this issue set PCIE_SS1_AXI2OCP_LEGACY_MODE_ENABLE to 1.
*/
static int dra7xx_pcie_ep_unaligned_memaccess(struct device *dev)
static int dra7xx_pcie_unaligned_memaccess(struct device *dev)
{
int ret;
struct device_node *np = dev->of_node;
@ -704,6 +704,11 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_DEVICE_TYPE,
DEVICE_TYPE_RC);
ret = dra7xx_pcie_unaligned_memaccess(dev);
if (ret)
dev_err(dev, "WA for Errata i870 not applied\n");
ret = dra7xx_add_pcie_port(dra7xx, pdev);
if (ret < 0)
goto err_gpio;
@ -717,7 +722,7 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_DEVICE_TYPE,
DEVICE_TYPE_EP);
ret = dra7xx_pcie_ep_unaligned_memaccess(dev);
ret = dra7xx_pcie_unaligned_memaccess(dev);
if (ret)
goto err_gpio;

View File

@ -50,6 +50,7 @@ struct imx6_pcie {
struct regmap *iomuxc_gpr;
struct reset_control *pciephy_reset;
struct reset_control *apps_reset;
struct reset_control *turnoff_reset;
enum imx6_pcie_variants variant;
u32 tx_deemph_gen1;
u32 tx_deemph_gen2_3p5db;
@ -97,6 +98,16 @@ struct imx6_pcie {
#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17)
/* PHY registers (not memory-mapped) */
#define PCIE_PHY_ATEOVRD 0x10
#define PCIE_PHY_ATEOVRD_EN (0x1 << 2)
#define PCIE_PHY_ATEOVRD_REF_CLKDIV_SHIFT 0
#define PCIE_PHY_ATEOVRD_REF_CLKDIV_MASK 0x1
#define PCIE_PHY_MPLL_OVRD_IN_LO 0x11
#define PCIE_PHY_MPLL_MULTIPLIER_SHIFT 2
#define PCIE_PHY_MPLL_MULTIPLIER_MASK 0x7f
#define PCIE_PHY_MPLL_MULTIPLIER_OVRD (0x1 << 9)
#define PCIE_PHY_RX_ASIC_OUT 0x100D
#define PCIE_PHY_RX_ASIC_OUT_VALID (1 << 0)
@ -508,6 +519,50 @@ static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
IMX6Q_GPR12_DEVICE_TYPE, PCI_EXP_TYPE_ROOT_PORT << 12);
}
static int imx6_setup_phy_mpll(struct imx6_pcie *imx6_pcie)
{
unsigned long phy_rate = clk_get_rate(imx6_pcie->pcie_phy);
int mult, div;
u32 val;
switch (phy_rate) {
case 125000000:
/*
* The default settings of the MPLL are for a 125MHz input
* clock, so no need to reconfigure anything in that case.
*/
return 0;
case 100000000:
mult = 25;
div = 0;
break;
case 200000000:
mult = 25;
div = 1;
break;
default:
dev_err(imx6_pcie->pci->dev,
"Unsupported PHY reference clock rate %lu\n", phy_rate);
return -EINVAL;
}
pcie_phy_read(imx6_pcie, PCIE_PHY_MPLL_OVRD_IN_LO, &val);
val &= ~(PCIE_PHY_MPLL_MULTIPLIER_MASK <<
PCIE_PHY_MPLL_MULTIPLIER_SHIFT);
val |= mult << PCIE_PHY_MPLL_MULTIPLIER_SHIFT;
val |= PCIE_PHY_MPLL_MULTIPLIER_OVRD;
pcie_phy_write(imx6_pcie, PCIE_PHY_MPLL_OVRD_IN_LO, val);
pcie_phy_read(imx6_pcie, PCIE_PHY_ATEOVRD, &val);
val &= ~(PCIE_PHY_ATEOVRD_REF_CLKDIV_MASK <<
PCIE_PHY_ATEOVRD_REF_CLKDIV_SHIFT);
val |= div << PCIE_PHY_ATEOVRD_REF_CLKDIV_SHIFT;
val |= PCIE_PHY_ATEOVRD_EN;
pcie_phy_write(imx6_pcie, PCIE_PHY_ATEOVRD, val);
return 0;
}
static int imx6_pcie_wait_for_link(struct imx6_pcie *imx6_pcie)
{
struct dw_pcie *pci = imx6_pcie->pci;
@ -542,6 +597,24 @@ static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie)
return -EINVAL;
}
static void imx6_pcie_ltssm_enable(struct device *dev)
{
struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev);
switch (imx6_pcie->variant) {
case IMX6Q:
case IMX6SX:
case IMX6QP:
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
IMX6Q_GPR12_PCIE_CTL_2,
IMX6Q_GPR12_PCIE_CTL_2);
break;
case IMX7D:
reset_control_deassert(imx6_pcie->apps_reset);
break;
}
}
static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
{
struct dw_pcie *pci = imx6_pcie->pci;
@ -560,11 +633,7 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp);
/* Start LTSSM. */
if (imx6_pcie->variant == IMX7D)
reset_control_deassert(imx6_pcie->apps_reset);
else
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
IMX6Q_GPR12_PCIE_CTL_2, 1 << 10);
imx6_pcie_ltssm_enable(dev);
ret = imx6_pcie_wait_for_link(imx6_pcie);
if (ret)
@ -632,6 +701,7 @@ static int imx6_pcie_host_init(struct pcie_port *pp)
imx6_pcie_assert_core_reset(imx6_pcie);
imx6_pcie_init_phy(imx6_pcie);
imx6_pcie_deassert_core_reset(imx6_pcie);
imx6_setup_phy_mpll(imx6_pcie);
dw_pcie_setup_rc(pp);
imx6_pcie_establish_link(imx6_pcie);
@ -682,6 +752,94 @@ static const struct dw_pcie_ops dw_pcie_ops = {
.link_up = imx6_pcie_link_up,
};
#ifdef CONFIG_PM_SLEEP
static void imx6_pcie_ltssm_disable(struct device *dev)
{
struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev);
switch (imx6_pcie->variant) {
case IMX6SX:
case IMX6QP:
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
IMX6Q_GPR12_PCIE_CTL_2, 0);
break;
case IMX7D:
reset_control_assert(imx6_pcie->apps_reset);
break;
default:
dev_err(dev, "ltssm_disable not supported\n");
}
}
static void imx6_pcie_pm_turnoff(struct imx6_pcie *imx6_pcie)
{
reset_control_assert(imx6_pcie->turnoff_reset);
reset_control_deassert(imx6_pcie->turnoff_reset);
/*
* Components with an upstream port must respond to
* PME_Turn_Off with PME_TO_Ack but we can't check.
*
* The standard recommends a 1-10ms timeout after which to
* proceed anyway as if acks were received.
*/
usleep_range(1000, 10000);
}
static void imx6_pcie_clk_disable(struct imx6_pcie *imx6_pcie)
{
clk_disable_unprepare(imx6_pcie->pcie);
clk_disable_unprepare(imx6_pcie->pcie_phy);
clk_disable_unprepare(imx6_pcie->pcie_bus);
if (imx6_pcie->variant == IMX7D) {
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
IMX7D_GPR12_PCIE_PHY_REFCLK_SEL,
IMX7D_GPR12_PCIE_PHY_REFCLK_SEL);
}
}
static int imx6_pcie_suspend_noirq(struct device *dev)
{
struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev);
if (imx6_pcie->variant != IMX7D)
return 0;
imx6_pcie_pm_turnoff(imx6_pcie);
imx6_pcie_clk_disable(imx6_pcie);
imx6_pcie_ltssm_disable(dev);
return 0;
}
static int imx6_pcie_resume_noirq(struct device *dev)
{
int ret;
struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev);
struct pcie_port *pp = &imx6_pcie->pci->pp;
if (imx6_pcie->variant != IMX7D)
return 0;
imx6_pcie_assert_core_reset(imx6_pcie);
imx6_pcie_init_phy(imx6_pcie);
imx6_pcie_deassert_core_reset(imx6_pcie);
dw_pcie_setup_rc(pp);
ret = imx6_pcie_establish_link(imx6_pcie);
if (ret < 0)
dev_info(dev, "pcie link is down after resume.\n");
return 0;
}
#endif
static const struct dev_pm_ops imx6_pcie_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx6_pcie_suspend_noirq,
imx6_pcie_resume_noirq)
};
static int imx6_pcie_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@ -776,6 +934,13 @@ static int imx6_pcie_probe(struct platform_device *pdev)
break;
}
/* Grab turnoff reset */
imx6_pcie->turnoff_reset = devm_reset_control_get_optional_exclusive(dev, "turnoff");
if (IS_ERR(imx6_pcie->turnoff_reset)) {
dev_err(dev, "Failed to get TURNOFF reset control\n");
return PTR_ERR(imx6_pcie->turnoff_reset);
}
/* Grab GPR config register range */
imx6_pcie->iomuxc_gpr =
syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
@ -848,6 +1013,7 @@ static struct platform_driver imx6_pcie_driver = {
.name = "imx6q-pcie",
.of_match_table = imx6_pcie_of_match,
.suppress_bind_attrs = true,
.pm = &imx6_pcie_pm_ops,
},
.probe = imx6_pcie_probe,
.shutdown = imx6_pcie_shutdown,

View File

@ -467,8 +467,8 @@ static int kirin_pcie_add_msi(struct dw_pcie *pci,
return 0;
}
static int __init kirin_add_pcie_port(struct dw_pcie *pci,
struct platform_device *pdev)
static int kirin_add_pcie_port(struct dw_pcie *pci,
struct platform_device *pdev)
{
int ret;

View File

@ -1089,7 +1089,6 @@ static int qcom_pcie_host_init(struct pcie_port *pp)
struct qcom_pcie *pcie = to_qcom_pcie(pci);
int ret;
pm_runtime_get_sync(pci->dev);
qcom_ep_reset_assert(pcie);
ret = pcie->ops->init(pcie);
@ -1126,7 +1125,6 @@ err_disable_phy:
phy_power_off(pcie->phy);
err_deinit:
pcie->ops->deinit(pcie);
pm_runtime_put(pci->dev);
return ret;
}
@ -1216,6 +1214,12 @@ static int qcom_pcie_probe(struct platform_device *pdev)
return -ENOMEM;
pm_runtime_enable(dev);
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
pm_runtime_disable(dev);
return ret;
}
pci->dev = dev;
pci->ops = &dw_pcie_ops;
pp = &pci->pp;
@ -1225,44 +1229,56 @@ static int qcom_pcie_probe(struct platform_device *pdev)
pcie->ops = of_device_get_match_data(dev);
pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_LOW);
if (IS_ERR(pcie->reset))
return PTR_ERR(pcie->reset);
if (IS_ERR(pcie->reset)) {
ret = PTR_ERR(pcie->reset);
goto err_pm_runtime_put;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf");
pcie->parf = devm_ioremap_resource(dev, res);
if (IS_ERR(pcie->parf))
return PTR_ERR(pcie->parf);
if (IS_ERR(pcie->parf)) {
ret = PTR_ERR(pcie->parf);
goto err_pm_runtime_put;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
pci->dbi_base = devm_pci_remap_cfg_resource(dev, res);
if (IS_ERR(pci->dbi_base))
return PTR_ERR(pci->dbi_base);
if (IS_ERR(pci->dbi_base)) {
ret = PTR_ERR(pci->dbi_base);
goto err_pm_runtime_put;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi");
pcie->elbi = devm_ioremap_resource(dev, res);
if (IS_ERR(pcie->elbi))
return PTR_ERR(pcie->elbi);
if (IS_ERR(pcie->elbi)) {
ret = PTR_ERR(pcie->elbi);
goto err_pm_runtime_put;
}
pcie->phy = devm_phy_optional_get(dev, "pciephy");
if (IS_ERR(pcie->phy))
return PTR_ERR(pcie->phy);
if (IS_ERR(pcie->phy)) {
ret = PTR_ERR(pcie->phy);
goto err_pm_runtime_put;
}
ret = pcie->ops->get_resources(pcie);
if (ret)
return ret;
goto err_pm_runtime_put;
pp->ops = &qcom_pcie_dw_ops;
if (IS_ENABLED(CONFIG_PCI_MSI)) {
pp->msi_irq = platform_get_irq_byname(pdev, "msi");
if (pp->msi_irq < 0)
return pp->msi_irq;
if (pp->msi_irq < 0) {
ret = pp->msi_irq;
goto err_pm_runtime_put;
}
}
ret = phy_init(pcie->phy);
if (ret) {
pm_runtime_disable(&pdev->dev);
return ret;
goto err_pm_runtime_put;
}
platform_set_drvdata(pdev, pcie);
@ -1271,10 +1287,16 @@ static int qcom_pcie_probe(struct platform_device *pdev)
if (ret) {
dev_err(dev, "cannot initialize host\n");
pm_runtime_disable(&pdev->dev);
return ret;
goto err_pm_runtime_put;
}
return 0;
err_pm_runtime_put:
pm_runtime_put(dev);
pm_runtime_disable(dev);
return ret;
}
static const struct of_device_id qcom_pcie_match[] = {

View File

@ -67,6 +67,7 @@ static const struct imx7_src_signal imx7_src_signals[IMX7_RESET_NUM] = {
[IMX7_RESET_PCIEPHY] = { SRC_PCIEPHY_RCR, BIT(2) | BIT(1) },
[IMX7_RESET_PCIEPHY_PERST] = { SRC_PCIEPHY_RCR, BIT(3) },
[IMX7_RESET_PCIE_CTRL_APPS_EN] = { SRC_PCIEPHY_RCR, BIT(6) },
[IMX7_RESET_PCIE_CTRL_APPS_TURNOFF] = { SRC_PCIEPHY_RCR, BIT(11) },
[IMX7_RESET_DDRC_PRST] = { SRC_DDRC_RCR, BIT(0) },
[IMX7_RESET_DDRC_CORE_RST] = { SRC_DDRC_RCR, BIT(1) },
};

View File

@ -56,7 +56,9 @@
#define IMX7_RESET_DDRC_PRST 23
#define IMX7_RESET_DDRC_CORE_RST 24
#define IMX7_RESET_NUM 25
#define IMX7_RESET_PCIE_CTRL_APPS_TURNOFF 25
#define IMX7_RESET_NUM 26
#endif