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.com
Signed-off-by: Jim Quinlan <jquinlan@broadcom.com>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Acked-by: Florian Fainelli <f.fainelli@gmail.com>
This commit is contained in:
Jim Quinlan 2020-09-11 13:52:26 -04:00 committed by Lorenzo Pieralisi
parent 04356ac307
commit 740d6c3708

View File

@ -23,6 +23,7 @@
#include <linux/of_platform.h>
#include <linux/pci.h>
#include <linux/printk.h>
#include <linux/reset.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/string.h>
@ -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");
}
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)
{
void __iomem *base = pcie->base;
@ -1021,11 +1068,13 @@ static void brcm_pcie_turn_off(struct brcm_pcie *pcie)
static int brcm_pcie_suspend(struct device *dev)
{
struct brcm_pcie *pcie = dev_get_drvdata(dev);
int ret;
brcm_pcie_turn_off(pcie);
ret = brcm_phy_stop(pcie);
clk_disable_unprepare(pcie->clk);
return 0;
return ret;
}
static int brcm_pcie_resume(struct device *dev)
@ -1038,6 +1087,10 @@ static int brcm_pcie_resume(struct device *dev)
base = pcie->base;
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 */
pcie->bridge_sw_init_set(pcie, 0);
@ -1051,18 +1104,24 @@ static int brcm_pcie_resume(struct device *dev)
ret = brcm_pcie_setup(pcie);
if (ret)
return ret;
goto err;
if (pcie->msi)
brcm_msi_set_regs(pcie->msi);
return 0;
err:
clk_disable_unprepare(pcie->clk);
return ret;
}
static void __brcm_pcie_remove(struct brcm_pcie *pcie)
{
brcm_msi_remove(pcie);
brcm_pcie_turn_off(pcie);
brcm_phy_stop(pcie);
reset_control_assert(pcie->rescal);
clk_disable_unprepare(pcie->clk);
}
@ -1142,6 +1201,22 @@ static int brcm_pcie_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "could not enable clock\n");
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);
if (ret)