/** * struct sg2042_pll_clock - PLL clock * @hw: clk_hw for initialization * @id: used to map clk_onecell_data * @base: used for readl/writel. * **NOTE**: PLL registers are all in SYS_CTRL! * @lock: spinlock to protect register access, modification * of frequency can only be served one at the time. * @offset_ctrl: offset of pll control registers * @shift_status_lock: shift of XXX_LOCK in pll status register * @shift_status_updating: shift of UPDATING_XXX in pll status register * @shift_enable: shift of XXX_CLK_EN in pll enable register
*/ struct sg2042_pll_clock { struct clk_hw hw;
/** * sg2042_pll_recalc_rate() - Calculate rate for plls * @reg_value: current register value * @parent_rate: parent frequency * * This function is used to calculate below "rate" in equation * rate = (parent_rate/REFDIV) x FBDIV/POSTDIV1/POSTDIV2 * = (parent_rate x FBDIV) / (REFDIV x POSTDIV1 x POSTDIV2) * * Return: The rate calculated.
*/ staticunsignedlong sg2042_pll_recalc_rate(unsignedint reg_value, unsignedlong parent_rate)
{ struct sg2042_pll_ctrl ctrl_table;
u64 numerator, denominator;
/** * sg2042_pll_get_postdiv_1_2() - Based on input rate/prate/fbdiv/refdiv, * look up the postdiv1_2 table to get the closest postdiiv combination. * @rate: FOUTPOSTDIV * @prate: parent rate, i.e. FREF * @fbdiv: FBDIV * @refdiv: REFDIV * @postdiv1: POSTDIV1, output * @postdiv2: POSTDIV2, output * * postdiv1_2 contains all the possible combination lists of POSTDIV1 and POSTDIV2 * for example: * postdiv1_2[0] = {2, 4, 8}, where div1 = 2, div2 = 4 , div1 * div2 = 8 * * See TRM: * FOUTPOSTDIV = FREF * FBDIV / REFDIV / (POSTDIV1 * POSTDIV2) * So we get following formula to get POSTDIV1 and POSTDIV2: * POSTDIV = (prate/REFDIV) x FBDIV/rate * above POSTDIV = POSTDIV1*POSTDIV2 * * Return: * %0 - OK * %-EINVAL - invalid argument, which means Failed to get the postdivs.
*/ staticint sg2042_pll_get_postdiv_1_2(unsignedlong rate, unsignedlong prate, unsignedint fbdiv, unsignedint refdiv, unsignedint *postdiv1, unsignedint *postdiv2)
{ int index;
u64 tmp0;
/* POSTDIV_RESULT_INDEX point to 3rd element in the array postdiv1_2 */ #define POSTDIV_RESULT_INDEX 2
/* prate/REFDIV and result save to tmp0 */
tmp0 = prate;
do_div(tmp0, refdiv);
/* ((prate/REFDIV) x FBDIV) and result save to tmp0 */
tmp0 *= fbdiv;
/* ((prate/REFDIV) x FBDIV)/rate and result save to tmp0 */
tmp0 = div64_ul(tmp0, rate);
/* tmp0 is POSTDIV1*POSTDIV2, now we calculate div1 and div2 value */ if (tmp0 <= 7) { /* (div1 * div2) <= 7, no need to use array search */
*postdiv1 = tmp0;
*postdiv2 = 1; return 0;
}
/* (div1 * div2) > 7, use array search */ for (index = 0; index < ARRAY_SIZE(postdiv1_2); index++) { if (tmp0 > postdiv1_2[index][POSTDIV_RESULT_INDEX]) { continue;
} else { /* found it */
*postdiv1 = postdiv1_2[index][1];
*postdiv2 = postdiv1_2[index][0]; return 0;
}
}
pr_warn("%s can not find in postdiv array!\n", __func__); return -EINVAL;
}
/** * sg2042_get_pll_ctl_setting() - Based on the given FOUTPISTDIV and the input * FREF to calculate the REFDIV/FBDIV/PSTDIV1/POSTDIV2 combination for pllctrl * register. * @req_rate: expected output clock rate, i.e. FOUTPISTDIV * @parent_rate: input parent clock rate, i.e. FREF * @best: output to hold calculated combination of REFDIV/FBDIV/PSTDIV1/POSTDIV2 * * Return: * %0 - OK * %-EINVAL - invalid argument
*/ staticint sg2042_get_pll_ctl_setting(struct sg2042_pll_ctrl *best, unsignedlong req_rate, unsignedlong parent_rate)
{ unsignedint fbdiv, refdiv, postdiv1, postdiv2; unsignedlong foutpostdiv;
u64 foutvco; int ret;
u64 tmp;
if (best->freq == 0) return -EINVAL; else return 0;
}
/** * sg2042_clk_pll_recalc_rate() - recalc_rate callback for pll clks * @hw: ccf use to hook get sg2042_pll_clock * @parent_rate: parent rate * * The is function will be called through clk_get_rate * and return current rate after decoding reg value * * Return: Current rate recalculated.
*/ staticunsignedlong sg2042_clk_pll_recalc_rate(struct clk_hw *hw, unsignedlong parent_rate)
{ struct sg2042_pll_clock *pll = to_sg2042_pll_clk(hw); unsignedlong rate;
u32 value;
value = readl(pll->base + pll->offset_ctrl);
rate = sg2042_pll_recalc_rate(value, parent_rate);
staticint sg2042_clk_register_plls(struct device *dev, struct sg2042_clk_data *clk_data, struct sg2042_pll_clock pll_clks[], int num_pll_clks)
{ struct sg2042_pll_clock *pll; struct clk_hw *hw; int i, ret = 0;
for (i = 0; i < num_pll_clks; i++) {
pll = &pll_clks[i]; /* assign these for ops usage during registration */
pll->base = clk_data->iobase;
pll->lock = &sg2042_clk_lock;
hw = &pll->hw;
ret = devm_clk_hw_register(dev, hw); if (ret) {
pr_err("failed to register clock %s\n", pll->hw.init->name); break;
}
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.