mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 04:31:50 +00:00
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:
commit
525fde0750
@ -50,6 +50,7 @@ Additional required properties for imx7d-pcie:
|
||||
- reset-names: Must contain the following entires:
|
||||
- "pciephy"
|
||||
- "apps"
|
||||
- "turnoff"
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -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
|
||||
===========
|
||||
|
@ -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";
|
||||
};
|
||||
};
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -467,7 +467,7 @@ static int kirin_pcie_add_msi(struct dw_pcie *pci,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init kirin_add_pcie_port(struct dw_pcie *pci,
|
||||
static int kirin_add_pcie_port(struct dw_pcie *pci,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
|
@ -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[] = {
|
||||
|
@ -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) },
|
||||
};
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user