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