Loading drivers/clk/renesas/rzv2h-cpg.c +505 −7 Original line number Diff line number Diff line Loading @@ -14,9 +14,14 @@ #include <linux/bitfield.h> #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/clk/renesas.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/iopoll.h> #include <linux/limits.h> #include <linux/math.h> #include <linux/math64.h> #include <linux/minmax.h> #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of.h> Loading @@ -26,6 +31,7 @@ #include <linux/refcount.h> #include <linux/reset-controller.h> #include <linux/string_choices.h> #include <linux/units.h> #include <dt-bindings/clock/renesas-cpg-mssr.h> Loading @@ -47,13 +53,15 @@ #define CPG_PLL_STBY(x) ((x)) #define CPG_PLL_STBY_RESETB BIT(0) #define CPG_PLL_STBY_SSC_EN BIT(2) #define CPG_PLL_STBY_RESETB_WEN BIT(16) #define CPG_PLL_STBY_SSC_EN_WEN BIT(18) #define CPG_PLL_CLK1(x) ((x) + 0x004) #define CPG_PLL_CLK1_KDIV(x) ((s16)FIELD_GET(GENMASK(31, 16), (x))) #define CPG_PLL_CLK1_MDIV(x) FIELD_GET(GENMASK(15, 6), (x)) #define CPG_PLL_CLK1_PDIV(x) FIELD_GET(GENMASK(5, 0), (x)) #define CPG_PLL_CLK1_KDIV GENMASK(31, 16) #define CPG_PLL_CLK1_MDIV GENMASK(15, 6) #define CPG_PLL_CLK1_PDIV GENMASK(5, 0) #define CPG_PLL_CLK2(x) ((x) + 0x008) #define CPG_PLL_CLK2_SDIV(x) FIELD_GET(GENMASK(2, 0), (x)) #define CPG_PLL_CLK2_SDIV GENMASK(2, 0) #define CPG_PLL_MON(x) ((x) + 0x010) #define CPG_PLL_MON_RESETB BIT(0) #define CPG_PLL_MON_LOCK BIT(4) Loading @@ -65,6 +73,22 @@ #define CPG_CLKSTATUS0 (0x700) /* On RZ/G3E SoC we have two DSI PLLs */ #define MAX_CPG_DSI_PLL 2 /** * struct rzv2h_pll_dsi_info - PLL DSI information, holds the limits and parameters * * @pll_dsi_limits: PLL DSI parameters limits * @pll_dsi_parameters: Calculated PLL DSI parameters * @req_pll_dsi_rate: Requested PLL DSI rate */ struct rzv2h_pll_dsi_info { const struct rzv2h_pll_limits *pll_dsi_limits; struct rzv2h_pll_div_pars pll_dsi_parameters; unsigned long req_pll_dsi_rate; }; /** * struct rzv2h_cpg_priv - Clock Pulse Generator Private Data * Loading @@ -80,6 +104,7 @@ * @ff_mod_status_ops: Fixed Factor Module Status Clock operations * @mstop_count: Array of mstop values * @rcdev: Reset controller entity * @pll_dsi_info: Array of PLL DSI information, holds the limits and parameters */ struct rzv2h_cpg_priv { struct device *dev; Loading @@ -98,6 +123,8 @@ struct rzv2h_cpg_priv { atomic_t *mstop_count; struct reset_controller_dev rcdev; struct rzv2h_pll_dsi_info pll_dsi_info[MAX_CPG_DSI_PLL]; }; #define rcdev_to_priv(x) container_of(x, struct rzv2h_cpg_priv, rcdev) Loading Loading @@ -168,6 +195,460 @@ struct rzv2h_ff_mod_status_clk { #define to_rzv2h_ff_mod_status_clk(_hw) \ container_of(_hw, struct rzv2h_ff_mod_status_clk, fix.hw) /** * struct rzv2h_plldsi_div_clk - PLL DSI DDIV clock * * @dtable: divider table * @priv: CPG private data * @hw: divider clk * @ddiv: divider configuration */ struct rzv2h_plldsi_div_clk { const struct clk_div_table *dtable; struct rzv2h_cpg_priv *priv; struct clk_hw hw; struct ddiv ddiv; }; #define to_plldsi_div_clk(_hw) \ container_of(_hw, struct rzv2h_plldsi_div_clk, hw) #define RZ_V2H_OSC_CLK_IN_MEGA (24 * MEGA) #define RZV2H_MAX_DIV_TABLES (16) /** * rzv2h_get_pll_pars - Finds the best combination of PLL parameters * for a given frequency. * * @limits: Pointer to the structure containing the limits for the PLL parameters * @pars: Pointer to the structure where the best calculated PLL parameters values * will be stored * @freq_millihz: Target output frequency in millihertz * * This function calculates the best set of PLL parameters (M, K, P, S) to achieve * the desired frequency. * There is no direct formula to calculate the PLL parameters, as it's an open * system of equations, therefore this function uses an iterative approach to * determine the best solution. The best solution is one that minimizes the error * (desired frequency - actual frequency). * * Return: true if a valid set of parameters values is found, false otherwise. */ bool rzv2h_get_pll_pars(const struct rzv2h_pll_limits *limits, struct rzv2h_pll_pars *pars, u64 freq_millihz) { u64 fout_min_millihz = mul_u32_u32(limits->fout.min, MILLI); u64 fout_max_millihz = mul_u32_u32(limits->fout.max, MILLI); struct rzv2h_pll_pars p, best; if (freq_millihz > fout_max_millihz || freq_millihz < fout_min_millihz) return false; /* Initialize best error to maximum possible value */ best.error_millihz = S64_MAX; for (p.p = limits->p.min; p.p <= limits->p.max; p.p++) { u32 fref = RZ_V2H_OSC_CLK_IN_MEGA / p.p; u16 divider; for (divider = 1 << limits->s.min, p.s = limits->s.min; p.s <= limits->s.max; p.s++, divider <<= 1) { for (p.m = limits->m.min; p.m <= limits->m.max; p.m++) { u64 output_m, output_k_range; s64 pll_k, output_k; u64 fvco, output; /* * The frequency generated by the PLL + divider * is calculated as follows: * * With: * Freq = Ffout = Ffvco / 2^(pll_s) * Ffvco = (pll_m + (pll_k / 65536)) * Ffref * Ffref = 24MHz / pll_p * * Freq can also be rewritten as: * Freq = Ffvco / 2^(pll_s) * = ((pll_m + (pll_k / 65536)) * Ffref) / 2^(pll_s) * = (pll_m * Ffref) / 2^(pll_s) + ((pll_k / 65536) * Ffref) / 2^(pll_s) * = output_m + output_k * * Every parameter has been determined at this * point, but pll_k. * * Considering that: * limits->k.min <= pll_k <= limits->k.max * Then: * -0.5 <= (pll_k / 65536) < 0.5 * Therefore: * -Ffref / (2 * 2^(pll_s)) <= output_k < Ffref / (2 * 2^(pll_s)) */ /* Compute output M component (in mHz) */ output_m = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(p.m, fref) * MILLI, divider); /* Compute range for output K (in mHz) */ output_k_range = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(fref, MILLI), 2 * divider); /* * No point in continuing if we can't achieve * the desired frequency */ if (freq_millihz < (output_m - output_k_range) || freq_millihz >= (output_m + output_k_range)) { continue; } /* * Compute the K component * * Since: * Freq = output_m + output_k * Then: * output_k = Freq - output_m * = ((pll_k / 65536) * Ffref) / 2^(pll_s) * Therefore: * pll_k = (output_k * 65536 * 2^(pll_s)) / Ffref */ output_k = freq_millihz - output_m; pll_k = div_s64(output_k * 65536ULL * divider, fref); pll_k = DIV_S64_ROUND_CLOSEST(pll_k, MILLI); /* Validate K value within allowed limits */ if (pll_k < limits->k.min || pll_k > limits->k.max) continue; p.k = pll_k; /* Compute (Ffvco * 65536) */ fvco = mul_u32_u32(p.m * 65536 + p.k, fref); if (fvco < mul_u32_u32(limits->fvco.min, 65536) || fvco > mul_u32_u32(limits->fvco.max, 65536)) continue; /* PLL_M component of (output * 65536 * PLL_P) */ output = mul_u32_u32(p.m * 65536, RZ_V2H_OSC_CLK_IN_MEGA); /* PLL_K component of (output * 65536 * PLL_P) */ output += p.k * RZ_V2H_OSC_CLK_IN_MEGA; /* Make it in mHz */ output *= MILLI; output = DIV_U64_ROUND_CLOSEST(output, 65536 * p.p * divider); /* Check output frequency against limits */ if (output < fout_min_millihz || output > fout_max_millihz) continue; p.error_millihz = freq_millihz - output; p.freq_millihz = output; /* If an exact match is found, return immediately */ if (p.error_millihz == 0) { *pars = p; return true; } /* Update best match if error is smaller */ if (abs(best.error_millihz) > abs(p.error_millihz)) best = p; } } } /* If no valid parameters were found, return false */ if (best.error_millihz == S64_MAX) return false; *pars = best; return true; } EXPORT_SYMBOL_NS_GPL(rzv2h_get_pll_pars, "RZV2H_CPG"); /* * rzv2h_get_pll_divs_pars - Finds the best combination of PLL parameters * and divider value for a given frequency. * * @limits: Pointer to the structure containing the limits for the PLL parameters * @pars: Pointer to the structure where the best calculated PLL parameters and * divider values will be stored * @table: Pointer to the array of valid divider values * @table_size: Size of the divider values array * @freq_millihz: Target output frequency in millihertz * * This function calculates the best set of PLL parameters (M, K, P, S) and divider * value to achieve the desired frequency. See rzv2h_get_pll_pars() for more details * on how the PLL parameters are calculated. * * freq_millihz is the desired frequency generated by the PLL followed by a * a gear. */ bool rzv2h_get_pll_divs_pars(const struct rzv2h_pll_limits *limits, struct rzv2h_pll_div_pars *pars, const u8 *table, u8 table_size, u64 freq_millihz) { struct rzv2h_pll_div_pars p, best; best.div.error_millihz = S64_MAX; p.div.error_millihz = S64_MAX; for (unsigned int i = 0; i < table_size; i++) { if (!rzv2h_get_pll_pars(limits, &p.pll, freq_millihz * table[i])) continue; p.div.divider_value = table[i]; p.div.freq_millihz = DIV_U64_ROUND_CLOSEST(p.pll.freq_millihz, table[i]); p.div.error_millihz = freq_millihz - p.div.freq_millihz; if (p.div.error_millihz == 0) { *pars = p; return true; } if (abs(best.div.error_millihz) > abs(p.div.error_millihz)) best = p; } if (best.div.error_millihz == S64_MAX) return false; *pars = best; return true; } EXPORT_SYMBOL_NS_GPL(rzv2h_get_pll_divs_pars, "RZV2H_CPG"); static unsigned long rzv2h_cpg_plldsi_div_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct rzv2h_plldsi_div_clk *dsi_div = to_plldsi_div_clk(hw); struct rzv2h_cpg_priv *priv = dsi_div->priv; struct ddiv ddiv = dsi_div->ddiv; u32 div; div = readl(priv->base + ddiv.offset); div >>= ddiv.shift; div &= clk_div_mask(ddiv.width); div = dsi_div->dtable[div].div; return DIV_ROUND_CLOSEST_ULL(parent_rate, div); } static int rzv2h_cpg_plldsi_div_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { struct rzv2h_plldsi_div_clk *dsi_div = to_plldsi_div_clk(hw); struct pll_clk *pll_clk = to_pll(clk_hw_get_parent(hw)); struct rzv2h_cpg_priv *priv = dsi_div->priv; u8 table[RZV2H_MAX_DIV_TABLES] = { 0 }; struct rzv2h_pll_div_pars *dsi_params; struct rzv2h_pll_dsi_info *dsi_info; const struct clk_div_table *div; unsigned int i = 0; u64 rate_millihz; dsi_info = &priv->pll_dsi_info[pll_clk->pll.instance]; dsi_params = &dsi_info->pll_dsi_parameters; rate_millihz = mul_u32_u32(req->rate, MILLI); if (rate_millihz == dsi_params->div.error_millihz + dsi_params->div.freq_millihz) goto exit_determine_rate; for (div = dsi_div->dtable; div->div; div++) { if (i >= RZV2H_MAX_DIV_TABLES) return -EINVAL; table[i++] = div->div; } if (!rzv2h_get_pll_divs_pars(dsi_info->pll_dsi_limits, dsi_params, table, i, rate_millihz)) { dev_err(priv->dev, "failed to determine rate for req->rate: %lu\n", req->rate); return -EINVAL; } exit_determine_rate: req->rate = DIV_ROUND_CLOSEST_ULL(dsi_params->div.freq_millihz, MILLI); req->best_parent_rate = req->rate * dsi_params->div.divider_value; dsi_info->req_pll_dsi_rate = req->best_parent_rate; return 0; } static int rzv2h_cpg_plldsi_div_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct rzv2h_plldsi_div_clk *dsi_div = to_plldsi_div_clk(hw); struct pll_clk *pll_clk = to_pll(clk_hw_get_parent(hw)); struct rzv2h_cpg_priv *priv = dsi_div->priv; struct rzv2h_pll_div_pars *dsi_params; struct rzv2h_pll_dsi_info *dsi_info; struct ddiv ddiv = dsi_div->ddiv; const struct clk_div_table *clkt; bool divider_found = false; u32 val, shift; dsi_info = &priv->pll_dsi_info[pll_clk->pll.instance]; dsi_params = &dsi_info->pll_dsi_parameters; for (clkt = dsi_div->dtable; clkt->div; clkt++) { if (clkt->div == dsi_params->div.divider_value) { divider_found = true; break; } } if (!divider_found) return -EINVAL; shift = ddiv.shift; val = readl(priv->base + ddiv.offset) | DDIV_DIVCTL_WEN(shift); val &= ~(clk_div_mask(ddiv.width) << shift); val |= clkt->val << shift; writel(val, priv->base + ddiv.offset); return 0; } static const struct clk_ops rzv2h_cpg_plldsi_div_ops = { .recalc_rate = rzv2h_cpg_plldsi_div_recalc_rate, .determine_rate = rzv2h_cpg_plldsi_div_determine_rate, .set_rate = rzv2h_cpg_plldsi_div_set_rate, }; static struct clk * __init rzv2h_cpg_plldsi_div_clk_register(const struct cpg_core_clk *core, struct rzv2h_cpg_priv *priv) { struct rzv2h_plldsi_div_clk *clk_hw_data; struct clk **clks = priv->clks; struct clk_init_data init; const struct clk *parent; const char *parent_name; struct clk_hw *clk_hw; int ret; parent = clks[core->parent]; if (IS_ERR(parent)) return ERR_CAST(parent); clk_hw_data = devm_kzalloc(priv->dev, sizeof(*clk_hw_data), GFP_KERNEL); if (!clk_hw_data) return ERR_PTR(-ENOMEM); clk_hw_data->priv = priv; clk_hw_data->ddiv = core->cfg.ddiv; clk_hw_data->dtable = core->dtable; parent_name = __clk_get_name(parent); init.name = core->name; init.ops = &rzv2h_cpg_plldsi_div_ops; init.flags = core->flag; init.parent_names = &parent_name; init.num_parents = 1; clk_hw = &clk_hw_data->hw; clk_hw->init = &init; ret = devm_clk_hw_register(priv->dev, clk_hw); if (ret) return ERR_PTR(ret); return clk_hw->clk; } static int rzv2h_cpg_plldsi_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { struct pll_clk *pll_clk = to_pll(hw); struct rzv2h_cpg_priv *priv = pll_clk->priv; struct rzv2h_pll_dsi_info *dsi_info; u64 rate_millihz; dsi_info = &priv->pll_dsi_info[pll_clk->pll.instance]; /* check if the divider has already invoked the algorithm */ if (req->rate == dsi_info->req_pll_dsi_rate) return 0; /* If the req->rate doesn't match we do the calculation assuming there is no divider */ rate_millihz = mul_u32_u32(req->rate, MILLI); if (!rzv2h_get_pll_pars(dsi_info->pll_dsi_limits, &dsi_info->pll_dsi_parameters.pll, rate_millihz)) { dev_err(priv->dev, "failed to determine rate for req->rate: %lu\n", req->rate); return -EINVAL; } req->rate = DIV_ROUND_CLOSEST_ULL(dsi_info->pll_dsi_parameters.pll.freq_millihz, MILLI); dsi_info->req_pll_dsi_rate = req->rate; return 0; } static int rzv2h_cpg_pll_set_rate(struct pll_clk *pll_clk, struct rzv2h_pll_pars *params, bool ssc_disable) { struct rzv2h_cpg_priv *priv = pll_clk->priv; u16 offset = pll_clk->pll.offset; u32 val; int ret; /* Put PLL into standby mode */ writel(CPG_PLL_STBY_RESETB_WEN, priv->base + CPG_PLL_STBY(offset)); ret = readl_poll_timeout_atomic(priv->base + CPG_PLL_MON(offset), val, !(val & CPG_PLL_MON_LOCK), 100, 2000); if (ret) { dev_err(priv->dev, "Failed to put PLLDSI into standby mode"); return ret; } /* Output clock setting 1 */ writel(FIELD_PREP(CPG_PLL_CLK1_KDIV, (u16)params->k) | FIELD_PREP(CPG_PLL_CLK1_MDIV, params->m) | FIELD_PREP(CPG_PLL_CLK1_PDIV, params->p), priv->base + CPG_PLL_CLK1(offset)); /* Output clock setting 2 */ val = readl(priv->base + CPG_PLL_CLK2(offset)); writel((val & ~CPG_PLL_CLK2_SDIV) | FIELD_PREP(CPG_PLL_CLK2_SDIV, params->s), priv->base + CPG_PLL_CLK2(offset)); /* Put PLL to normal mode */ if (ssc_disable) val = CPG_PLL_STBY_SSC_EN_WEN; else val = CPG_PLL_STBY_SSC_EN_WEN | CPG_PLL_STBY_SSC_EN; writel(val | CPG_PLL_STBY_RESETB_WEN | CPG_PLL_STBY_RESETB, priv->base + CPG_PLL_STBY(offset)); /* PLL normal mode transition, output clock stability check */ ret = readl_poll_timeout_atomic(priv->base + CPG_PLL_MON(offset), val, (val & CPG_PLL_MON_LOCK), 100, 2000); if (ret) { dev_err(priv->dev, "Failed to put PLLDSI into normal mode"); return ret; } return 0; } static int rzv2h_cpg_plldsi_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct pll_clk *pll_clk = to_pll(hw); struct rzv2h_pll_dsi_info *dsi_info; struct rzv2h_cpg_priv *priv = pll_clk->priv; dsi_info = &priv->pll_dsi_info[pll_clk->pll.instance]; return rzv2h_cpg_pll_set_rate(pll_clk, &dsi_info->pll_dsi_parameters.pll, true); } static int rzv2h_cpg_pll_clk_is_enabled(struct clk_hw *hw) { struct pll_clk *pll_clk = to_pll(hw); Loading Loading @@ -231,12 +712,19 @@ static unsigned long rzv2h_cpg_pll_clk_recalc_rate(struct clk_hw *hw, clk1 = readl(priv->base + CPG_PLL_CLK1(pll.offset)); clk2 = readl(priv->base + CPG_PLL_CLK2(pll.offset)); rate = mul_u64_u32_shr(parent_rate, (CPG_PLL_CLK1_MDIV(clk1) << 16) + CPG_PLL_CLK1_KDIV(clk1), 16 + CPG_PLL_CLK2_SDIV(clk2)); rate = mul_u64_u32_shr(parent_rate, (FIELD_GET(CPG_PLL_CLK1_MDIV, clk1) << 16) + (s16)FIELD_GET(CPG_PLL_CLK1_KDIV, clk1), 16 + FIELD_GET(CPG_PLL_CLK2_SDIV, clk2)); return DIV_ROUND_CLOSEST_ULL(rate, CPG_PLL_CLK1_PDIV(clk1)); return DIV_ROUND_CLOSEST_ULL(rate, FIELD_GET(CPG_PLL_CLK1_PDIV, clk1)); } static const struct clk_ops rzv2h_cpg_plldsi_ops = { .recalc_rate = rzv2h_cpg_pll_clk_recalc_rate, .determine_rate = rzv2h_cpg_plldsi_determine_rate, .set_rate = rzv2h_cpg_plldsi_set_rate, }; static const struct clk_ops rzv2h_cpg_pll_ops = { .is_enabled = rzv2h_cpg_pll_clk_is_enabled, .enable = rzv2h_cpg_pll_clk_enable, Loading @@ -263,6 +751,10 @@ rzv2h_cpg_pll_clk_register(const struct cpg_core_clk *core, if (!pll_clk) return ERR_PTR(-ENOMEM); if (core->type == CLK_TYPE_PLLDSI) priv->pll_dsi_info[core->cfg.pll.instance].pll_dsi_limits = core->cfg.pll.limits; parent_name = __clk_get_name(parent); init.name = core->name; init.ops = ops; Loading Loading @@ -587,6 +1079,12 @@ rzv2h_cpg_register_core_clk(const struct cpg_core_clk *core, case CLK_TYPE_SMUX: clk = rzv2h_cpg_mux_clk_register(core, priv); break; case CLK_TYPE_PLLDSI: clk = rzv2h_cpg_pll_clk_register(core, priv, &rzv2h_cpg_plldsi_ops); break; case CLK_TYPE_PLLDSI_DIV: clk = rzv2h_cpg_plldsi_div_clk_register(core, priv); break; default: goto fail; } Loading drivers/clk/renesas/rzv2h-cpg.h +22 −4 Original line number Diff line number Diff line Loading @@ -16,20 +16,28 @@ * * @offset: STBY register offset * @has_clkn: Flag to indicate if CLK1/2 are accessible or not * @instance: PLL instance number */ struct pll { unsigned int offset:9; unsigned int has_clkn:1; unsigned int instance:2; const struct rzv2h_pll_limits *limits; }; #define PLL_PACK(_offset, _has_clkn) \ #define PLL_PACK_LIMITS(_offset, _has_clkn, _instance, _limits) \ ((struct pll){ \ .offset = _offset, \ .has_clkn = _has_clkn \ .has_clkn = _has_clkn, \ .instance = _instance, \ .limits = _limits \ }) #define PLLCA55 PLL_PACK(0x60, 1) #define PLLGPU PLL_PACK(0x120, 1) #define PLL_PACK(_offset, _has_clkn, _instance) \ PLL_PACK_LIMITS(_offset, _has_clkn, _instance, NULL) #define PLLCA55 PLL_PACK(0x60, 1, 0) #define PLLGPU PLL_PACK(0x120, 1, 0) /** * struct ddiv - Structure for dynamic switching divider Loading Loading @@ -190,6 +198,8 @@ enum clk_types { CLK_TYPE_PLL, CLK_TYPE_DDIV, /* Dynamic Switching Divider */ CLK_TYPE_SMUX, /* Static Mux */ CLK_TYPE_PLLDSI, /* PLLDSI */ CLK_TYPE_PLLDSI_DIV, /* PLLDSI divider */ }; #define DEF_TYPE(_name, _id, _type...) \ Loading Loading @@ -220,6 +230,14 @@ enum clk_types { .num_parents = ARRAY_SIZE(_parent_names), \ .flag = CLK_SET_RATE_PARENT, \ .mux_flags = CLK_MUX_HIWORD_MASK) #define DEF_PLLDSI(_name, _id, _parent, _pll_packed) \ DEF_TYPE(_name, _id, CLK_TYPE_PLLDSI, .parent = _parent, .cfg.pll = _pll_packed) #define DEF_PLLDSI_DIV(_name, _id, _parent, _ddiv_packed, _dtable) \ DEF_TYPE(_name, _id, CLK_TYPE_PLLDSI_DIV, \ .cfg.ddiv = _ddiv_packed, \ .dtable = _dtable, \ .parent = _parent, \ .flag = CLK_SET_RATE_PARENT) /** * struct rzv2h_mod_clk - Module Clocks definitions Loading include/linux/clk/renesas.h +145 −0 Original line number Diff line number Diff line Loading @@ -10,7 +10,9 @@ #ifndef __LINUX_CLK_RENESAS_H_ #define __LINUX_CLK_RENESAS_H_ #include <linux/clk-provider.h> #include <linux/types.h> #include <linux/units.h> struct device; struct device_node; Loading @@ -32,4 +34,147 @@ void cpg_mssr_detach_dev(struct generic_pm_domain *unused, struct device *dev); #define cpg_mssr_attach_dev NULL #define cpg_mssr_detach_dev NULL #endif /** * struct rzv2h_pll_limits - PLL parameter constraints * * This structure defines the minimum and maximum allowed values for * various parameters used to configure a PLL. These limits ensure * the PLL operates within valid and stable ranges. * * @fout: Output frequency range (in MHz) * @fout.min: Minimum allowed output frequency * @fout.max: Maximum allowed output frequency * * @fvco: PLL oscillation frequency range (in MHz) * @fvco.min: Minimum allowed VCO frequency * @fvco.max: Maximum allowed VCO frequency * * @m: Main-divider range * @m.min: Minimum main-divider value * @m.max: Maximum main-divider value * * @p: Pre-divider range * @p.min: Minimum pre-divider value * @p.max: Maximum pre-divider value * * @s: Divider range * @s.min: Minimum divider value * @s.max: Maximum divider value * * @k: Delta-sigma modulator range (signed) * @k.min: Minimum delta-sigma value * @k.max: Maximum delta-sigma value */ struct rzv2h_pll_limits { struct { u32 min; u32 max; } fout; struct { u32 min; u32 max; } fvco; struct { u16 min; u16 max; } m; struct { u8 min; u8 max; } p; struct { u8 min; u8 max; } s; struct { s16 min; s16 max; } k; }; /** * struct rzv2h_pll_pars - PLL configuration parameters * * This structure contains the configuration parameters for the * Phase-Locked Loop (PLL), used to achieve a specific output frequency. * * @m: Main divider value * @p: Pre-divider value * @s: Output divider value * @k: Delta-sigma modulation value * @freq_millihz: Calculated PLL output frequency in millihertz * @error_millihz: Frequency error from target in millihertz (signed) */ struct rzv2h_pll_pars { u16 m; u8 p; u8 s; s16 k; u64 freq_millihz; s64 error_millihz; }; /** * struct rzv2h_pll_div_pars - PLL parameters with post-divider * * This structure is used for PLLs that include an additional post-divider * stage after the main PLL block. It contains both the PLL configuration * parameters and the resulting frequency/error values after the divider. * * @pll: Main PLL configuration parameters (see struct rzv2h_pll_pars) * * @div: Post-divider configuration and result * @div.divider_value: Divider applied to the PLL output * @div.freq_millihz: Output frequency after divider in millihertz * @div.error_millihz: Frequency error from target in millihertz (signed) */ struct rzv2h_pll_div_pars { struct rzv2h_pll_pars pll; struct { u8 divider_value; u64 freq_millihz; s64 error_millihz; } div; }; #define RZV2H_CPG_PLL_DSI_LIMITS(name) \ static const struct rzv2h_pll_limits (name) = { \ .fout = { .min = 25 * MEGA, .max = 375 * MEGA }, \ .fvco = { .min = 1600 * MEGA, .max = 3200 * MEGA }, \ .m = { .min = 64, .max = 533 }, \ .p = { .min = 1, .max = 4 }, \ .s = { .min = 0, .max = 6 }, \ .k = { .min = -32768, .max = 32767 }, \ } \ #ifdef CONFIG_CLK_RZV2H bool rzv2h_get_pll_pars(const struct rzv2h_pll_limits *limits, struct rzv2h_pll_pars *pars, u64 freq_millihz); bool rzv2h_get_pll_divs_pars(const struct rzv2h_pll_limits *limits, struct rzv2h_pll_div_pars *pars, const u8 *table, u8 table_size, u64 freq_millihz); #else static inline bool rzv2h_get_pll_pars(const struct rzv2h_pll_limits *limits, struct rzv2h_pll_pars *pars, u64 freq_millihz) { return false; } static inline bool rzv2h_get_pll_divs_pars(const struct rzv2h_pll_limits *limits, struct rzv2h_pll_div_pars *pars, const u8 *table, u8 table_size, u64 freq_millihz) { return false; } #endif #endif Loading
drivers/clk/renesas/rzv2h-cpg.c +505 −7 Original line number Diff line number Diff line Loading @@ -14,9 +14,14 @@ #include <linux/bitfield.h> #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/clk/renesas.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/iopoll.h> #include <linux/limits.h> #include <linux/math.h> #include <linux/math64.h> #include <linux/minmax.h> #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of.h> Loading @@ -26,6 +31,7 @@ #include <linux/refcount.h> #include <linux/reset-controller.h> #include <linux/string_choices.h> #include <linux/units.h> #include <dt-bindings/clock/renesas-cpg-mssr.h> Loading @@ -47,13 +53,15 @@ #define CPG_PLL_STBY(x) ((x)) #define CPG_PLL_STBY_RESETB BIT(0) #define CPG_PLL_STBY_SSC_EN BIT(2) #define CPG_PLL_STBY_RESETB_WEN BIT(16) #define CPG_PLL_STBY_SSC_EN_WEN BIT(18) #define CPG_PLL_CLK1(x) ((x) + 0x004) #define CPG_PLL_CLK1_KDIV(x) ((s16)FIELD_GET(GENMASK(31, 16), (x))) #define CPG_PLL_CLK1_MDIV(x) FIELD_GET(GENMASK(15, 6), (x)) #define CPG_PLL_CLK1_PDIV(x) FIELD_GET(GENMASK(5, 0), (x)) #define CPG_PLL_CLK1_KDIV GENMASK(31, 16) #define CPG_PLL_CLK1_MDIV GENMASK(15, 6) #define CPG_PLL_CLK1_PDIV GENMASK(5, 0) #define CPG_PLL_CLK2(x) ((x) + 0x008) #define CPG_PLL_CLK2_SDIV(x) FIELD_GET(GENMASK(2, 0), (x)) #define CPG_PLL_CLK2_SDIV GENMASK(2, 0) #define CPG_PLL_MON(x) ((x) + 0x010) #define CPG_PLL_MON_RESETB BIT(0) #define CPG_PLL_MON_LOCK BIT(4) Loading @@ -65,6 +73,22 @@ #define CPG_CLKSTATUS0 (0x700) /* On RZ/G3E SoC we have two DSI PLLs */ #define MAX_CPG_DSI_PLL 2 /** * struct rzv2h_pll_dsi_info - PLL DSI information, holds the limits and parameters * * @pll_dsi_limits: PLL DSI parameters limits * @pll_dsi_parameters: Calculated PLL DSI parameters * @req_pll_dsi_rate: Requested PLL DSI rate */ struct rzv2h_pll_dsi_info { const struct rzv2h_pll_limits *pll_dsi_limits; struct rzv2h_pll_div_pars pll_dsi_parameters; unsigned long req_pll_dsi_rate; }; /** * struct rzv2h_cpg_priv - Clock Pulse Generator Private Data * Loading @@ -80,6 +104,7 @@ * @ff_mod_status_ops: Fixed Factor Module Status Clock operations * @mstop_count: Array of mstop values * @rcdev: Reset controller entity * @pll_dsi_info: Array of PLL DSI information, holds the limits and parameters */ struct rzv2h_cpg_priv { struct device *dev; Loading @@ -98,6 +123,8 @@ struct rzv2h_cpg_priv { atomic_t *mstop_count; struct reset_controller_dev rcdev; struct rzv2h_pll_dsi_info pll_dsi_info[MAX_CPG_DSI_PLL]; }; #define rcdev_to_priv(x) container_of(x, struct rzv2h_cpg_priv, rcdev) Loading Loading @@ -168,6 +195,460 @@ struct rzv2h_ff_mod_status_clk { #define to_rzv2h_ff_mod_status_clk(_hw) \ container_of(_hw, struct rzv2h_ff_mod_status_clk, fix.hw) /** * struct rzv2h_plldsi_div_clk - PLL DSI DDIV clock * * @dtable: divider table * @priv: CPG private data * @hw: divider clk * @ddiv: divider configuration */ struct rzv2h_plldsi_div_clk { const struct clk_div_table *dtable; struct rzv2h_cpg_priv *priv; struct clk_hw hw; struct ddiv ddiv; }; #define to_plldsi_div_clk(_hw) \ container_of(_hw, struct rzv2h_plldsi_div_clk, hw) #define RZ_V2H_OSC_CLK_IN_MEGA (24 * MEGA) #define RZV2H_MAX_DIV_TABLES (16) /** * rzv2h_get_pll_pars - Finds the best combination of PLL parameters * for a given frequency. * * @limits: Pointer to the structure containing the limits for the PLL parameters * @pars: Pointer to the structure where the best calculated PLL parameters values * will be stored * @freq_millihz: Target output frequency in millihertz * * This function calculates the best set of PLL parameters (M, K, P, S) to achieve * the desired frequency. * There is no direct formula to calculate the PLL parameters, as it's an open * system of equations, therefore this function uses an iterative approach to * determine the best solution. The best solution is one that minimizes the error * (desired frequency - actual frequency). * * Return: true if a valid set of parameters values is found, false otherwise. */ bool rzv2h_get_pll_pars(const struct rzv2h_pll_limits *limits, struct rzv2h_pll_pars *pars, u64 freq_millihz) { u64 fout_min_millihz = mul_u32_u32(limits->fout.min, MILLI); u64 fout_max_millihz = mul_u32_u32(limits->fout.max, MILLI); struct rzv2h_pll_pars p, best; if (freq_millihz > fout_max_millihz || freq_millihz < fout_min_millihz) return false; /* Initialize best error to maximum possible value */ best.error_millihz = S64_MAX; for (p.p = limits->p.min; p.p <= limits->p.max; p.p++) { u32 fref = RZ_V2H_OSC_CLK_IN_MEGA / p.p; u16 divider; for (divider = 1 << limits->s.min, p.s = limits->s.min; p.s <= limits->s.max; p.s++, divider <<= 1) { for (p.m = limits->m.min; p.m <= limits->m.max; p.m++) { u64 output_m, output_k_range; s64 pll_k, output_k; u64 fvco, output; /* * The frequency generated by the PLL + divider * is calculated as follows: * * With: * Freq = Ffout = Ffvco / 2^(pll_s) * Ffvco = (pll_m + (pll_k / 65536)) * Ffref * Ffref = 24MHz / pll_p * * Freq can also be rewritten as: * Freq = Ffvco / 2^(pll_s) * = ((pll_m + (pll_k / 65536)) * Ffref) / 2^(pll_s) * = (pll_m * Ffref) / 2^(pll_s) + ((pll_k / 65536) * Ffref) / 2^(pll_s) * = output_m + output_k * * Every parameter has been determined at this * point, but pll_k. * * Considering that: * limits->k.min <= pll_k <= limits->k.max * Then: * -0.5 <= (pll_k / 65536) < 0.5 * Therefore: * -Ffref / (2 * 2^(pll_s)) <= output_k < Ffref / (2 * 2^(pll_s)) */ /* Compute output M component (in mHz) */ output_m = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(p.m, fref) * MILLI, divider); /* Compute range for output K (in mHz) */ output_k_range = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(fref, MILLI), 2 * divider); /* * No point in continuing if we can't achieve * the desired frequency */ if (freq_millihz < (output_m - output_k_range) || freq_millihz >= (output_m + output_k_range)) { continue; } /* * Compute the K component * * Since: * Freq = output_m + output_k * Then: * output_k = Freq - output_m * = ((pll_k / 65536) * Ffref) / 2^(pll_s) * Therefore: * pll_k = (output_k * 65536 * 2^(pll_s)) / Ffref */ output_k = freq_millihz - output_m; pll_k = div_s64(output_k * 65536ULL * divider, fref); pll_k = DIV_S64_ROUND_CLOSEST(pll_k, MILLI); /* Validate K value within allowed limits */ if (pll_k < limits->k.min || pll_k > limits->k.max) continue; p.k = pll_k; /* Compute (Ffvco * 65536) */ fvco = mul_u32_u32(p.m * 65536 + p.k, fref); if (fvco < mul_u32_u32(limits->fvco.min, 65536) || fvco > mul_u32_u32(limits->fvco.max, 65536)) continue; /* PLL_M component of (output * 65536 * PLL_P) */ output = mul_u32_u32(p.m * 65536, RZ_V2H_OSC_CLK_IN_MEGA); /* PLL_K component of (output * 65536 * PLL_P) */ output += p.k * RZ_V2H_OSC_CLK_IN_MEGA; /* Make it in mHz */ output *= MILLI; output = DIV_U64_ROUND_CLOSEST(output, 65536 * p.p * divider); /* Check output frequency against limits */ if (output < fout_min_millihz || output > fout_max_millihz) continue; p.error_millihz = freq_millihz - output; p.freq_millihz = output; /* If an exact match is found, return immediately */ if (p.error_millihz == 0) { *pars = p; return true; } /* Update best match if error is smaller */ if (abs(best.error_millihz) > abs(p.error_millihz)) best = p; } } } /* If no valid parameters were found, return false */ if (best.error_millihz == S64_MAX) return false; *pars = best; return true; } EXPORT_SYMBOL_NS_GPL(rzv2h_get_pll_pars, "RZV2H_CPG"); /* * rzv2h_get_pll_divs_pars - Finds the best combination of PLL parameters * and divider value for a given frequency. * * @limits: Pointer to the structure containing the limits for the PLL parameters * @pars: Pointer to the structure where the best calculated PLL parameters and * divider values will be stored * @table: Pointer to the array of valid divider values * @table_size: Size of the divider values array * @freq_millihz: Target output frequency in millihertz * * This function calculates the best set of PLL parameters (M, K, P, S) and divider * value to achieve the desired frequency. See rzv2h_get_pll_pars() for more details * on how the PLL parameters are calculated. * * freq_millihz is the desired frequency generated by the PLL followed by a * a gear. */ bool rzv2h_get_pll_divs_pars(const struct rzv2h_pll_limits *limits, struct rzv2h_pll_div_pars *pars, const u8 *table, u8 table_size, u64 freq_millihz) { struct rzv2h_pll_div_pars p, best; best.div.error_millihz = S64_MAX; p.div.error_millihz = S64_MAX; for (unsigned int i = 0; i < table_size; i++) { if (!rzv2h_get_pll_pars(limits, &p.pll, freq_millihz * table[i])) continue; p.div.divider_value = table[i]; p.div.freq_millihz = DIV_U64_ROUND_CLOSEST(p.pll.freq_millihz, table[i]); p.div.error_millihz = freq_millihz - p.div.freq_millihz; if (p.div.error_millihz == 0) { *pars = p; return true; } if (abs(best.div.error_millihz) > abs(p.div.error_millihz)) best = p; } if (best.div.error_millihz == S64_MAX) return false; *pars = best; return true; } EXPORT_SYMBOL_NS_GPL(rzv2h_get_pll_divs_pars, "RZV2H_CPG"); static unsigned long rzv2h_cpg_plldsi_div_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct rzv2h_plldsi_div_clk *dsi_div = to_plldsi_div_clk(hw); struct rzv2h_cpg_priv *priv = dsi_div->priv; struct ddiv ddiv = dsi_div->ddiv; u32 div; div = readl(priv->base + ddiv.offset); div >>= ddiv.shift; div &= clk_div_mask(ddiv.width); div = dsi_div->dtable[div].div; return DIV_ROUND_CLOSEST_ULL(parent_rate, div); } static int rzv2h_cpg_plldsi_div_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { struct rzv2h_plldsi_div_clk *dsi_div = to_plldsi_div_clk(hw); struct pll_clk *pll_clk = to_pll(clk_hw_get_parent(hw)); struct rzv2h_cpg_priv *priv = dsi_div->priv; u8 table[RZV2H_MAX_DIV_TABLES] = { 0 }; struct rzv2h_pll_div_pars *dsi_params; struct rzv2h_pll_dsi_info *dsi_info; const struct clk_div_table *div; unsigned int i = 0; u64 rate_millihz; dsi_info = &priv->pll_dsi_info[pll_clk->pll.instance]; dsi_params = &dsi_info->pll_dsi_parameters; rate_millihz = mul_u32_u32(req->rate, MILLI); if (rate_millihz == dsi_params->div.error_millihz + dsi_params->div.freq_millihz) goto exit_determine_rate; for (div = dsi_div->dtable; div->div; div++) { if (i >= RZV2H_MAX_DIV_TABLES) return -EINVAL; table[i++] = div->div; } if (!rzv2h_get_pll_divs_pars(dsi_info->pll_dsi_limits, dsi_params, table, i, rate_millihz)) { dev_err(priv->dev, "failed to determine rate for req->rate: %lu\n", req->rate); return -EINVAL; } exit_determine_rate: req->rate = DIV_ROUND_CLOSEST_ULL(dsi_params->div.freq_millihz, MILLI); req->best_parent_rate = req->rate * dsi_params->div.divider_value; dsi_info->req_pll_dsi_rate = req->best_parent_rate; return 0; } static int rzv2h_cpg_plldsi_div_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct rzv2h_plldsi_div_clk *dsi_div = to_plldsi_div_clk(hw); struct pll_clk *pll_clk = to_pll(clk_hw_get_parent(hw)); struct rzv2h_cpg_priv *priv = dsi_div->priv; struct rzv2h_pll_div_pars *dsi_params; struct rzv2h_pll_dsi_info *dsi_info; struct ddiv ddiv = dsi_div->ddiv; const struct clk_div_table *clkt; bool divider_found = false; u32 val, shift; dsi_info = &priv->pll_dsi_info[pll_clk->pll.instance]; dsi_params = &dsi_info->pll_dsi_parameters; for (clkt = dsi_div->dtable; clkt->div; clkt++) { if (clkt->div == dsi_params->div.divider_value) { divider_found = true; break; } } if (!divider_found) return -EINVAL; shift = ddiv.shift; val = readl(priv->base + ddiv.offset) | DDIV_DIVCTL_WEN(shift); val &= ~(clk_div_mask(ddiv.width) << shift); val |= clkt->val << shift; writel(val, priv->base + ddiv.offset); return 0; } static const struct clk_ops rzv2h_cpg_plldsi_div_ops = { .recalc_rate = rzv2h_cpg_plldsi_div_recalc_rate, .determine_rate = rzv2h_cpg_plldsi_div_determine_rate, .set_rate = rzv2h_cpg_plldsi_div_set_rate, }; static struct clk * __init rzv2h_cpg_plldsi_div_clk_register(const struct cpg_core_clk *core, struct rzv2h_cpg_priv *priv) { struct rzv2h_plldsi_div_clk *clk_hw_data; struct clk **clks = priv->clks; struct clk_init_data init; const struct clk *parent; const char *parent_name; struct clk_hw *clk_hw; int ret; parent = clks[core->parent]; if (IS_ERR(parent)) return ERR_CAST(parent); clk_hw_data = devm_kzalloc(priv->dev, sizeof(*clk_hw_data), GFP_KERNEL); if (!clk_hw_data) return ERR_PTR(-ENOMEM); clk_hw_data->priv = priv; clk_hw_data->ddiv = core->cfg.ddiv; clk_hw_data->dtable = core->dtable; parent_name = __clk_get_name(parent); init.name = core->name; init.ops = &rzv2h_cpg_plldsi_div_ops; init.flags = core->flag; init.parent_names = &parent_name; init.num_parents = 1; clk_hw = &clk_hw_data->hw; clk_hw->init = &init; ret = devm_clk_hw_register(priv->dev, clk_hw); if (ret) return ERR_PTR(ret); return clk_hw->clk; } static int rzv2h_cpg_plldsi_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { struct pll_clk *pll_clk = to_pll(hw); struct rzv2h_cpg_priv *priv = pll_clk->priv; struct rzv2h_pll_dsi_info *dsi_info; u64 rate_millihz; dsi_info = &priv->pll_dsi_info[pll_clk->pll.instance]; /* check if the divider has already invoked the algorithm */ if (req->rate == dsi_info->req_pll_dsi_rate) return 0; /* If the req->rate doesn't match we do the calculation assuming there is no divider */ rate_millihz = mul_u32_u32(req->rate, MILLI); if (!rzv2h_get_pll_pars(dsi_info->pll_dsi_limits, &dsi_info->pll_dsi_parameters.pll, rate_millihz)) { dev_err(priv->dev, "failed to determine rate for req->rate: %lu\n", req->rate); return -EINVAL; } req->rate = DIV_ROUND_CLOSEST_ULL(dsi_info->pll_dsi_parameters.pll.freq_millihz, MILLI); dsi_info->req_pll_dsi_rate = req->rate; return 0; } static int rzv2h_cpg_pll_set_rate(struct pll_clk *pll_clk, struct rzv2h_pll_pars *params, bool ssc_disable) { struct rzv2h_cpg_priv *priv = pll_clk->priv; u16 offset = pll_clk->pll.offset; u32 val; int ret; /* Put PLL into standby mode */ writel(CPG_PLL_STBY_RESETB_WEN, priv->base + CPG_PLL_STBY(offset)); ret = readl_poll_timeout_atomic(priv->base + CPG_PLL_MON(offset), val, !(val & CPG_PLL_MON_LOCK), 100, 2000); if (ret) { dev_err(priv->dev, "Failed to put PLLDSI into standby mode"); return ret; } /* Output clock setting 1 */ writel(FIELD_PREP(CPG_PLL_CLK1_KDIV, (u16)params->k) | FIELD_PREP(CPG_PLL_CLK1_MDIV, params->m) | FIELD_PREP(CPG_PLL_CLK1_PDIV, params->p), priv->base + CPG_PLL_CLK1(offset)); /* Output clock setting 2 */ val = readl(priv->base + CPG_PLL_CLK2(offset)); writel((val & ~CPG_PLL_CLK2_SDIV) | FIELD_PREP(CPG_PLL_CLK2_SDIV, params->s), priv->base + CPG_PLL_CLK2(offset)); /* Put PLL to normal mode */ if (ssc_disable) val = CPG_PLL_STBY_SSC_EN_WEN; else val = CPG_PLL_STBY_SSC_EN_WEN | CPG_PLL_STBY_SSC_EN; writel(val | CPG_PLL_STBY_RESETB_WEN | CPG_PLL_STBY_RESETB, priv->base + CPG_PLL_STBY(offset)); /* PLL normal mode transition, output clock stability check */ ret = readl_poll_timeout_atomic(priv->base + CPG_PLL_MON(offset), val, (val & CPG_PLL_MON_LOCK), 100, 2000); if (ret) { dev_err(priv->dev, "Failed to put PLLDSI into normal mode"); return ret; } return 0; } static int rzv2h_cpg_plldsi_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct pll_clk *pll_clk = to_pll(hw); struct rzv2h_pll_dsi_info *dsi_info; struct rzv2h_cpg_priv *priv = pll_clk->priv; dsi_info = &priv->pll_dsi_info[pll_clk->pll.instance]; return rzv2h_cpg_pll_set_rate(pll_clk, &dsi_info->pll_dsi_parameters.pll, true); } static int rzv2h_cpg_pll_clk_is_enabled(struct clk_hw *hw) { struct pll_clk *pll_clk = to_pll(hw); Loading Loading @@ -231,12 +712,19 @@ static unsigned long rzv2h_cpg_pll_clk_recalc_rate(struct clk_hw *hw, clk1 = readl(priv->base + CPG_PLL_CLK1(pll.offset)); clk2 = readl(priv->base + CPG_PLL_CLK2(pll.offset)); rate = mul_u64_u32_shr(parent_rate, (CPG_PLL_CLK1_MDIV(clk1) << 16) + CPG_PLL_CLK1_KDIV(clk1), 16 + CPG_PLL_CLK2_SDIV(clk2)); rate = mul_u64_u32_shr(parent_rate, (FIELD_GET(CPG_PLL_CLK1_MDIV, clk1) << 16) + (s16)FIELD_GET(CPG_PLL_CLK1_KDIV, clk1), 16 + FIELD_GET(CPG_PLL_CLK2_SDIV, clk2)); return DIV_ROUND_CLOSEST_ULL(rate, CPG_PLL_CLK1_PDIV(clk1)); return DIV_ROUND_CLOSEST_ULL(rate, FIELD_GET(CPG_PLL_CLK1_PDIV, clk1)); } static const struct clk_ops rzv2h_cpg_plldsi_ops = { .recalc_rate = rzv2h_cpg_pll_clk_recalc_rate, .determine_rate = rzv2h_cpg_plldsi_determine_rate, .set_rate = rzv2h_cpg_plldsi_set_rate, }; static const struct clk_ops rzv2h_cpg_pll_ops = { .is_enabled = rzv2h_cpg_pll_clk_is_enabled, .enable = rzv2h_cpg_pll_clk_enable, Loading @@ -263,6 +751,10 @@ rzv2h_cpg_pll_clk_register(const struct cpg_core_clk *core, if (!pll_clk) return ERR_PTR(-ENOMEM); if (core->type == CLK_TYPE_PLLDSI) priv->pll_dsi_info[core->cfg.pll.instance].pll_dsi_limits = core->cfg.pll.limits; parent_name = __clk_get_name(parent); init.name = core->name; init.ops = ops; Loading Loading @@ -587,6 +1079,12 @@ rzv2h_cpg_register_core_clk(const struct cpg_core_clk *core, case CLK_TYPE_SMUX: clk = rzv2h_cpg_mux_clk_register(core, priv); break; case CLK_TYPE_PLLDSI: clk = rzv2h_cpg_pll_clk_register(core, priv, &rzv2h_cpg_plldsi_ops); break; case CLK_TYPE_PLLDSI_DIV: clk = rzv2h_cpg_plldsi_div_clk_register(core, priv); break; default: goto fail; } Loading
drivers/clk/renesas/rzv2h-cpg.h +22 −4 Original line number Diff line number Diff line Loading @@ -16,20 +16,28 @@ * * @offset: STBY register offset * @has_clkn: Flag to indicate if CLK1/2 are accessible or not * @instance: PLL instance number */ struct pll { unsigned int offset:9; unsigned int has_clkn:1; unsigned int instance:2; const struct rzv2h_pll_limits *limits; }; #define PLL_PACK(_offset, _has_clkn) \ #define PLL_PACK_LIMITS(_offset, _has_clkn, _instance, _limits) \ ((struct pll){ \ .offset = _offset, \ .has_clkn = _has_clkn \ .has_clkn = _has_clkn, \ .instance = _instance, \ .limits = _limits \ }) #define PLLCA55 PLL_PACK(0x60, 1) #define PLLGPU PLL_PACK(0x120, 1) #define PLL_PACK(_offset, _has_clkn, _instance) \ PLL_PACK_LIMITS(_offset, _has_clkn, _instance, NULL) #define PLLCA55 PLL_PACK(0x60, 1, 0) #define PLLGPU PLL_PACK(0x120, 1, 0) /** * struct ddiv - Structure for dynamic switching divider Loading Loading @@ -190,6 +198,8 @@ enum clk_types { CLK_TYPE_PLL, CLK_TYPE_DDIV, /* Dynamic Switching Divider */ CLK_TYPE_SMUX, /* Static Mux */ CLK_TYPE_PLLDSI, /* PLLDSI */ CLK_TYPE_PLLDSI_DIV, /* PLLDSI divider */ }; #define DEF_TYPE(_name, _id, _type...) \ Loading Loading @@ -220,6 +230,14 @@ enum clk_types { .num_parents = ARRAY_SIZE(_parent_names), \ .flag = CLK_SET_RATE_PARENT, \ .mux_flags = CLK_MUX_HIWORD_MASK) #define DEF_PLLDSI(_name, _id, _parent, _pll_packed) \ DEF_TYPE(_name, _id, CLK_TYPE_PLLDSI, .parent = _parent, .cfg.pll = _pll_packed) #define DEF_PLLDSI_DIV(_name, _id, _parent, _ddiv_packed, _dtable) \ DEF_TYPE(_name, _id, CLK_TYPE_PLLDSI_DIV, \ .cfg.ddiv = _ddiv_packed, \ .dtable = _dtable, \ .parent = _parent, \ .flag = CLK_SET_RATE_PARENT) /** * struct rzv2h_mod_clk - Module Clocks definitions Loading
include/linux/clk/renesas.h +145 −0 Original line number Diff line number Diff line Loading @@ -10,7 +10,9 @@ #ifndef __LINUX_CLK_RENESAS_H_ #define __LINUX_CLK_RENESAS_H_ #include <linux/clk-provider.h> #include <linux/types.h> #include <linux/units.h> struct device; struct device_node; Loading @@ -32,4 +34,147 @@ void cpg_mssr_detach_dev(struct generic_pm_domain *unused, struct device *dev); #define cpg_mssr_attach_dev NULL #define cpg_mssr_detach_dev NULL #endif /** * struct rzv2h_pll_limits - PLL parameter constraints * * This structure defines the minimum and maximum allowed values for * various parameters used to configure a PLL. These limits ensure * the PLL operates within valid and stable ranges. * * @fout: Output frequency range (in MHz) * @fout.min: Minimum allowed output frequency * @fout.max: Maximum allowed output frequency * * @fvco: PLL oscillation frequency range (in MHz) * @fvco.min: Minimum allowed VCO frequency * @fvco.max: Maximum allowed VCO frequency * * @m: Main-divider range * @m.min: Minimum main-divider value * @m.max: Maximum main-divider value * * @p: Pre-divider range * @p.min: Minimum pre-divider value * @p.max: Maximum pre-divider value * * @s: Divider range * @s.min: Minimum divider value * @s.max: Maximum divider value * * @k: Delta-sigma modulator range (signed) * @k.min: Minimum delta-sigma value * @k.max: Maximum delta-sigma value */ struct rzv2h_pll_limits { struct { u32 min; u32 max; } fout; struct { u32 min; u32 max; } fvco; struct { u16 min; u16 max; } m; struct { u8 min; u8 max; } p; struct { u8 min; u8 max; } s; struct { s16 min; s16 max; } k; }; /** * struct rzv2h_pll_pars - PLL configuration parameters * * This structure contains the configuration parameters for the * Phase-Locked Loop (PLL), used to achieve a specific output frequency. * * @m: Main divider value * @p: Pre-divider value * @s: Output divider value * @k: Delta-sigma modulation value * @freq_millihz: Calculated PLL output frequency in millihertz * @error_millihz: Frequency error from target in millihertz (signed) */ struct rzv2h_pll_pars { u16 m; u8 p; u8 s; s16 k; u64 freq_millihz; s64 error_millihz; }; /** * struct rzv2h_pll_div_pars - PLL parameters with post-divider * * This structure is used for PLLs that include an additional post-divider * stage after the main PLL block. It contains both the PLL configuration * parameters and the resulting frequency/error values after the divider. * * @pll: Main PLL configuration parameters (see struct rzv2h_pll_pars) * * @div: Post-divider configuration and result * @div.divider_value: Divider applied to the PLL output * @div.freq_millihz: Output frequency after divider in millihertz * @div.error_millihz: Frequency error from target in millihertz (signed) */ struct rzv2h_pll_div_pars { struct rzv2h_pll_pars pll; struct { u8 divider_value; u64 freq_millihz; s64 error_millihz; } div; }; #define RZV2H_CPG_PLL_DSI_LIMITS(name) \ static const struct rzv2h_pll_limits (name) = { \ .fout = { .min = 25 * MEGA, .max = 375 * MEGA }, \ .fvco = { .min = 1600 * MEGA, .max = 3200 * MEGA }, \ .m = { .min = 64, .max = 533 }, \ .p = { .min = 1, .max = 4 }, \ .s = { .min = 0, .max = 6 }, \ .k = { .min = -32768, .max = 32767 }, \ } \ #ifdef CONFIG_CLK_RZV2H bool rzv2h_get_pll_pars(const struct rzv2h_pll_limits *limits, struct rzv2h_pll_pars *pars, u64 freq_millihz); bool rzv2h_get_pll_divs_pars(const struct rzv2h_pll_limits *limits, struct rzv2h_pll_div_pars *pars, const u8 *table, u8 table_size, u64 freq_millihz); #else static inline bool rzv2h_get_pll_pars(const struct rzv2h_pll_limits *limits, struct rzv2h_pll_pars *pars, u64 freq_millihz) { return false; } static inline bool rzv2h_get_pll_divs_pars(const struct rzv2h_pll_limits *limits, struct rzv2h_pll_div_pars *pars, const u8 *table, u8 table_size, u64 freq_millihz) { return false; } #endif #endif