/** * zynqmp_pll_get_mode() - Get mode of PLL * @hw: Handle between common and hardware-specific interfaces * * Return: Mode of PLL
*/ staticinlineenum pll_mode zynqmp_pll_get_mode(struct clk_hw *hw)
{ struct zynqmp_pll *clk = to_zynqmp_pll(hw);
u32 clk_id = clk->clk_id; constchar *clk_name = clk_hw_get_name(hw);
u32 ret_payload[PAYLOAD_ARG_CNT]; int ret;
ret = zynqmp_pm_get_pll_frac_mode(clk_id, ret_payload); if (ret) {
pr_debug("%s() PLL get frac mode failed for %s, ret = %d\n",
__func__, clk_name, ret); return PLL_MODE_ERROR;
}
return ret_payload[1];
}
/** * zynqmp_pll_set_mode() - Set the PLL mode * @hw: Handle between common and hardware-specific interfaces * @on: Flag to determine the mode
*/ staticinlinevoid zynqmp_pll_set_mode(struct clk_hw *hw, bool on)
{ struct zynqmp_pll *clk = to_zynqmp_pll(hw);
u32 clk_id = clk->clk_id; constchar *clk_name = clk_hw_get_name(hw); int ret;
u32 mode;
if (on)
mode = PLL_MODE_FRAC; else
mode = PLL_MODE_INT;
ret = zynqmp_pm_set_pll_frac_mode(clk_id, mode); if (ret)
pr_debug("%s() PLL set frac mode failed for %s, ret = %d\n",
__func__, clk_name, ret); else
clk->set_pll_mode = true;
}
/** * zynqmp_pll_round_rate() - Round a clock frequency * @hw: Handle between common and hardware-specific interfaces * @rate: Desired clock frequency * @prate: Clock frequency of parent clock * * Return: Frequency closest to @rate the hardware can generate
*/ staticlong zynqmp_pll_round_rate(struct clk_hw *hw, unsignedlong rate, unsignedlong *prate)
{
u32 fbdiv;
u32 mult, div;
/* Let rate fall inside the range PS_PLL_VCO_MIN ~ PS_PLL_VCO_MAX */ if (rate > PS_PLL_VCO_MAX) {
div = DIV_ROUND_UP(rate, PS_PLL_VCO_MAX);
rate = rate / div;
} if (rate < PS_PLL_VCO_MIN) {
mult = DIV_ROUND_UP(PS_PLL_VCO_MIN, rate);
rate = rate * mult;
}
/** * zynqmp_pll_recalc_rate() - Recalculate clock frequency * @hw: Handle between common and hardware-specific interfaces * @parent_rate: Clock frequency of parent clock * * Return: Current clock frequency or 0 in case of error
*/ staticunsignedlong zynqmp_pll_recalc_rate(struct clk_hw *hw, unsignedlong parent_rate)
{ struct zynqmp_pll *clk = to_zynqmp_pll(hw);
u32 clk_id = clk->clk_id; constchar *clk_name = clk_hw_get_name(hw);
u32 fbdiv, data; unsignedlong rate, frac;
u32 ret_payload[PAYLOAD_ARG_CNT]; int ret; enum pll_mode mode;
ret = zynqmp_pm_clock_getdivider(clk_id, &fbdiv); if (ret) {
pr_debug("%s() get divider failed for %s, ret = %d\n",
__func__, clk_name, ret); return 0ul;
}
mode = zynqmp_pll_get_mode(hw); if (mode == PLL_MODE_ERROR) return 0ul;
/** * zynqmp_pll_set_rate() - Set rate of PLL * @hw: Handle between common and hardware-specific interfaces * @rate: Frequency of clock to be set * @parent_rate: Clock frequency of parent clock * * Set PLL divider to set desired rate. * * Returns: rate which is set on success else error code
*/ staticint zynqmp_pll_set_rate(struct clk_hw *hw, unsignedlong rate, unsignedlong parent_rate)
{ struct zynqmp_pll *clk = to_zynqmp_pll(hw);
u32 clk_id = clk->clk_id; constchar *clk_name = clk_hw_get_name(hw);
u32 fbdiv; long rate_div, frac, m, f; int ret;
if (f) {
m = rate_div / FRAC_DIV;
m = clamp_t(u32, m, (PLL_FBDIV_MIN), (PLL_FBDIV_MAX));
rate = parent_rate * m;
frac = (parent_rate * f) / FRAC_DIV;
ret = zynqmp_pm_clock_setdivider(clk_id, m); if (ret == -EUSERS)
WARN(1, "More than allowed devices are using the %s, which is forbidden\n",
clk_name); elseif (ret)
pr_debug("%s() set divider failed for %s, ret = %d\n",
__func__, clk_name, ret);
zynqmp_pm_set_pll_frac_data(clk_id, f);
return rate + frac;
}
fbdiv = DIV_ROUND_CLOSEST(rate, parent_rate);
fbdiv = clamp_t(u32, fbdiv, PLL_FBDIV_MIN, PLL_FBDIV_MAX);
ret = zynqmp_pm_clock_setdivider(clk_id, fbdiv); if (ret)
pr_debug("%s() set divider failed for %s, ret = %d\n",
__func__, clk_name, ret);
return parent_rate * fbdiv;
}
/** * zynqmp_pll_is_enabled() - Check if a clock is enabled * @hw: Handle between common and hardware-specific interfaces * * Return: 1 if the clock is enabled, 0 otherwise
*/ staticint zynqmp_pll_is_enabled(struct clk_hw *hw)
{ struct zynqmp_pll *clk = to_zynqmp_pll(hw); constchar *clk_name = clk_hw_get_name(hw);
u32 clk_id = clk->clk_id; unsignedint state; int ret;
ret = zynqmp_pm_clock_getstate(clk_id, &state); if (ret) {
pr_debug("%s() clock get state failed for %s, ret = %d\n",
__func__, clk_name, ret); return -EIO;
}
return state ? 1 : 0;
}
/** * zynqmp_pll_enable() - Enable clock * @hw: Handle between common and hardware-specific interfaces * * Return: 0 on success else error code
*/ staticint zynqmp_pll_enable(struct clk_hw *hw)
{ struct zynqmp_pll *clk = to_zynqmp_pll(hw); constchar *clk_name = clk_hw_get_name(hw);
u32 clk_id = clk->clk_id; int ret;
/* * Don't skip enabling clock if there is an IOCTL_SET_PLL_FRAC_MODE request * that has been sent to ATF.
*/ if (zynqmp_pll_is_enabled(hw) && (!clk->set_pll_mode)) return 0;
clk->set_pll_mode = false;
ret = zynqmp_pm_clock_enable(clk_id); if (ret)
pr_debug("%s() clock enable failed for %s, ret = %d\n",
__func__, clk_name, ret);
return ret;
}
/** * zynqmp_pll_disable() - Disable clock * @hw: Handle between common and hardware-specific interfaces
*/ staticvoid zynqmp_pll_disable(struct clk_hw *hw)
{ struct zynqmp_pll *clk = to_zynqmp_pll(hw); constchar *clk_name = clk_hw_get_name(hw);
u32 clk_id = clk->clk_id; int ret;
if (!zynqmp_pll_is_enabled(hw)) return;
ret = zynqmp_pm_clock_disable(clk_id); if (ret)
pr_debug("%s() clock disable failed for %s, ret = %d\n",
__func__, clk_name, ret);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.