[ARM] 3992/1: i.MX/MX1 CPU Frequency scaling support

Support to change MX1 CPU frequency at runtime.
Tested on PiKRON's PiMX1 board and seems to be fully
stable up to 200 MHz end even as low as 8 MHz.

Signed-off-by: Pavel Pisa <pisa@cmp.felk.cvut.cz>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

authored by Pavel Pisa and committed by Russell King 3c8cd0cc 47fd7052

+306 -4
+10 -1
arch/arm/Kconfig
··· 732 733 endmenu 734 735 - if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP) 736 737 menu "CPU Frequency scaling" 738 ··· 758 For details, take a look at <file:Documentation/cpu-freq>. 759 760 If in doubt, say Y. 761 762 endmenu 763
··· 732 733 endmenu 734 735 + if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_IMX ) 736 737 menu "CPU Frequency scaling" 738 ··· 758 For details, take a look at <file:Documentation/cpu-freq>. 759 760 If in doubt, say Y. 761 + 762 + config CPU_FREQ_IMX 763 + tristate "CPUfreq driver for i.MX CPUs" 764 + depends on ARCH_IMX && CPU_FREQ 765 + default n 766 + help 767 + This enables the CPUfreq driver for i.MX CPUs. 768 + 769 + If in doubt, say N. 770 771 endmenu 772
+2
arch/arm/mach-imx/Makefile
··· 9 10 obj-y += irq.o time.o dma.o generic.o 11 12 # Specific board support 13 obj-$(CONFIG_ARCH_MX1ADS) += mx1ads.o 14
··· 9 10 obj-y += irq.o time.o dma.o generic.o 11 12 + obj-$(CONFIG_CPU_FREQ_IMX) += cpufreq.o 13 + 14 # Specific board support 15 obj-$(CONFIG_ARCH_MX1ADS) += mx1ads.o 16
+287
arch/arm/mach-imx/cpufreq.c
···
··· 1 + /* 2 + * cpu.c: clock scaling for the iMX 3 + * 4 + * Copyright (C) 2000 2001, The Delft University of Technology 5 + * Copyright (c) 2004 Sascha Hauer <sascha@saschahauer.de> 6 + * Copyright (C) 2006 Inky Lung <ilung@cwlinux.com> 7 + * Copyright (C) 2006 Pavel Pisa, PiKRON <ppisa@pikron.com> 8 + * 9 + * Based on SA1100 version written by: 10 + * - Johan Pouwelse (J.A.Pouwelse@its.tudelft.nl): initial version 11 + * - Erik Mouw (J.A.K.Mouw@its.tudelft.nl): 12 + * 13 + * This program is free software; you can redistribute it and/or modify 14 + * it under the terms of the GNU General Public License as published by 15 + * the Free Software Foundation; either version 2 of the License, or 16 + * (at your option) any later version. 17 + * 18 + * This program is distributed in the hope that it will be useful, 19 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 + * GNU General Public License for more details. 22 + * 23 + * You should have received a copy of the GNU General Public License 24 + * along with this program; if not, write to the Free Software 25 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 26 + * 27 + */ 28 + 29 + /*#define DEBUG*/ 30 + 31 + #include <linux/kernel.h> 32 + #include <linux/types.h> 33 + #include <linux/init.h> 34 + #include <linux/cpufreq.h> 35 + #include <asm/system.h> 36 + 37 + #include <asm/hardware.h> 38 + 39 + #include "generic.h" 40 + 41 + #ifndef __val2mfld 42 + #define __val2mfld(mask,val) (((mask)&~((mask)<<1))*(val)&(mask)) 43 + #endif 44 + #ifndef __mfld2val 45 + #define __mfld2val(mask,val) (((val)&(mask))/((mask)&~((mask)<<1))) 46 + #endif 47 + 48 + #define CR_920T_CLOCK_MODE 0xC0000000 49 + #define CR_920T_FASTBUS_MODE 0x00000000 50 + #define CR_920T_ASYNC_MODE 0xC0000000 51 + 52 + static u32 mpctl0_at_boot; 53 + 54 + static void imx_set_async_mode(void) 55 + { 56 + adjust_cr(CR_920T_CLOCK_MODE, CR_920T_ASYNC_MODE); 57 + } 58 + 59 + static void imx_set_fastbus_mode(void) 60 + { 61 + adjust_cr(CR_920T_CLOCK_MODE, CR_920T_FASTBUS_MODE); 62 + } 63 + 64 + static void imx_set_mpctl0(u32 mpctl0) 65 + { 66 + unsigned long flags; 67 + 68 + if (mpctl0 == 0) { 69 + local_irq_save(flags); 70 + CSCR &= ~CSCR_MPEN; 71 + local_irq_restore(flags); 72 + return; 73 + } 74 + 75 + local_irq_save(flags); 76 + MPCTL0 = mpctl0; 77 + CSCR |= CSCR_MPEN; 78 + local_irq_restore(flags); 79 + } 80 + 81 + /** 82 + * imx_compute_mpctl - compute new PLL parameters 83 + * @new_mpctl: pointer to location assigned by new PLL control register value 84 + * @cur_mpctl: current PLL control register parameters 85 + * @freq: required frequency in Hz 86 + * @relation: is one of %CPUFREQ_RELATION_L (supremum) 87 + * and %CPUFREQ_RELATION_H (infimum) 88 + */ 89 + long imx_compute_mpctl(u32 *new_mpctl, u32 cur_mpctl, unsigned long freq, int relation) 90 + { 91 + u32 f_ref = (CSCR & CSCR_SYSTEM_SEL) ? 16000000 : (CLK32 * 512); 92 + u32 mfi; 93 + u32 mfn; 94 + u32 mfd; 95 + u32 pd; 96 + unsigned long long ll; 97 + long l; 98 + long quot; 99 + 100 + /* Fdppl=2*Fref*(MFI+MFN/(MFD+1))/(PD+1) */ 101 + /* PD=<0,15>, MFD=<1,1023>, MFI=<5,15> MFN=<0,1022> */ 102 + 103 + if (cur_mpctl) { 104 + mfd = ((cur_mpctl >> 16) & 0x3ff) + 1; 105 + pd = ((cur_mpctl >> 26) & 0xf) + 1; 106 + } else { 107 + pd=2; mfd=313; 108 + } 109 + 110 + /* pd=2; mfd=313; mfi=8; mfn=183; */ 111 + /* (MFI+MFN/(MFD)) = Fdppl / (2*Fref) * (PD); */ 112 + 113 + quot = (f_ref + (1 << 9)) >> 10; 114 + l = (freq * pd + quot) / (2 * quot); 115 + mfi = l >> 10; 116 + mfn = ((l & ((1 << 10) - 1)) * mfd + (1 << 9)) >> 10; 117 + 118 + mfd -= 1; 119 + pd -= 1; 120 + 121 + *new_mpctl = ((mfi & 0xf) << 10) | (mfn & 0x3ff) | ((mfd & 0x3ff) << 16) 122 + | ((pd & 0xf) << 26); 123 + 124 + ll = 2 * (unsigned long long)f_ref * ( (mfi<<16) + (mfn<<16) / (mfd+1) ); 125 + quot = (pd+1) * (1<<16); 126 + ll += quot / 2; 127 + do_div(ll, quot); 128 + freq = ll; 129 + 130 + pr_debug(KERN_DEBUG "imx: new PLL parameters pd=%d mfd=%d mfi=%d mfn=%d, freq=%ld\n", 131 + pd, mfd, mfi, mfn, freq); 132 + 133 + return freq; 134 + } 135 + 136 + 137 + static int imx_verify_speed(struct cpufreq_policy *policy) 138 + { 139 + if (policy->cpu != 0) 140 + return -EINVAL; 141 + 142 + cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, policy->cpuinfo.max_freq); 143 + 144 + return 0; 145 + } 146 + 147 + static unsigned int imx_get_speed(unsigned int cpu) 148 + { 149 + unsigned int freq; 150 + unsigned int cr; 151 + unsigned int cscr; 152 + unsigned int bclk_div; 153 + 154 + if (cpu) 155 + return 0; 156 + 157 + cscr = CSCR; 158 + bclk_div = __mfld2val(CSCR_BCLK_DIV, cscr) + 1; 159 + cr = get_cr(); 160 + 161 + if((cr & CR_920T_CLOCK_MODE) == CR_920T_FASTBUS_MODE) { 162 + freq = imx_get_system_clk(); 163 + freq = (freq + bclk_div/2) / bclk_div; 164 + } else { 165 + freq = imx_get_mcu_clk(); 166 + if (cscr & CSCR_MPU_PRESC) 167 + freq /= 2; 168 + } 169 + 170 + freq = (freq + 500) / 1000; 171 + 172 + return freq; 173 + } 174 + 175 + static int imx_set_target(struct cpufreq_policy *policy, 176 + unsigned int target_freq, 177 + unsigned int relation) 178 + { 179 + struct cpufreq_freqs freqs; 180 + u32 mpctl0 = 0; 181 + u32 cscr; 182 + unsigned long flags; 183 + long freq; 184 + long sysclk; 185 + unsigned int bclk_div = 1; 186 + 187 + freq = target_freq * 1000; 188 + 189 + pr_debug(KERN_DEBUG "imx: requested frequency %ld Hz, mpctl0 at boot 0x%08x\n", 190 + freq, mpctl0_at_boot); 191 + 192 + sysclk = imx_get_system_clk(); 193 + 194 + if (freq > sysclk + 1000000) { 195 + freq = imx_compute_mpctl(&mpctl0, mpctl0_at_boot, freq, relation); 196 + if (freq < 0) { 197 + printk(KERN_WARNING "imx: target frequency %ld Hz cannot be set\n", freq); 198 + return -EINVAL; 199 + } 200 + } else { 201 + if(freq + 1000 < sysclk) { 202 + if (relation == CPUFREQ_RELATION_L) 203 + bclk_div = (sysclk - 1000) / freq; 204 + else 205 + bclk_div = (sysclk + freq + 1000) / freq; 206 + 207 + if(bclk_div > 16) 208 + bclk_div = 16; 209 + } 210 + freq = (sysclk + bclk_div / 2) / bclk_div; 211 + } 212 + 213 + freqs.old = imx_get_speed(0); 214 + freqs.new = (freq + 500) / 1000; 215 + freqs.cpu = 0; 216 + freqs.flags = 0; 217 + 218 + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); 219 + 220 + local_irq_save(flags); 221 + 222 + imx_set_fastbus_mode(); 223 + 224 + imx_set_mpctl0(mpctl0); 225 + 226 + cscr = CSCR; 227 + cscr &= ~CSCR_BCLK_DIV; 228 + cscr |= __val2mfld(CSCR_BCLK_DIV, bclk_div - 1); 229 + CSCR = cscr; 230 + 231 + if(mpctl0) { 232 + CSCR |= CSCR_MPLL_RESTART; 233 + 234 + /* Wait until MPLL is stablized */ 235 + while( CSCR & CSCR_MPLL_RESTART ); 236 + 237 + imx_set_async_mode(); 238 + } 239 + 240 + local_irq_restore(flags); 241 + 242 + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); 243 + 244 + pr_debug(KERN_INFO "imx: set frequency %ld Hz, running from %s\n", 245 + freq, mpctl0? "MPLL": "SPLL"); 246 + 247 + return 0; 248 + } 249 + 250 + static int __init imx_cpufreq_driver_init(struct cpufreq_policy *policy) 251 + { 252 + printk(KERN_INFO "i.MX cpu freq change driver v1.0\n"); 253 + 254 + if (policy->cpu != 0) 255 + return -EINVAL; 256 + 257 + policy->cur = policy->min = policy->max = imx_get_speed(0); 258 + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; 259 + policy->cpuinfo.min_freq = 8000; 260 + policy->cpuinfo.max_freq = 200000; 261 + policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; 262 + return 0; 263 + } 264 + 265 + static struct cpufreq_driver imx_driver = { 266 + .flags = CPUFREQ_STICKY, 267 + .verify = imx_verify_speed, 268 + .target = imx_set_target, 269 + .get = imx_get_speed, 270 + .init = imx_cpufreq_driver_init, 271 + .name = "imx", 272 + }; 273 + 274 + static int __init imx_cpufreq_init(void) 275 + { 276 + 277 + mpctl0_at_boot = 0; 278 + 279 + if((CSCR & CSCR_MPEN) && 280 + ((get_cr() & CR_920T_CLOCK_MODE) != CR_920T_FASTBUS_MODE)) 281 + mpctl0_at_boot = MPCTL0; 282 + 283 + return cpufreq_register_driver(&imx_driver); 284 + } 285 + 286 + arch_initcall(imx_cpufreq_init); 287 +
+7 -3
include/asm-arm/arch-imx/imx-regs.h
··· 41 42 /* PLL registers */ 43 #define CSCR __REG(IMX_PLL_BASE) /* Clock Source Control Register */ 44 - #define CSCR_SYSTEM_SEL (1<<16) 45 46 #define MPCTL0 __REG(IMX_PLL_BASE + 0x4) /* MCU PLL Control Register 0 */ 47 #define MPCTL1 __REG(IMX_PLL_BASE + 0x8) /* MCU PLL and System Clock Register 1 */ 48 #define SPCTL0 __REG(IMX_PLL_BASE + 0xc) /* System PLL Control Register 0 */ 49 #define SPCTL1 __REG(IMX_PLL_BASE + 0x10) /* System PLL Control Register 1 */ 50 #define PCDR __REG(IMX_PLL_BASE + 0x20) /* Peripheral Clock Divider Register */ 51 - 52 - #define CSCR_MPLL_RESTART (1<<21) 53 54 /* 55 * GPIO Module and I/O Multiplexer
··· 41 42 /* PLL registers */ 43 #define CSCR __REG(IMX_PLL_BASE) /* Clock Source Control Register */ 44 + #define CSCR_SPLL_RESTART (1<<22) 45 + #define CSCR_MPLL_RESTART (1<<21) 46 + #define CSCR_SYSTEM_SEL (1<<16) 47 + #define CSCR_BCLK_DIV (0xf<<10) 48 + #define CSCR_MPU_PRESC (1<<15) 49 + #define CSCR_SPEN (1<<1) 50 + #define CSCR_MPEN (1<<0) 51 52 #define MPCTL0 __REG(IMX_PLL_BASE + 0x4) /* MCU PLL Control Register 0 */ 53 #define MPCTL1 __REG(IMX_PLL_BASE + 0x8) /* MCU PLL and System Clock Register 1 */ 54 #define SPCTL0 __REG(IMX_PLL_BASE + 0xc) /* System PLL Control Register 0 */ 55 #define SPCTL1 __REG(IMX_PLL_BASE + 0x10) /* System PLL Control Register 1 */ 56 #define PCDR __REG(IMX_PLL_BASE + 0x20) /* Peripheral Clock Divider Register */ 57 58 /* 59 * GPIO Module and I/O Multiplexer