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 */ 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; 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 */ 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 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 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