Commit 740d6c37 authored by Jim Quinlan's avatar Jim Quinlan Committed by Lorenzo Pieralisi

PCI: brcmstb: Add control of rescal reset

Some STB chips have a special purpose reset controller named RESCAL (reset
calibration).  The PCIe HW can now control RESCAL to start and stop its
operation.  On probe(), the RESCAL is deasserted and the driver goes
through the sequence of setting registers and reading status in order to
start the internal PHY that is required for the PCIe.

Link: https://lore.kernel.org/r/20200911175232.19016-7-james.quinlan@broadcom.comSigned-off-by: default avatarJim Quinlan <jquinlan@broadcom.com>
Signed-off-by: default avatarLorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Reviewed-by: default avatarRob Herring <robh@kernel.org>
Acked-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>
parent 04356ac3
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/printk.h> #include <linux/printk.h>
#include <linux/reset.h>
#include <linux/sizes.h> #include <linux/sizes.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/string.h> #include <linux/string.h>
...@@ -994,6 +995,52 @@ static void brcm_pcie_enter_l23(struct brcm_pcie *pcie) ...@@ -994,6 +995,52 @@ static void brcm_pcie_enter_l23(struct brcm_pcie *pcie)
dev_err(pcie->dev, "failed to enter low-power link state\n"); dev_err(pcie->dev, "failed to enter low-power link state\n");
} }
static int brcm_phy_cntl(struct brcm_pcie *pcie, const int start)
{
static const u32 shifts[PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_NFLDS] = {
PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_PWRDN_SHIFT,
PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_RESET_SHIFT,
PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_DIG_RESET_SHIFT,};
static const u32 masks[PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_NFLDS] = {
PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_PWRDN_MASK,
PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_RESET_MASK,
PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_DIG_RESET_MASK,};
const int beg = start ? 0 : PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_NFLDS - 1;
const int end = start ? PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_NFLDS : -1;
u32 tmp, combined_mask = 0;
u32 val;
void __iomem *base = pcie->base;
int i, ret;
for (i = beg; i != end; start ? i++ : i--) {
val = start ? BIT_MASK(shifts[i]) : 0;
tmp = readl(base + PCIE_DVT_PMU_PCIE_PHY_CTRL);
tmp = (tmp & ~masks[i]) | (val & masks[i]);
writel(tmp, base + PCIE_DVT_PMU_PCIE_PHY_CTRL);
usleep_range(50, 200);
combined_mask |= masks[i];
}
tmp = readl(base + PCIE_DVT_PMU_PCIE_PHY_CTRL);
val = start ? combined_mask : 0;
ret = (tmp & combined_mask) == val ? 0 : -EIO;
if (ret)
dev_err(pcie->dev, "failed to %s phy\n", (start ? "start" : "stop"));
return ret;
}
static inline int brcm_phy_start(struct brcm_pcie *pcie)
{
return pcie->rescal ? brcm_phy_cntl(pcie, 1) : 0;
}
static inline int brcm_phy_stop(struct brcm_pcie *pcie)
{
return pcie->rescal ? brcm_phy_cntl(pcie, 0) : 0;
}
static void brcm_pcie_turn_off(struct brcm_pcie *pcie) static void brcm_pcie_turn_off(struct brcm_pcie *pcie)
{ {
void __iomem *base = pcie->base; void __iomem *base = pcie->base;
...@@ -1021,11 +1068,13 @@ static void brcm_pcie_turn_off(struct brcm_pcie *pcie) ...@@ -1021,11 +1068,13 @@ static void brcm_pcie_turn_off(struct brcm_pcie *pcie)
static int brcm_pcie_suspend(struct device *dev) static int brcm_pcie_suspend(struct device *dev)
{ {
struct brcm_pcie *pcie = dev_get_drvdata(dev); struct brcm_pcie *pcie = dev_get_drvdata(dev);
int ret;
brcm_pcie_turn_off(pcie); brcm_pcie_turn_off(pcie);
ret = brcm_phy_stop(pcie);
clk_disable_unprepare(pcie->clk); clk_disable_unprepare(pcie->clk);
return 0; return ret;
} }
static int brcm_pcie_resume(struct device *dev) static int brcm_pcie_resume(struct device *dev)
...@@ -1038,6 +1087,10 @@ static int brcm_pcie_resume(struct device *dev) ...@@ -1038,6 +1087,10 @@ static int brcm_pcie_resume(struct device *dev)
base = pcie->base; base = pcie->base;
clk_prepare_enable(pcie->clk); clk_prepare_enable(pcie->clk);
ret = brcm_phy_start(pcie);
if (ret)
goto err;
/* Take bridge out of reset so we can access the SERDES reg */ /* Take bridge out of reset so we can access the SERDES reg */
pcie->bridge_sw_init_set(pcie, 0); pcie->bridge_sw_init_set(pcie, 0);
...@@ -1051,18 +1104,24 @@ static int brcm_pcie_resume(struct device *dev) ...@@ -1051,18 +1104,24 @@ static int brcm_pcie_resume(struct device *dev)
ret = brcm_pcie_setup(pcie); ret = brcm_pcie_setup(pcie);
if (ret) if (ret)
return ret; goto err;
if (pcie->msi) if (pcie->msi)
brcm_msi_set_regs(pcie->msi); brcm_msi_set_regs(pcie->msi);
return 0; return 0;
err:
clk_disable_unprepare(pcie->clk);
return ret;
} }
static void __brcm_pcie_remove(struct brcm_pcie *pcie) static void __brcm_pcie_remove(struct brcm_pcie *pcie)
{ {
brcm_msi_remove(pcie); brcm_msi_remove(pcie);
brcm_pcie_turn_off(pcie); brcm_pcie_turn_off(pcie);
brcm_phy_stop(pcie);
reset_control_assert(pcie->rescal);
clk_disable_unprepare(pcie->clk); clk_disable_unprepare(pcie->clk);
} }
...@@ -1142,6 +1201,22 @@ static int brcm_pcie_probe(struct platform_device *pdev) ...@@ -1142,6 +1201,22 @@ static int brcm_pcie_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "could not enable clock\n"); dev_err(&pdev->dev, "could not enable clock\n");
return ret; return ret;
} }
pcie->rescal = devm_reset_control_get_optional_shared(&pdev->dev, "rescal");
if (IS_ERR(pcie->rescal)) {
clk_disable_unprepare(pcie->clk);
return PTR_ERR(pcie->rescal);
}
ret = reset_control_deassert(pcie->rescal);
if (ret)
dev_err(&pdev->dev, "failed to deassert 'rescal'\n");
ret = brcm_phy_start(pcie);
if (ret) {
reset_control_assert(pcie->rescal);
clk_disable_unprepare(pcie->clk);
return ret;
}
ret = brcm_pcie_setup(pcie); ret = brcm_pcie_setup(pcie);
if (ret) if (ret)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment