Commit | Line | Data |
---|---|---|
734f69a7 PW |
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> | |
5a0e3ad6 | 34 | #include <linux/slab.h> |
734f69a7 | 35 | |
622297fd | 36 | #include "../plat-omap/sram.h" |
734f69a7 | 37 | |
dbc04161 | 38 | #include "soc.h" |
734f69a7 PW |
39 | #include "clock.h" |
40 | #include "clock2xxx.h" | |
41 | #include "opp2xxx.h" | |
59fb659b | 42 | #include "cm2xxx_3xxx.h" |
734f69a7 | 43 | #include "cm-regbits-24xx.h" |
3e6ece13 | 44 | #include "sdrc.h" |
734f69a7 PW |
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 | */ | |
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 | */ | |
67 | long omap2_round_to_table_rate(struct clk *clk, unsigned long rate) | |
68 | { | |
69 | const struct prcm_config *ptr; | |
5dcc3b97 | 70 | long highest_rate, sys_clk_rate; |
734f69a7 PW |
71 | |
72 | highest_rate = -EINVAL; | |
5dcc3b97 | 73 | sys_clk_rate = __clk_get_rate(sclk); |
734f69a7 PW |
74 | |
75 | for (ptr = rate_table; ptr->mpu_speed; ptr++) { | |
76 | if (!(ptr->flags & cpu_mask)) | |
77 | continue; | |
5dcc3b97 | 78 | if (ptr->xtal_speed != sys_clk_rate) |
734f69a7 PW |
79 | continue; |
80 | ||
81 | highest_rate = ptr->mpu_speed; | |
82 | ||
83 | /* Can check only after xtal frequency check */ | |
84 | if (ptr->mpu_speed <= rate) | |
85 | break; | |
86 | } | |
87 | return highest_rate; | |
88 | } | |
89 | ||
90 | /* Sets basic clocks based on the specified rate */ | |
91 | int omap2_select_table_rate(struct clk *clk, unsigned long rate) | |
92 | { | |
93 | u32 cur_rate, done_rate, bypass = 0, tmp; | |
94 | const struct prcm_config *prcm; | |
95 | unsigned long found_speed = 0; | |
96 | unsigned long flags; | |
5dcc3b97 RN |
97 | long sys_clk_rate; |
98 | ||
99 | sys_clk_rate = __clk_get_rate(sclk); | |
734f69a7 PW |
100 | |
101 | for (prcm = rate_table; prcm->mpu_speed; prcm++) { | |
102 | if (!(prcm->flags & cpu_mask)) | |
103 | continue; | |
104 | ||
5dcc3b97 | 105 | if (prcm->xtal_speed != sys_clk_rate) |
734f69a7 PW |
106 | continue; |
107 | ||
108 | if (prcm->mpu_speed <= rate) { | |
109 | found_speed = prcm->mpu_speed; | |
110 | break; | |
111 | } | |
112 | } | |
113 | ||
114 | if (!found_speed) { | |
115 | printk(KERN_INFO "Could not set MPU rate to %luMHz\n", | |
116 | rate / 1000000); | |
117 | return -EINVAL; | |
118 | } | |
119 | ||
120 | curr_prcm_set = prcm; | |
5f039377 | 121 | cur_rate = omap2xxx_clk_get_core_rate(); |
734f69a7 PW |
122 | |
123 | if (prcm->dpll_speed == cur_rate / 2) { | |
124 | omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL, 1); | |
125 | } else if (prcm->dpll_speed == cur_rate * 2) { | |
126 | omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL_X2, 1); | |
127 | } else if (prcm->dpll_speed != cur_rate) { | |
128 | local_irq_save(flags); | |
129 | ||
130 | if (prcm->dpll_speed == prcm->xtal_speed) | |
131 | bypass = 1; | |
132 | ||
133 | if ((prcm->cm_clksel2_pll & OMAP24XX_CORE_CLK_SRC_MASK) == | |
134 | CORE_CLK_SRC_DPLL_X2) | |
135 | done_rate = CORE_CLK_SRC_DPLL_X2; | |
136 | else | |
137 | done_rate = CORE_CLK_SRC_DPLL; | |
138 | ||
139 | /* MPU divider */ | |
c4d7e58f | 140 | omap2_cm_write_mod_reg(prcm->cm_clksel_mpu, MPU_MOD, CM_CLKSEL); |
734f69a7 PW |
141 | |
142 | /* dsp + iva1 div(2420), iva2.1(2430) */ | |
c4d7e58f | 143 | omap2_cm_write_mod_reg(prcm->cm_clksel_dsp, |
734f69a7 PW |
144 | OMAP24XX_DSP_MOD, CM_CLKSEL); |
145 | ||
c4d7e58f | 146 | omap2_cm_write_mod_reg(prcm->cm_clksel_gfx, GFX_MOD, CM_CLKSEL); |
734f69a7 PW |
147 | |
148 | /* Major subsystem dividers */ | |
c4d7e58f PW |
149 | tmp = omap2_cm_read_mod_reg(CORE_MOD, CM_CLKSEL1) & OMAP24XX_CLKSEL_DSS2_MASK; |
150 | omap2_cm_write_mod_reg(prcm->cm_clksel1_core | tmp, CORE_MOD, | |
734f69a7 PW |
151 | CM_CLKSEL1); |
152 | ||
153 | if (cpu_is_omap2430()) | |
c4d7e58f | 154 | omap2_cm_write_mod_reg(prcm->cm_clksel_mdm, |
734f69a7 PW |
155 | OMAP2430_MDM_MOD, CM_CLKSEL); |
156 | ||
157 | /* x2 to enter omap2xxx_sdrc_init_params() */ | |
158 | omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL_X2, 1); | |
159 | ||
160 | omap2_set_prcm(prcm->cm_clksel1_pll, prcm->base_sdrc_rfr, | |
161 | bypass); | |
162 | ||
163 | omap2xxx_sdrc_init_params(omap2xxx_sdrc_dll_is_unlocked()); | |
164 | omap2xxx_sdrc_reprogram(done_rate, 0); | |
165 | ||
166 | local_irq_restore(flags); | |
167 | } | |
168 | ||
169 | return 0; | |
170 | } |