227 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * OMAP4-specific DPLL control functions4 *5 * Copyright (C) 2011 Texas Instruments, Inc.6 * Rajendra Nayak7 */8 9#include <linux/kernel.h>10#include <linux/errno.h>11#include <linux/clk.h>12#include <linux/io.h>13#include <linux/bitops.h>14#include <linux/clk/ti.h>15 16#include "clock.h"17 18/*19 * Maximum DPLL input frequency (FINT) and output frequency (FOUT) that20 * can supported when using the DPLL low-power mode. Frequencies are21 * defined in OMAP4430/60 Public TRM section 3.6.3.3.2 "Enable Control,22 * Status, and Low-Power Operation Mode".23 */24#define OMAP4_DPLL_LP_FINT_MAX 100000025#define OMAP4_DPLL_LP_FOUT_MAX 10000000026 27/*28 * Bitfield declarations29 */30#define OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK BIT(8)31#define OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK BIT(10)32#define OMAP4430_DPLL_REGM4XEN_MASK BIT(11)33 34/* Static rate multiplier for OMAP4 REGM4XEN clocks */35#define OMAP4430_REGM4XEN_MULT 436 37static void omap4_dpllmx_allow_gatectrl(struct clk_hw_omap *clk)38{39 u32 v;40 u32 mask;41 42 if (!clk)43 return;44 45 mask = clk->flags & CLOCK_CLKOUTX2 ?46 OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK :47 OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK;48 49 v = ti_clk_ll_ops->clk_readl(&clk->clksel_reg);50 /* Clear the bit to allow gatectrl */51 v &= ~mask;52 ti_clk_ll_ops->clk_writel(v, &clk->clksel_reg);53}54 55static void omap4_dpllmx_deny_gatectrl(struct clk_hw_omap *clk)56{57 u32 v;58 u32 mask;59 60 if (!clk)61 return;62 63 mask = clk->flags & CLOCK_CLKOUTX2 ?64 OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK :65 OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK;66 67 v = ti_clk_ll_ops->clk_readl(&clk->clksel_reg);68 /* Set the bit to deny gatectrl */69 v |= mask;70 ti_clk_ll_ops->clk_writel(v, &clk->clksel_reg);71}72 73const struct clk_hw_omap_ops clkhwops_omap4_dpllmx = {74 .allow_idle = omap4_dpllmx_allow_gatectrl,75 .deny_idle = omap4_dpllmx_deny_gatectrl,76};77 78/**79 * omap4_dpll_lpmode_recalc - compute DPLL low-power setting80 * @dd: pointer to the dpll data structure81 *82 * Calculates if low-power mode can be enabled based upon the last83 * multiplier and divider values calculated. If low-power mode can be84 * enabled, then the bit to enable low-power mode is stored in the85 * last_rounded_lpmode variable. This implementation is based upon the86 * criteria for enabling low-power mode as described in the OMAP4430/6087 * Public TRM section 3.6.3.3.2 "Enable Control, Status, and Low-Power88 * Operation Mode".89 */90static void omap4_dpll_lpmode_recalc(struct dpll_data *dd)91{92 long fint, fout;93 94 fint = clk_hw_get_rate(dd->clk_ref) / (dd->last_rounded_n + 1);95 fout = fint * dd->last_rounded_m;96 97 if ((fint < OMAP4_DPLL_LP_FINT_MAX) && (fout < OMAP4_DPLL_LP_FOUT_MAX))98 dd->last_rounded_lpmode = 1;99 else100 dd->last_rounded_lpmode = 0;101}102 103/**104 * omap4_dpll_regm4xen_recalc - compute DPLL rate, considering REGM4XEN bit105 * @hw: pointer to the clock to compute the rate for106 * @parent_rate: clock rate of the DPLL parent107 *108 * Compute the output rate for the OMAP4 DPLL represented by @clk.109 * Takes the REGM4XEN bit into consideration, which is needed for the110 * OMAP4 ABE DPLL. Returns the DPLL's output rate (before M-dividers)111 * upon success, or 0 upon error.112 */113unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw,114 unsigned long parent_rate)115{116 struct clk_hw_omap *clk = to_clk_hw_omap(hw);117 u32 v;118 unsigned long rate;119 struct dpll_data *dd;120 121 if (!clk || !clk->dpll_data)122 return 0;123 124 dd = clk->dpll_data;125 126 rate = omap2_get_dpll_rate(clk);127 128 /* regm4xen adds a multiplier of 4 to DPLL calculations */129 v = ti_clk_ll_ops->clk_readl(&dd->control_reg);130 if (v & OMAP4430_DPLL_REGM4XEN_MASK)131 rate *= OMAP4430_REGM4XEN_MULT;132 133 return rate;134}135 136/**137 * omap4_dpll_regm4xen_round_rate - round DPLL rate, considering REGM4XEN bit138 * @hw: struct hw_clk containing the struct clk * of the DPLL to round a rate for139 * @target_rate: the desired rate of the DPLL140 * @parent_rate: clock rate of the DPLL parent141 *142 * Compute the rate that would be programmed into the DPLL hardware143 * for @clk if set_rate() were to be provided with the rate144 * @target_rate. Takes the REGM4XEN bit into consideration, which is145 * needed for the OMAP4 ABE DPLL. Returns the rounded rate (before146 * M-dividers) upon success, -EINVAL if @clk is null or not a DPLL, or147 * ~0 if an error occurred in omap2_dpll_round_rate().148 */149long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw,150 unsigned long target_rate,151 unsigned long *parent_rate)152{153 struct clk_hw_omap *clk = to_clk_hw_omap(hw);154 struct dpll_data *dd;155 long r;156 157 if (!clk || !clk->dpll_data)158 return -EINVAL;159 160 dd = clk->dpll_data;161 162 dd->last_rounded_m4xen = 0;163 164 /*165 * First try to compute the DPLL configuration for166 * target rate without using the 4X multiplier.167 */168 r = omap2_dpll_round_rate(hw, target_rate, NULL);169 if (r != ~0)170 goto out;171 172 /*173 * If we did not find a valid DPLL configuration, try again, but174 * this time see if using the 4X multiplier can help. Enabling the175 * 4X multiplier is equivalent to dividing the target rate by 4.176 */177 r = omap2_dpll_round_rate(hw, target_rate / OMAP4430_REGM4XEN_MULT,178 NULL);179 if (r == ~0)180 return r;181 182 dd->last_rounded_rate *= OMAP4430_REGM4XEN_MULT;183 dd->last_rounded_m4xen = 1;184 185out:186 omap4_dpll_lpmode_recalc(dd);187 188 return dd->last_rounded_rate;189}190 191/**192 * omap4_dpll_regm4xen_determine_rate - determine rate for a DPLL193 * @hw: pointer to the clock to determine rate for194 * @req: target rate request195 *196 * Determines which DPLL mode to use for reaching a desired rate.197 * Checks whether the DPLL shall be in bypass or locked mode, and if198 * locked, calculates the M,N values for the DPLL via round-rate.199 * Returns 0 on success and a negative error value otherwise.200 */201int omap4_dpll_regm4xen_determine_rate(struct clk_hw *hw,202 struct clk_rate_request *req)203{204 struct clk_hw_omap *clk = to_clk_hw_omap(hw);205 struct dpll_data *dd;206 207 if (!req->rate)208 return -EINVAL;209 210 dd = clk->dpll_data;211 if (!dd)212 return -EINVAL;213 214 if (clk_hw_get_rate(dd->clk_bypass) == req->rate &&215 (dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) {216 req->best_parent_hw = dd->clk_bypass;217 } else {218 req->rate = omap4_dpll_regm4xen_round_rate(hw, req->rate,219 &req->best_parent_rate);220 req->best_parent_hw = dd->clk_ref;221 }222 223 req->best_parent_rate = req->rate;224 225 return 0;226}227