1 /*
2  * OMAP2xxx DVFS virtual clock functions
3  *
4  * Copyright (C) 2005-2008 Texas Instruments, Inc.
5  * Copyright (C) 2004-2010 Nokia Corporation
6  *
7  * Contacts:
8  * Richard Woodruff <r-woodruff2@ti.com>
9  * Paul Walmsley
10  *
11  * Based on earlier work by Tuukka Tikkanen, Tony Lindgren,
12  * Gordon McNutt and RidgeRun, Inc.
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License version 2 as
16  * published by the Free Software Foundation.
17  *
18  * XXX Some of this code should be replaceable by the upcoming OPP layer
19  * code.  However, some notion of "rate set" is probably still necessary
20  * for OMAP2xxx at least.  Rate sets should be generalized so they can be
21  * used for any OMAP chip, not just OMAP2xxx.  In particular, Richard Woodruff
22  * has in the past expressed a preference to use rate sets for OPP changes,
23  * rather than dynamically recalculating the clock tree, so if someone wants
24  * this badly enough to write the code to handle it, we should support it
25  * as an option.
26  */
27 #undef DEBUG
28 
29 #include <linux/kernel.h>
30 #include <linux/errno.h>
31 #include <linux/clk.h>
32 #include <linux/io.h>
33 #include <linux/cpufreq.h>
34 #include <linux/slab.h>
35 
36 #include <plat/clock.h>
37 #include <plat/sram.h>
38 #include <plat/sdrc.h>
39 
40 #include "clock.h"
41 #include "clock2xxx.h"
42 #include "opp2xxx.h"
43 #include "cm2xxx_3xxx.h"
44 #include "cm-regbits-24xx.h"
45 
46 const struct prcm_config *curr_prcm_set;
47 const struct prcm_config *rate_table;
48 
49 /**
50  * omap2_table_mpu_recalc - just return the MPU speed
51  * @clk: virt_prcm_set struct clk
52  *
53  * Set virt_prcm_set's rate to the mpu_speed field of the current PRCM set.
54  */
omap2_table_mpu_recalc(struct clk * clk)55 unsigned long omap2_table_mpu_recalc(struct clk *clk)
56 {
57 	return curr_prcm_set->mpu_speed;
58 }
59 
60 /*
61  * Look for a rate equal or less than the target rate given a configuration set.
62  *
63  * What's not entirely clear is "which" field represents the key field.
64  * Some might argue L3-DDR, others ARM, others IVA. This code is simple and
65  * just uses the ARM rates.
66  */
omap2_round_to_table_rate(struct clk * clk,unsigned long rate)67 long omap2_round_to_table_rate(struct clk *clk, unsigned long rate)
68 {
69 	const struct prcm_config *ptr;
70 	long highest_rate;
71 
72 	highest_rate = -EINVAL;
73 
74 	for (ptr = rate_table; ptr->mpu_speed; ptr++) {
75 		if (!(ptr->flags & cpu_mask))
76 			continue;
77 		if (ptr->xtal_speed != sclk->rate)
78 			continue;
79 
80 		highest_rate = ptr->mpu_speed;
81 
82 		/* Can check only after xtal frequency check */
83 		if (ptr->mpu_speed <= rate)
84 			break;
85 	}
86 	return highest_rate;
87 }
88 
89 /* Sets basic clocks based on the specified rate */
omap2_select_table_rate(struct clk * clk,unsigned long rate)90 int omap2_select_table_rate(struct clk *clk, unsigned long rate)
91 {
92 	u32 cur_rate, done_rate, bypass = 0, tmp;
93 	const struct prcm_config *prcm;
94 	unsigned long found_speed = 0;
95 	unsigned long flags;
96 
97 	for (prcm = rate_table; prcm->mpu_speed; prcm++) {
98 		if (!(prcm->flags & cpu_mask))
99 			continue;
100 
101 		if (prcm->xtal_speed != sclk->rate)
102 			continue;
103 
104 		if (prcm->mpu_speed <= rate) {
105 			found_speed = prcm->mpu_speed;
106 			break;
107 		}
108 	}
109 
110 	if (!found_speed) {
111 		printk(KERN_INFO "Could not set MPU rate to %luMHz\n",
112 		       rate / 1000000);
113 		return -EINVAL;
114 	}
115 
116 	curr_prcm_set = prcm;
117 	cur_rate = omap2xxx_clk_get_core_rate(dclk);
118 
119 	if (prcm->dpll_speed == cur_rate / 2) {
120 		omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL, 1);
121 	} else if (prcm->dpll_speed == cur_rate * 2) {
122 		omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL_X2, 1);
123 	} else if (prcm->dpll_speed != cur_rate) {
124 		local_irq_save(flags);
125 
126 		if (prcm->dpll_speed == prcm->xtal_speed)
127 			bypass = 1;
128 
129 		if ((prcm->cm_clksel2_pll & OMAP24XX_CORE_CLK_SRC_MASK) ==
130 		    CORE_CLK_SRC_DPLL_X2)
131 			done_rate = CORE_CLK_SRC_DPLL_X2;
132 		else
133 			done_rate = CORE_CLK_SRC_DPLL;
134 
135 		/* MPU divider */
136 		omap2_cm_write_mod_reg(prcm->cm_clksel_mpu, MPU_MOD, CM_CLKSEL);
137 
138 		/* dsp + iva1 div(2420), iva2.1(2430) */
139 		omap2_cm_write_mod_reg(prcm->cm_clksel_dsp,
140 				 OMAP24XX_DSP_MOD, CM_CLKSEL);
141 
142 		omap2_cm_write_mod_reg(prcm->cm_clksel_gfx, GFX_MOD, CM_CLKSEL);
143 
144 		/* Major subsystem dividers */
145 		tmp = omap2_cm_read_mod_reg(CORE_MOD, CM_CLKSEL1) & OMAP24XX_CLKSEL_DSS2_MASK;
146 		omap2_cm_write_mod_reg(prcm->cm_clksel1_core | tmp, CORE_MOD,
147 				 CM_CLKSEL1);
148 
149 		if (cpu_is_omap2430())
150 			omap2_cm_write_mod_reg(prcm->cm_clksel_mdm,
151 					 OMAP2430_MDM_MOD, CM_CLKSEL);
152 
153 		/* x2 to enter omap2xxx_sdrc_init_params() */
154 		omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL_X2, 1);
155 
156 		omap2_set_prcm(prcm->cm_clksel1_pll, prcm->base_sdrc_rfr,
157 			       bypass);
158 
159 		omap2xxx_sdrc_init_params(omap2xxx_sdrc_dll_is_unlocked());
160 		omap2xxx_sdrc_reprogram(done_rate, 0);
161 
162 		local_irq_restore(flags);
163 	}
164 
165 	return 0;
166 }
167 
168 #ifdef CONFIG_CPU_FREQ
169 /*
170  * Walk PRCM rate table and fillout cpufreq freq_table
171  * XXX This should be replaced by an OPP layer in the near future
172  */
173 static struct cpufreq_frequency_table *freq_table;
174 
omap2_clk_init_cpufreq_table(struct cpufreq_frequency_table ** table)175 void omap2_clk_init_cpufreq_table(struct cpufreq_frequency_table **table)
176 {
177 	const struct prcm_config *prcm;
178 	int i = 0;
179 	int tbl_sz = 0;
180 
181 	if (!cpu_is_omap24xx())
182 		return;
183 
184 	for (prcm = rate_table; prcm->mpu_speed; prcm++) {
185 		if (!(prcm->flags & cpu_mask))
186 			continue;
187 		if (prcm->xtal_speed != sclk->rate)
188 			continue;
189 
190 		/* don't put bypass rates in table */
191 		if (prcm->dpll_speed == prcm->xtal_speed)
192 			continue;
193 
194 		tbl_sz++;
195 	}
196 
197 	/*
198 	 * XXX Ensure that we're doing what CPUFreq expects for this error
199 	 * case and the following one
200 	 */
201 	if (tbl_sz == 0) {
202 		pr_warning("%s: no matching entries in rate_table\n",
203 			   __func__);
204 		return;
205 	}
206 
207 	/* Include the CPUFREQ_TABLE_END terminator entry */
208 	tbl_sz++;
209 
210 	freq_table = kzalloc(sizeof(struct cpufreq_frequency_table) * tbl_sz,
211 			     GFP_ATOMIC);
212 	if (!freq_table) {
213 		pr_err("%s: could not kzalloc frequency table\n", __func__);
214 		return;
215 	}
216 
217 	for (prcm = rate_table; prcm->mpu_speed; prcm++) {
218 		if (!(prcm->flags & cpu_mask))
219 			continue;
220 		if (prcm->xtal_speed != sclk->rate)
221 			continue;
222 
223 		/* don't put bypass rates in table */
224 		if (prcm->dpll_speed == prcm->xtal_speed)
225 			continue;
226 
227 		freq_table[i].index = i;
228 		freq_table[i].frequency = prcm->mpu_speed / 1000;
229 		i++;
230 	}
231 
232 	freq_table[i].index = i;
233 	freq_table[i].frequency = CPUFREQ_TABLE_END;
234 
235 	*table = &freq_table[0];
236 }
237 
omap2_clk_exit_cpufreq_table(struct cpufreq_frequency_table ** table)238 void omap2_clk_exit_cpufreq_table(struct cpufreq_frequency_table **table)
239 {
240 	if (!cpu_is_omap24xx())
241 		return;
242 
243 	kfree(freq_table);
244 }
245 
246 #endif
247