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 
omap4_dpllmx_allow_gatectrl(struct clk_hw_omap * clk)40 static void omap4_dpllmx_allow_gatectrl(struct clk_hw_omap *clk)
41 {
42 	u32 v;
43 	u32 mask;
44 
45 	if (!clk)
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 
omap4_dpllmx_deny_gatectrl(struct clk_hw_omap * clk)58 static void omap4_dpllmx_deny_gatectrl(struct clk_hw_omap *clk)
59 {
60 	u32 v;
61 	u32 mask;
62 
63 	if (!clk)
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  */
omap4_dpll_lpmode_recalc(struct dpll_data * dd)93 static void omap4_dpll_lpmode_recalc(struct dpll_data *dd)
94 {
95 	long fint, fout;
96 
97 	fint = clk_hw_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  */
omap4_dpll_regm4xen_recalc(struct clk_hw * hw,unsigned long parent_rate)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  */
omap4_dpll_regm4xen_round_rate(struct clk_hw * hw,unsigned long target_rate,unsigned long * parent_rate)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  */
omap4_dpll_regm4xen_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)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_hw_get_rate(dd->clk_bypass) == req->rate &&
216 	    (dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) {
217 		req->best_parent_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 = dd->clk_ref;
222 	}
223 
224 	req->best_parent_rate = req->rate;
225 
226 	return 0;
227 }
228