summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/soc/xilinx/xlnx_vcu.c140
1 files changed, 103 insertions, 37 deletions
diff --git a/drivers/soc/xilinx/xlnx_vcu.c b/drivers/soc/xilinx/xlnx_vcu.c
index 9e410d7ffd08..7585b26ab51a 100644
--- a/drivers/soc/xilinx/xlnx_vcu.c
+++ b/drivers/soc/xilinx/xlnx_vcu.c
@@ -260,9 +260,18 @@ static void xvcu_write_field_reg(void __iomem *iomem, int offset,
xvcu_write(iomem, offset, val);
}
-static int xvcu_pll_wait_for_lock(struct xvcu_device *xvcu)
+#define to_vcu_pll(_hw) container_of(_hw, struct vcu_pll, hw)
+
+struct vcu_pll {
+ struct clk_hw hw;
+ void __iomem *reg_base;
+ unsigned long fvco_min;
+ unsigned long fvco_max;
+};
+
+static int xvcu_pll_wait_for_lock(struct vcu_pll *pll)
{
- void __iomem *base = xvcu->vcu_slcr_ba;
+ void __iomem *base = pll->reg_base;
unsigned long timeout;
u32 lock_status;
@@ -311,9 +320,9 @@ static const struct xvcu_pll_cfg *xvcu_find_cfg(int div)
return cfg;
}
-static int xvcu_pll_set_div(struct xvcu_device *xvcu, int div)
+static int xvcu_pll_set_div(struct vcu_pll *pll, int div)
{
- void __iomem *base = xvcu->vcu_slcr_ba;
+ void __iomem *base = pll->reg_base;
const struct xvcu_pll_cfg *cfg = NULL;
u32 vcu_pll_ctrl;
u32 cfg_val;
@@ -338,24 +347,49 @@ static int xvcu_pll_set_div(struct xvcu_device *xvcu, int div)
return 0;
}
-static int xvcu_pll_set_rate(struct xvcu_device *xvcu,
+static long xvcu_pll_round_rate(struct clk_hw *hw,
+ unsigned long rate, unsigned long *parent_rate)
+{
+ struct vcu_pll *pll = to_vcu_pll(hw);
+ unsigned int feedback_div;
+
+ rate = clamp_t(unsigned long, rate, pll->fvco_min, pll->fvco_max);
+
+ feedback_div = DIV_ROUND_CLOSEST_ULL(rate, *parent_rate);
+ feedback_div = clamp_t(unsigned int, feedback_div, 25, 125);
+
+ return *parent_rate * feedback_div;
+}
+
+static unsigned long xvcu_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct vcu_pll *pll = to_vcu_pll(hw);
+ void __iomem *base = pll->reg_base;
+ unsigned int div;
+ u32 vcu_pll_ctrl;
+
+ vcu_pll_ctrl = xvcu_read(base, VCU_PLL_CTRL);
+ div = (vcu_pll_ctrl >> VCU_PLL_CTRL_FBDIV_SHIFT) & VCU_PLL_CTRL_FBDIV_MASK;
+
+ return div * parent_rate;
+}
+
+static int xvcu_pll_set_rate(struct clk_hw *hw,
unsigned long rate, unsigned long parent_rate)
{
- return xvcu_pll_set_div(xvcu, rate / parent_rate);
+ struct vcu_pll *pll = to_vcu_pll(hw);
+
+ return xvcu_pll_set_div(pll, rate / parent_rate);
}
-static int xvcu_pll_enable(struct xvcu_device *xvcu)
+static int xvcu_pll_enable(struct clk_hw *hw)
{
- void __iomem *base = xvcu->vcu_slcr_ba;
+ struct vcu_pll *pll = to_vcu_pll(hw);
+ void __iomem *base = pll->reg_base;
u32 vcu_pll_ctrl;
int ret;
- ret = clk_prepare_enable(xvcu->pll_ref);
- if (ret) {
- dev_err(xvcu->dev, "failed to enable pll_ref clock source\n");
- return ret;
- }
-
xvcu_write_field_reg(base, VCU_PLL_CTRL,
1, VCU_PLL_CTRL_BYPASS_MASK,
VCU_PLL_CTRL_BYPASS_SHIFT);
@@ -375,9 +409,9 @@ static int xvcu_pll_enable(struct xvcu_device *xvcu)
vcu_pll_ctrl |= (0 & VCU_PLL_CTRL_RESET_MASK) << VCU_PLL_CTRL_RESET_SHIFT;
xvcu_write(base, VCU_PLL_CTRL, vcu_pll_ctrl);
- ret = xvcu_pll_wait_for_lock(xvcu);
+ ret = xvcu_pll_wait_for_lock(pll);
if (ret) {
- dev_err(xvcu->dev, "PLL is not locked\n");
+ pr_err("VCU PLL is not locked\n");
goto err;
}
@@ -385,15 +419,14 @@ static int xvcu_pll_enable(struct xvcu_device *xvcu)
0, VCU_PLL_CTRL_BYPASS_MASK,
VCU_PLL_CTRL_BYPASS_SHIFT);
- return ret;
err:
- clk_disable_unprepare(xvcu->pll_ref);
return ret;
}
-static void xvcu_pll_disable(struct xvcu_device *xvcu)
+static void xvcu_pll_disable(struct clk_hw *hw)
{
- void __iomem *base = xvcu->vcu_slcr_ba;
+ struct vcu_pll *pll = to_vcu_pll(hw);
+ void __iomem *base = pll->reg_base;
u32 vcu_pll_ctrl;
vcu_pll_ctrl = xvcu_read(base, VCU_PLL_CTRL);
@@ -404,8 +437,49 @@ static void xvcu_pll_disable(struct xvcu_device *xvcu)
vcu_pll_ctrl &= ~(VCU_PLL_CTRL_RESET_MASK << VCU_PLL_CTRL_RESET_SHIFT);
vcu_pll_ctrl |= (1 & VCU_PLL_CTRL_RESET_MASK) << VCU_PLL_CTRL_RESET_SHIFT;
xvcu_write(base, VCU_PLL_CTRL, vcu_pll_ctrl);
+}
+
+static const struct clk_ops vcu_pll_ops = {
+ .enable = xvcu_pll_enable,
+ .disable = xvcu_pll_disable,
+ .round_rate = xvcu_pll_round_rate,
+ .recalc_rate = xvcu_pll_recalc_rate,
+ .set_rate = xvcu_pll_set_rate,
+};
- clk_disable_unprepare(xvcu->pll_ref);
+static struct clk_hw *xvcu_register_pll(struct device *dev,
+ void __iomem *reg_base,
+ const char *name, const char *parent,
+ unsigned long flags)
+{
+ struct vcu_pll *pll;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ int ret;
+
+ init.name = name;
+ init.parent_names = &parent;
+ init.ops = &vcu_pll_ops;
+ init.num_parents = 1;
+ init.flags = flags;
+
+ pll = devm_kmalloc(dev, sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ pll->hw.init = &init;
+ pll->reg_base = reg_base;
+ pll->fvco_min = FVCO_MIN;
+ pll->fvco_max = FVCO_MAX;
+
+ hw = &pll->hw;
+ ret = devm_clk_hw_register(dev, hw);
+ if (ret)
+ return ERR_PTR(ret);
+
+ clk_hw_set_rate_range(hw, pll->fvco_min, pll->fvco_max);
+
+ return hw;
}
/**
@@ -430,9 +504,7 @@ static int xvcu_set_vcu_pll_info(struct xvcu_device *xvcu)
u32 pll_clk;
u32 mod;
int i;
- int ret;
const struct xvcu_pll_cfg *found = NULL;
- struct clk_hw *hw;
regmap_read(xvcu->logicore_reg_ba, VCU_PLL_CLK, &inte);
regmap_read(xvcu->logicore_reg_ba, VCU_PLL_CLK_DEC, &deci);
@@ -490,17 +562,6 @@ static int xvcu_set_vcu_pll_info(struct xvcu_device *xvcu)
dev_dbg(xvcu->dev, "Actual Core clock freq is %uHz\n", coreclk);
dev_dbg(xvcu->dev, "Actual Mcu clock freq is %uHz\n", mcuclk);
- ret = xvcu_pll_set_rate(xvcu, fvco, refclk);
- if (ret)
- return ret;
-
- hw = clk_hw_register_fixed_rate(xvcu->dev, "vcu_pll",
- __clk_get_name(xvcu->pll_ref),
- 0, fvco);
- if (IS_ERR(hw))
- return PTR_ERR(hw);
- xvcu->pll = hw;
-
return 0;
}
@@ -523,7 +584,7 @@ static int xvcu_set_pll(struct xvcu_device *xvcu)
return ret;
}
- return xvcu_pll_enable(xvcu);
+ return 0;
}
static struct clk_hw *xvcu_clk_hw_register_leaf(struct device *dev,
@@ -630,6 +691,13 @@ static int xvcu_register_clock_provider(struct xvcu_device *xvcu)
xvcu->clk_data = data;
+ hw = xvcu_register_pll(dev, reg_base,
+ "vcu_pll", __clk_get_name(xvcu->pll_ref),
+ CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE);
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+ xvcu->pll = hw;
+
hw = xvcu_register_pll_post(dev, "vcu_pll_post", xvcu->pll, reg_base);
if (IS_ERR(hw))
return PTR_ERR(hw);
@@ -811,8 +879,6 @@ static int xvcu_remove(struct platform_device *pdev)
/* Add the the Gasket isolation and put the VCU in reset. */
regmap_write(xvcu->logicore_reg_ba, VCU_GASKET_INIT, 0);
- clk_hw_unregister_fixed_rate(xvcu->pll);
- xvcu_pll_disable(xvcu);
clk_disable_unprepare(xvcu->aclk);
return 0;