mirror of
https://github.com/torvalds/linux.git
synced 2026-05-30 18:13:41 +02:00
soc: xilinx: vcu: make pll post divider explicit
According to the downstream driver documentation due to timing constraints the output divider of the PLL has to be set to 1/2. Add a helper function for that check instead of burying the code in one large setup function. The bit is undocumented and marked as reserved in the register reference. Signed-off-by: Michael Tretter <m.tretter@pengutronix.de> Acked-by: Michal Simek <michal.simek@xilinx.com> Link: https://lore.kernel.org/r/20210121071659.1226489-10-m.tretter@pengutronix.de Signed-off-by: Stephen Boyd <sboyd@kernel.org>
This commit is contained in:
parent
9c789deea2
commit
4472e1849d
|
|
@ -72,6 +72,7 @@
|
||||||
* @logicore_reg_ba: logicore reg base address
|
* @logicore_reg_ba: logicore reg base address
|
||||||
* @vcu_slcr_ba: vcu_slcr Register base address
|
* @vcu_slcr_ba: vcu_slcr Register base address
|
||||||
* @pll: handle for the VCU PLL
|
* @pll: handle for the VCU PLL
|
||||||
|
* @pll_post: handle for the VCU PLL post divider
|
||||||
* @clk_data: clocks provided by the vcu clock provider
|
* @clk_data: clocks provided by the vcu clock provider
|
||||||
*/
|
*/
|
||||||
struct xvcu_device {
|
struct xvcu_device {
|
||||||
|
|
@ -81,6 +82,7 @@ struct xvcu_device {
|
||||||
struct regmap *logicore_reg_ba;
|
struct regmap *logicore_reg_ba;
|
||||||
void __iomem *vcu_slcr_ba;
|
void __iomem *vcu_slcr_ba;
|
||||||
struct clk_hw *pll;
|
struct clk_hw *pll;
|
||||||
|
struct clk_hw *pll_post;
|
||||||
struct clk_hw_onecell_data *clk_data;
|
struct clk_hw_onecell_data *clk_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -274,6 +276,29 @@ static int xvcu_pll_wait_for_lock(struct xvcu_device *xvcu)
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct clk_hw *xvcu_register_pll_post(struct device *dev,
|
||||||
|
const char *name,
|
||||||
|
const struct clk_hw *parent_hw,
|
||||||
|
void __iomem *reg_base)
|
||||||
|
{
|
||||||
|
u32 div;
|
||||||
|
u32 vcu_pll_ctrl;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The output divider of the PLL must be set to 1/2 to meet the
|
||||||
|
* timing in the design.
|
||||||
|
*/
|
||||||
|
vcu_pll_ctrl = xvcu_read(reg_base, VCU_PLL_CTRL);
|
||||||
|
div = vcu_pll_ctrl >> VCU_PLL_CTRL_CLKOUTDIV_SHIFT;
|
||||||
|
div = div & VCU_PLL_CTRL_CLKOUTDIV_MASK;
|
||||||
|
if (div != 1)
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
|
return clk_hw_register_fixed_factor(dev, "vcu_pll_post",
|
||||||
|
clk_hw_get_name(parent_hw),
|
||||||
|
CLK_SET_RATE_PARENT, 1, 2);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct xvcu_pll_cfg *xvcu_find_cfg(int div)
|
static const struct xvcu_pll_cfg *xvcu_find_cfg(int div)
|
||||||
{
|
{
|
||||||
const struct xvcu_pll_cfg *cfg = NULL;
|
const struct xvcu_pll_cfg *cfg = NULL;
|
||||||
|
|
@ -402,7 +427,7 @@ static int xvcu_set_vcu_pll_info(struct xvcu_device *xvcu)
|
||||||
{
|
{
|
||||||
u32 refclk, coreclk, mcuclk, inte, deci;
|
u32 refclk, coreclk, mcuclk, inte, deci;
|
||||||
u32 divisor_mcu, divisor_core, fvco;
|
u32 divisor_mcu, divisor_core, fvco;
|
||||||
u32 clkoutdiv, vcu_pll_ctrl, pll_clk;
|
u32 pll_clk;
|
||||||
u32 mod;
|
u32 mod;
|
||||||
int i;
|
int i;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
@ -425,19 +450,6 @@ static int xvcu_set_vcu_pll_info(struct xvcu_device *xvcu)
|
||||||
dev_dbg(xvcu->dev, "Core clock from logicoreIP is %uHz\n", coreclk);
|
dev_dbg(xvcu->dev, "Core clock from logicoreIP is %uHz\n", coreclk);
|
||||||
dev_dbg(xvcu->dev, "Mcu clock from logicoreIP is %uHz\n", mcuclk);
|
dev_dbg(xvcu->dev, "Mcu clock from logicoreIP is %uHz\n", mcuclk);
|
||||||
|
|
||||||
/*
|
|
||||||
* The divide-by-2 should be always enabled (==1)
|
|
||||||
* to meet the timing in the design.
|
|
||||||
* Otherwise, it's an error
|
|
||||||
*/
|
|
||||||
vcu_pll_ctrl = xvcu_read(xvcu->vcu_slcr_ba, VCU_PLL_CTRL);
|
|
||||||
clkoutdiv = vcu_pll_ctrl >> VCU_PLL_CTRL_CLKOUTDIV_SHIFT;
|
|
||||||
clkoutdiv = clkoutdiv & VCU_PLL_CTRL_CLKOUTDIV_MASK;
|
|
||||||
if (clkoutdiv != 1) {
|
|
||||||
dev_err(xvcu->dev, "clkoutdiv value is invalid\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = ARRAY_SIZE(xvcu_pll_cfg) - 1; i >= 0; i--) {
|
for (i = ARRAY_SIZE(xvcu_pll_cfg) - 1; i >= 0; i--) {
|
||||||
const struct xvcu_pll_cfg *cfg = &xvcu_pll_cfg[i];
|
const struct xvcu_pll_cfg *cfg = &xvcu_pll_cfg[i];
|
||||||
|
|
||||||
|
|
@ -484,7 +496,7 @@ static int xvcu_set_vcu_pll_info(struct xvcu_device *xvcu)
|
||||||
|
|
||||||
hw = clk_hw_register_fixed_rate(xvcu->dev, "vcu_pll",
|
hw = clk_hw_register_fixed_rate(xvcu->dev, "vcu_pll",
|
||||||
__clk_get_name(xvcu->pll_ref),
|
__clk_get_name(xvcu->pll_ref),
|
||||||
0, pll_clk);
|
0, fvco);
|
||||||
if (IS_ERR(hw))
|
if (IS_ERR(hw))
|
||||||
return PTR_ERR(hw);
|
return PTR_ERR(hw);
|
||||||
xvcu->pll = hw;
|
xvcu->pll = hw;
|
||||||
|
|
@ -607,6 +619,7 @@ static int xvcu_register_clock_provider(struct xvcu_device *xvcu)
|
||||||
struct clk_parent_data parent_data[2] = { 0 };
|
struct clk_parent_data parent_data[2] = { 0 };
|
||||||
struct clk_hw_onecell_data *data;
|
struct clk_hw_onecell_data *data;
|
||||||
struct clk_hw **hws;
|
struct clk_hw **hws;
|
||||||
|
struct clk_hw *hw;
|
||||||
void __iomem *reg_base = xvcu->vcu_slcr_ba;
|
void __iomem *reg_base = xvcu->vcu_slcr_ba;
|
||||||
|
|
||||||
data = devm_kzalloc(dev, struct_size(data, hws, CLK_XVCU_NUM_CLOCKS), GFP_KERNEL);
|
data = devm_kzalloc(dev, struct_size(data, hws, CLK_XVCU_NUM_CLOCKS), GFP_KERNEL);
|
||||||
|
|
@ -617,8 +630,13 @@ static int xvcu_register_clock_provider(struct xvcu_device *xvcu)
|
||||||
|
|
||||||
xvcu->clk_data = data;
|
xvcu->clk_data = data;
|
||||||
|
|
||||||
|
hw = xvcu_register_pll_post(dev, "vcu_pll_post", xvcu->pll, reg_base);
|
||||||
|
if (IS_ERR(hw))
|
||||||
|
return PTR_ERR(hw);
|
||||||
|
xvcu->pll_post = hw;
|
||||||
|
|
||||||
parent_data[0].fw_name = "pll_ref";
|
parent_data[0].fw_name = "pll_ref";
|
||||||
parent_data[1].hw = xvcu->pll;
|
parent_data[1].hw = xvcu->pll_post;
|
||||||
|
|
||||||
hws[CLK_XVCU_ENC_CORE] =
|
hws[CLK_XVCU_ENC_CORE] =
|
||||||
xvcu_clk_hw_register_leaf(dev, "venc_core_clk",
|
xvcu_clk_hw_register_leaf(dev, "venc_core_clk",
|
||||||
|
|
@ -657,6 +675,8 @@ static void xvcu_unregister_clock_provider(struct xvcu_device *xvcu)
|
||||||
xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_ENC_MCU]);
|
xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_ENC_MCU]);
|
||||||
if (!IS_ERR_OR_NULL(hws[CLK_XVCU_ENC_CORE]))
|
if (!IS_ERR_OR_NULL(hws[CLK_XVCU_ENC_CORE]))
|
||||||
xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_ENC_CORE]);
|
xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_ENC_CORE]);
|
||||||
|
|
||||||
|
clk_hw_unregister_fixed_factor(xvcu->pll_post);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user