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