1731e0cc6SSantosh Shilimkar /* 2731e0cc6SSantosh Shilimkar * CPU frequency scaling for OMAP 3731e0cc6SSantosh Shilimkar * 4731e0cc6SSantosh Shilimkar * Copyright (C) 2005 Nokia Corporation 5731e0cc6SSantosh Shilimkar * Written by Tony Lindgren <tony@atomide.com> 6731e0cc6SSantosh Shilimkar * 7731e0cc6SSantosh Shilimkar * Based on cpu-sa1110.c, Copyright (C) 2001 Russell King 8731e0cc6SSantosh Shilimkar * 9731e0cc6SSantosh Shilimkar * Copyright (C) 2007-2011 Texas Instruments, Inc. 10731e0cc6SSantosh Shilimkar * - OMAP3/4 support by Rajendra Nayak, Santosh Shilimkar 11731e0cc6SSantosh Shilimkar * 12731e0cc6SSantosh Shilimkar * This program is free software; you can redistribute it and/or modify 13731e0cc6SSantosh Shilimkar * it under the terms of the GNU General Public License version 2 as 14731e0cc6SSantosh Shilimkar * published by the Free Software Foundation. 15731e0cc6SSantosh Shilimkar */ 16731e0cc6SSantosh Shilimkar #include <linux/types.h> 17731e0cc6SSantosh Shilimkar #include <linux/kernel.h> 18731e0cc6SSantosh Shilimkar #include <linux/sched.h> 19731e0cc6SSantosh Shilimkar #include <linux/cpufreq.h> 20731e0cc6SSantosh Shilimkar #include <linux/delay.h> 21731e0cc6SSantosh Shilimkar #include <linux/init.h> 22731e0cc6SSantosh Shilimkar #include <linux/err.h> 23731e0cc6SSantosh Shilimkar #include <linux/clk.h> 24731e0cc6SSantosh Shilimkar #include <linux/io.h> 25731e0cc6SSantosh Shilimkar #include <linux/opp.h> 26*46c12216SRussell King #include <linux/cpu.h> 27731e0cc6SSantosh Shilimkar 28731e0cc6SSantosh Shilimkar #include <asm/system.h> 29731e0cc6SSantosh Shilimkar #include <asm/smp_plat.h> 30*46c12216SRussell King #include <asm/cpu.h> 31731e0cc6SSantosh Shilimkar 32731e0cc6SSantosh Shilimkar #include <plat/clock.h> 33731e0cc6SSantosh Shilimkar #include <plat/omap-pm.h> 34731e0cc6SSantosh Shilimkar #include <plat/common.h> 35731e0cc6SSantosh Shilimkar 36731e0cc6SSantosh Shilimkar #include <mach/hardware.h> 37731e0cc6SSantosh Shilimkar 38731e0cc6SSantosh Shilimkar #define VERY_HI_RATE 900000000 39731e0cc6SSantosh Shilimkar 40*46c12216SRussell King #ifdef CONFIG_SMP 41*46c12216SRussell King struct lpj_info { 42*46c12216SRussell King unsigned long ref; 43*46c12216SRussell King unsigned int freq; 44*46c12216SRussell King }; 45*46c12216SRussell King 46*46c12216SRussell King static DEFINE_PER_CPU(struct lpj_info, lpj_ref); 47*46c12216SRussell King static struct lpj_info global_lpj_ref; 48*46c12216SRussell King #endif 49*46c12216SRussell King 50731e0cc6SSantosh Shilimkar static struct cpufreq_frequency_table *freq_table; 51731e0cc6SSantosh Shilimkar static struct clk *mpu_clk; 52731e0cc6SSantosh Shilimkar 53731e0cc6SSantosh Shilimkar static int omap_verify_speed(struct cpufreq_policy *policy) 54731e0cc6SSantosh Shilimkar { 55731e0cc6SSantosh Shilimkar if (freq_table) 56731e0cc6SSantosh Shilimkar return cpufreq_frequency_table_verify(policy, freq_table); 57731e0cc6SSantosh Shilimkar 58731e0cc6SSantosh Shilimkar if (policy->cpu) 59731e0cc6SSantosh Shilimkar return -EINVAL; 60731e0cc6SSantosh Shilimkar 61731e0cc6SSantosh Shilimkar cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, 62731e0cc6SSantosh Shilimkar policy->cpuinfo.max_freq); 63731e0cc6SSantosh Shilimkar 64731e0cc6SSantosh Shilimkar policy->min = clk_round_rate(mpu_clk, policy->min * 1000) / 1000; 65731e0cc6SSantosh Shilimkar policy->max = clk_round_rate(mpu_clk, policy->max * 1000) / 1000; 66731e0cc6SSantosh Shilimkar cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, 67731e0cc6SSantosh Shilimkar policy->cpuinfo.max_freq); 68731e0cc6SSantosh Shilimkar return 0; 69731e0cc6SSantosh Shilimkar } 70731e0cc6SSantosh Shilimkar 71731e0cc6SSantosh Shilimkar static unsigned int omap_getspeed(unsigned int cpu) 72731e0cc6SSantosh Shilimkar { 73731e0cc6SSantosh Shilimkar unsigned long rate; 74731e0cc6SSantosh Shilimkar 75*46c12216SRussell King if (cpu >= NR_CPUS) 76731e0cc6SSantosh Shilimkar return 0; 77731e0cc6SSantosh Shilimkar 78731e0cc6SSantosh Shilimkar rate = clk_get_rate(mpu_clk) / 1000; 79731e0cc6SSantosh Shilimkar return rate; 80731e0cc6SSantosh Shilimkar } 81731e0cc6SSantosh Shilimkar 82731e0cc6SSantosh Shilimkar static int omap_target(struct cpufreq_policy *policy, 83731e0cc6SSantosh Shilimkar unsigned int target_freq, 84731e0cc6SSantosh Shilimkar unsigned int relation) 85731e0cc6SSantosh Shilimkar { 86*46c12216SRussell King int i, ret = 0; 87731e0cc6SSantosh Shilimkar struct cpufreq_freqs freqs; 88731e0cc6SSantosh Shilimkar 89731e0cc6SSantosh Shilimkar /* Ensure desired rate is within allowed range. Some govenors 90731e0cc6SSantosh Shilimkar * (ondemand) will just pass target_freq=0 to get the minimum. */ 91731e0cc6SSantosh Shilimkar if (target_freq < policy->min) 92731e0cc6SSantosh Shilimkar target_freq = policy->min; 93731e0cc6SSantosh Shilimkar if (target_freq > policy->max) 94731e0cc6SSantosh Shilimkar target_freq = policy->max; 95731e0cc6SSantosh Shilimkar 96*46c12216SRussell King freqs.old = omap_getspeed(policy->cpu); 97731e0cc6SSantosh Shilimkar freqs.new = clk_round_rate(mpu_clk, target_freq * 1000) / 1000; 98*46c12216SRussell King freqs.cpu = policy->cpu; 99731e0cc6SSantosh Shilimkar 100731e0cc6SSantosh Shilimkar if (freqs.old == freqs.new) 101731e0cc6SSantosh Shilimkar return ret; 102731e0cc6SSantosh Shilimkar 103*46c12216SRussell King /* notifiers */ 104*46c12216SRussell King for_each_cpu(i, policy->cpus) { 105*46c12216SRussell King freqs.cpu = i; 106731e0cc6SSantosh Shilimkar cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); 107*46c12216SRussell King } 108731e0cc6SSantosh Shilimkar 109731e0cc6SSantosh Shilimkar #ifdef CONFIG_CPU_FREQ_DEBUG 110731e0cc6SSantosh Shilimkar pr_info("cpufreq-omap: transition: %u --> %u\n", freqs.old, freqs.new); 111731e0cc6SSantosh Shilimkar #endif 112731e0cc6SSantosh Shilimkar 113731e0cc6SSantosh Shilimkar ret = clk_set_rate(mpu_clk, freqs.new * 1000); 114*46c12216SRussell King freqs.new = omap_getspeed(policy->cpu); 115731e0cc6SSantosh Shilimkar 116*46c12216SRussell King #ifdef CONFIG_SMP 117*46c12216SRussell King /* 118*46c12216SRussell King * Note that loops_per_jiffy is not updated on SMP systems in 119*46c12216SRussell King * cpufreq driver. So, update the per-CPU loops_per_jiffy value 120*46c12216SRussell King * on frequency transition. We need to update all dependent CPUs. 121*46c12216SRussell King */ 122*46c12216SRussell King for_each_cpu(i, policy->cpus) { 123*46c12216SRussell King struct lpj_info *lpj = &per_cpu(lpj_ref, i); 124*46c12216SRussell King if (!lpj->freq) { 125*46c12216SRussell King lpj->ref = per_cpu(cpu_data, i).loops_per_jiffy; 126*46c12216SRussell King lpj->freq = freqs.old; 127*46c12216SRussell King } 128*46c12216SRussell King 129*46c12216SRussell King per_cpu(cpu_data, i).loops_per_jiffy = 130*46c12216SRussell King cpufreq_scale(lpj->ref, lpj->freq, freqs.new); 131*46c12216SRussell King } 132*46c12216SRussell King 133*46c12216SRussell King /* And don't forget to adjust the global one */ 134*46c12216SRussell King if (!global_lpj_ref.freq) { 135*46c12216SRussell King global_lpj_ref.ref = loops_per_jiffy; 136*46c12216SRussell King global_lpj_ref.freq = freqs.old; 137*46c12216SRussell King } 138*46c12216SRussell King loops_per_jiffy = cpufreq_scale(global_lpj_ref.ref, global_lpj_ref.freq, 139*46c12216SRussell King freqs.new); 140*46c12216SRussell King #endif 141*46c12216SRussell King 142*46c12216SRussell King /* notifiers */ 143*46c12216SRussell King for_each_cpu(i, policy->cpus) { 144*46c12216SRussell King freqs.cpu = i; 145731e0cc6SSantosh Shilimkar cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); 146*46c12216SRussell King } 147731e0cc6SSantosh Shilimkar 148731e0cc6SSantosh Shilimkar return ret; 149731e0cc6SSantosh Shilimkar } 150731e0cc6SSantosh Shilimkar 151731e0cc6SSantosh Shilimkar static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) 152731e0cc6SSantosh Shilimkar { 153731e0cc6SSantosh Shilimkar int result = 0; 154731e0cc6SSantosh Shilimkar struct device *mpu_dev; 155*46c12216SRussell King static cpumask_var_t cpumask; 156731e0cc6SSantosh Shilimkar 157731e0cc6SSantosh Shilimkar if (cpu_is_omap24xx()) 158731e0cc6SSantosh Shilimkar mpu_clk = clk_get(NULL, "virt_prcm_set"); 159731e0cc6SSantosh Shilimkar else if (cpu_is_omap34xx()) 160731e0cc6SSantosh Shilimkar mpu_clk = clk_get(NULL, "dpll1_ck"); 161731e0cc6SSantosh Shilimkar else if (cpu_is_omap44xx()) 162731e0cc6SSantosh Shilimkar mpu_clk = clk_get(NULL, "dpll_mpu_ck"); 163731e0cc6SSantosh Shilimkar 164731e0cc6SSantosh Shilimkar if (IS_ERR(mpu_clk)) 165731e0cc6SSantosh Shilimkar return PTR_ERR(mpu_clk); 166731e0cc6SSantosh Shilimkar 167*46c12216SRussell King if (policy->cpu >= NR_CPUS) 168731e0cc6SSantosh Shilimkar return -EINVAL; 169731e0cc6SSantosh Shilimkar 170*46c12216SRussell King policy->cur = policy->min = policy->max = omap_getspeed(policy->cpu); 171731e0cc6SSantosh Shilimkar mpu_dev = omap2_get_mpuss_device(); 172*46c12216SRussell King 173731e0cc6SSantosh Shilimkar if (!mpu_dev) { 174731e0cc6SSantosh Shilimkar pr_warning("%s: unable to get the mpu device\n", __func__); 175731e0cc6SSantosh Shilimkar return -EINVAL; 176731e0cc6SSantosh Shilimkar } 177731e0cc6SSantosh Shilimkar opp_init_cpufreq_table(mpu_dev, &freq_table); 178731e0cc6SSantosh Shilimkar 179731e0cc6SSantosh Shilimkar if (freq_table) { 180731e0cc6SSantosh Shilimkar result = cpufreq_frequency_table_cpuinfo(policy, freq_table); 181731e0cc6SSantosh Shilimkar if (!result) 182731e0cc6SSantosh Shilimkar cpufreq_frequency_table_get_attr(freq_table, 183731e0cc6SSantosh Shilimkar policy->cpu); 184731e0cc6SSantosh Shilimkar } else { 185731e0cc6SSantosh Shilimkar policy->cpuinfo.min_freq = clk_round_rate(mpu_clk, 0) / 1000; 186731e0cc6SSantosh Shilimkar policy->cpuinfo.max_freq = clk_round_rate(mpu_clk, 187731e0cc6SSantosh Shilimkar VERY_HI_RATE) / 1000; 188731e0cc6SSantosh Shilimkar } 189731e0cc6SSantosh Shilimkar 190731e0cc6SSantosh Shilimkar policy->min = policy->cpuinfo.min_freq; 191731e0cc6SSantosh Shilimkar policy->max = policy->cpuinfo.max_freq; 192*46c12216SRussell King policy->cur = omap_getspeed(policy->cpu); 193*46c12216SRussell King 194*46c12216SRussell King /* 195*46c12216SRussell King * On OMAP SMP configuartion, both processors share the voltage 196*46c12216SRussell King * and clock. So both CPUs needs to be scaled together and hence 197*46c12216SRussell King * needs software co-ordination. Use cpufreq affected_cpus 198*46c12216SRussell King * interface to handle this scenario. Additional is_smp() check 199*46c12216SRussell King * is to keep SMP_ON_UP build working. 200*46c12216SRussell King */ 201*46c12216SRussell King if (is_smp()) { 202*46c12216SRussell King policy->shared_type = CPUFREQ_SHARED_TYPE_ANY; 203*46c12216SRussell King cpumask_or(cpumask, cpumask_of(policy->cpu), cpumask); 204*46c12216SRussell King cpumask_copy(policy->cpus, cpumask); 205*46c12216SRussell King } 206731e0cc6SSantosh Shilimkar 207731e0cc6SSantosh Shilimkar /* FIXME: what's the actual transition time? */ 208731e0cc6SSantosh Shilimkar policy->cpuinfo.transition_latency = 300 * 1000; 209731e0cc6SSantosh Shilimkar 210731e0cc6SSantosh Shilimkar return 0; 211731e0cc6SSantosh Shilimkar } 212731e0cc6SSantosh Shilimkar 213731e0cc6SSantosh Shilimkar static int omap_cpu_exit(struct cpufreq_policy *policy) 214731e0cc6SSantosh Shilimkar { 215731e0cc6SSantosh Shilimkar clk_exit_cpufreq_table(&freq_table); 216731e0cc6SSantosh Shilimkar clk_put(mpu_clk); 217731e0cc6SSantosh Shilimkar return 0; 218731e0cc6SSantosh Shilimkar } 219731e0cc6SSantosh Shilimkar 220731e0cc6SSantosh Shilimkar static struct freq_attr *omap_cpufreq_attr[] = { 221731e0cc6SSantosh Shilimkar &cpufreq_freq_attr_scaling_available_freqs, 222731e0cc6SSantosh Shilimkar NULL, 223731e0cc6SSantosh Shilimkar }; 224731e0cc6SSantosh Shilimkar 225731e0cc6SSantosh Shilimkar static struct cpufreq_driver omap_driver = { 226731e0cc6SSantosh Shilimkar .flags = CPUFREQ_STICKY, 227731e0cc6SSantosh Shilimkar .verify = omap_verify_speed, 228731e0cc6SSantosh Shilimkar .target = omap_target, 229731e0cc6SSantosh Shilimkar .get = omap_getspeed, 230731e0cc6SSantosh Shilimkar .init = omap_cpu_init, 231731e0cc6SSantosh Shilimkar .exit = omap_cpu_exit, 232731e0cc6SSantosh Shilimkar .name = "omap", 233731e0cc6SSantosh Shilimkar .attr = omap_cpufreq_attr, 234731e0cc6SSantosh Shilimkar }; 235731e0cc6SSantosh Shilimkar 236731e0cc6SSantosh Shilimkar static int __init omap_cpufreq_init(void) 237731e0cc6SSantosh Shilimkar { 238731e0cc6SSantosh Shilimkar return cpufreq_register_driver(&omap_driver); 239731e0cc6SSantosh Shilimkar } 240731e0cc6SSantosh Shilimkar 241731e0cc6SSantosh Shilimkar static void __exit omap_cpufreq_exit(void) 242731e0cc6SSantosh Shilimkar { 243731e0cc6SSantosh Shilimkar cpufreq_unregister_driver(&omap_driver); 244731e0cc6SSantosh Shilimkar } 245731e0cc6SSantosh Shilimkar 246731e0cc6SSantosh Shilimkar MODULE_DESCRIPTION("cpufreq driver for OMAP SoCs"); 247731e0cc6SSantosh Shilimkar MODULE_LICENSE("GPL"); 248731e0cc6SSantosh Shilimkar module_init(omap_cpufreq_init); 249731e0cc6SSantosh Shilimkar module_exit(omap_cpufreq_exit); 250