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