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> 2646c12216SRussell King #include <linux/cpu.h> 27731e0cc6SSantosh Shilimkar 28731e0cc6SSantosh Shilimkar #include <asm/system.h> 29731e0cc6SSantosh Shilimkar #include <asm/smp_plat.h> 3046c12216SRussell 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 4046c12216SRussell King #ifdef CONFIG_SMP 4146c12216SRussell King struct lpj_info { 4246c12216SRussell King unsigned long ref; 4346c12216SRussell King unsigned int freq; 4446c12216SRussell King }; 4546c12216SRussell King 4646c12216SRussell King static DEFINE_PER_CPU(struct lpj_info, lpj_ref); 4746c12216SRussell King static struct lpj_info global_lpj_ref; 4846c12216SRussell King #endif 4946c12216SRussell King 50731e0cc6SSantosh Shilimkar static struct cpufreq_frequency_table *freq_table; 51731e0cc6SSantosh Shilimkar static struct clk *mpu_clk; 5208ca3e3bSNishanth Menon static char *mpu_clk_name; 53*a820ffa8SNishanth Menon static struct device *mpu_dev; 54731e0cc6SSantosh Shilimkar 55731e0cc6SSantosh Shilimkar static int omap_verify_speed(struct cpufreq_policy *policy) 56731e0cc6SSantosh Shilimkar { 57731e0cc6SSantosh Shilimkar if (freq_table) 58731e0cc6SSantosh Shilimkar return cpufreq_frequency_table_verify(policy, freq_table); 59731e0cc6SSantosh Shilimkar 60731e0cc6SSantosh Shilimkar if (policy->cpu) 61731e0cc6SSantosh Shilimkar return -EINVAL; 62731e0cc6SSantosh Shilimkar 63731e0cc6SSantosh Shilimkar cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, 64731e0cc6SSantosh Shilimkar policy->cpuinfo.max_freq); 65731e0cc6SSantosh Shilimkar 66731e0cc6SSantosh Shilimkar policy->min = clk_round_rate(mpu_clk, policy->min * 1000) / 1000; 67731e0cc6SSantosh Shilimkar policy->max = clk_round_rate(mpu_clk, policy->max * 1000) / 1000; 68731e0cc6SSantosh Shilimkar cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, 69731e0cc6SSantosh Shilimkar policy->cpuinfo.max_freq); 70731e0cc6SSantosh Shilimkar return 0; 71731e0cc6SSantosh Shilimkar } 72731e0cc6SSantosh Shilimkar 73731e0cc6SSantosh Shilimkar static unsigned int omap_getspeed(unsigned int cpu) 74731e0cc6SSantosh Shilimkar { 75731e0cc6SSantosh Shilimkar unsigned long rate; 76731e0cc6SSantosh Shilimkar 7746c12216SRussell King if (cpu >= NR_CPUS) 78731e0cc6SSantosh Shilimkar return 0; 79731e0cc6SSantosh Shilimkar 80731e0cc6SSantosh Shilimkar rate = clk_get_rate(mpu_clk) / 1000; 81731e0cc6SSantosh Shilimkar return rate; 82731e0cc6SSantosh Shilimkar } 83731e0cc6SSantosh Shilimkar 84731e0cc6SSantosh Shilimkar static int omap_target(struct cpufreq_policy *policy, 85731e0cc6SSantosh Shilimkar unsigned int target_freq, 86731e0cc6SSantosh Shilimkar unsigned int relation) 87731e0cc6SSantosh Shilimkar { 8846c12216SRussell King int i, ret = 0; 89731e0cc6SSantosh Shilimkar struct cpufreq_freqs freqs; 90731e0cc6SSantosh Shilimkar 91731e0cc6SSantosh Shilimkar /* Ensure desired rate is within allowed range. Some govenors 92731e0cc6SSantosh Shilimkar * (ondemand) will just pass target_freq=0 to get the minimum. */ 93731e0cc6SSantosh Shilimkar if (target_freq < policy->min) 94731e0cc6SSantosh Shilimkar target_freq = policy->min; 95731e0cc6SSantosh Shilimkar if (target_freq > policy->max) 96731e0cc6SSantosh Shilimkar target_freq = policy->max; 97731e0cc6SSantosh Shilimkar 9846c12216SRussell King freqs.old = omap_getspeed(policy->cpu); 99731e0cc6SSantosh Shilimkar freqs.new = clk_round_rate(mpu_clk, target_freq * 1000) / 1000; 10046c12216SRussell King freqs.cpu = policy->cpu; 101731e0cc6SSantosh Shilimkar 102022ac03bSColin Cross if (freqs.old == freqs.new && policy->cur == freqs.new) 103731e0cc6SSantosh Shilimkar return ret; 104731e0cc6SSantosh Shilimkar 10546c12216SRussell King /* notifiers */ 10646c12216SRussell King for_each_cpu(i, policy->cpus) { 10746c12216SRussell King freqs.cpu = i; 108731e0cc6SSantosh Shilimkar cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); 10946c12216SRussell King } 110731e0cc6SSantosh Shilimkar 111731e0cc6SSantosh Shilimkar #ifdef CONFIG_CPU_FREQ_DEBUG 112731e0cc6SSantosh Shilimkar pr_info("cpufreq-omap: transition: %u --> %u\n", freqs.old, freqs.new); 113731e0cc6SSantosh Shilimkar #endif 114731e0cc6SSantosh Shilimkar 115731e0cc6SSantosh Shilimkar ret = clk_set_rate(mpu_clk, freqs.new * 1000); 11646c12216SRussell King freqs.new = omap_getspeed(policy->cpu); 117731e0cc6SSantosh Shilimkar 11846c12216SRussell King #ifdef CONFIG_SMP 11946c12216SRussell King /* 12046c12216SRussell King * Note that loops_per_jiffy is not updated on SMP systems in 12146c12216SRussell King * cpufreq driver. So, update the per-CPU loops_per_jiffy value 12246c12216SRussell King * on frequency transition. We need to update all dependent CPUs. 12346c12216SRussell King */ 12446c12216SRussell King for_each_cpu(i, policy->cpus) { 12546c12216SRussell King struct lpj_info *lpj = &per_cpu(lpj_ref, i); 12646c12216SRussell King if (!lpj->freq) { 12746c12216SRussell King lpj->ref = per_cpu(cpu_data, i).loops_per_jiffy; 12846c12216SRussell King lpj->freq = freqs.old; 12946c12216SRussell King } 13046c12216SRussell King 13146c12216SRussell King per_cpu(cpu_data, i).loops_per_jiffy = 13246c12216SRussell King cpufreq_scale(lpj->ref, lpj->freq, freqs.new); 13346c12216SRussell King } 13446c12216SRussell King 13546c12216SRussell King /* And don't forget to adjust the global one */ 13646c12216SRussell King if (!global_lpj_ref.freq) { 13746c12216SRussell King global_lpj_ref.ref = loops_per_jiffy; 13846c12216SRussell King global_lpj_ref.freq = freqs.old; 13946c12216SRussell King } 14046c12216SRussell King loops_per_jiffy = cpufreq_scale(global_lpj_ref.ref, global_lpj_ref.freq, 14146c12216SRussell King freqs.new); 14246c12216SRussell King #endif 14346c12216SRussell King 14446c12216SRussell King /* notifiers */ 14546c12216SRussell King for_each_cpu(i, policy->cpus) { 14646c12216SRussell King freqs.cpu = i; 147731e0cc6SSantosh Shilimkar cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); 14846c12216SRussell King } 149731e0cc6SSantosh Shilimkar 150731e0cc6SSantosh Shilimkar return ret; 151731e0cc6SSantosh Shilimkar } 152731e0cc6SSantosh Shilimkar 153731e0cc6SSantosh Shilimkar static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) 154731e0cc6SSantosh Shilimkar { 155731e0cc6SSantosh Shilimkar int result = 0; 156731e0cc6SSantosh Shilimkar 15708ca3e3bSNishanth Menon mpu_clk = clk_get(NULL, mpu_clk_name); 158731e0cc6SSantosh Shilimkar if (IS_ERR(mpu_clk)) 159731e0cc6SSantosh Shilimkar return PTR_ERR(mpu_clk); 160731e0cc6SSantosh Shilimkar 16146c12216SRussell King if (policy->cpu >= NR_CPUS) 162731e0cc6SSantosh Shilimkar return -EINVAL; 163731e0cc6SSantosh Shilimkar 16446c12216SRussell King policy->cur = policy->min = policy->max = omap_getspeed(policy->cpu); 165731e0cc6SSantosh Shilimkar opp_init_cpufreq_table(mpu_dev, &freq_table); 166731e0cc6SSantosh Shilimkar 167731e0cc6SSantosh Shilimkar if (freq_table) { 168731e0cc6SSantosh Shilimkar result = cpufreq_frequency_table_cpuinfo(policy, freq_table); 169731e0cc6SSantosh Shilimkar if (!result) 170731e0cc6SSantosh Shilimkar cpufreq_frequency_table_get_attr(freq_table, 171731e0cc6SSantosh Shilimkar policy->cpu); 172731e0cc6SSantosh Shilimkar } else { 173731e0cc6SSantosh Shilimkar policy->cpuinfo.min_freq = clk_round_rate(mpu_clk, 0) / 1000; 174731e0cc6SSantosh Shilimkar policy->cpuinfo.max_freq = clk_round_rate(mpu_clk, 175731e0cc6SSantosh Shilimkar VERY_HI_RATE) / 1000; 176731e0cc6SSantosh Shilimkar } 177731e0cc6SSantosh Shilimkar 178731e0cc6SSantosh Shilimkar policy->min = policy->cpuinfo.min_freq; 179731e0cc6SSantosh Shilimkar policy->max = policy->cpuinfo.max_freq; 18046c12216SRussell King policy->cur = omap_getspeed(policy->cpu); 18146c12216SRussell King 18246c12216SRussell King /* 18346c12216SRussell King * On OMAP SMP configuartion, both processors share the voltage 18446c12216SRussell King * and clock. So both CPUs needs to be scaled together and hence 18546c12216SRussell King * needs software co-ordination. Use cpufreq affected_cpus 18646c12216SRussell King * interface to handle this scenario. Additional is_smp() check 18746c12216SRussell King * is to keep SMP_ON_UP build working. 18846c12216SRussell King */ 18946c12216SRussell King if (is_smp()) { 19046c12216SRussell King policy->shared_type = CPUFREQ_SHARED_TYPE_ANY; 191ed8ce00cSTodd Poynor cpumask_setall(policy->cpus); 19246c12216SRussell King } 193731e0cc6SSantosh Shilimkar 194731e0cc6SSantosh Shilimkar /* FIXME: what's the actual transition time? */ 195731e0cc6SSantosh Shilimkar policy->cpuinfo.transition_latency = 300 * 1000; 196731e0cc6SSantosh Shilimkar 197731e0cc6SSantosh Shilimkar return 0; 198731e0cc6SSantosh Shilimkar } 199731e0cc6SSantosh Shilimkar 200731e0cc6SSantosh Shilimkar static int omap_cpu_exit(struct cpufreq_policy *policy) 201731e0cc6SSantosh Shilimkar { 202731e0cc6SSantosh Shilimkar clk_exit_cpufreq_table(&freq_table); 203731e0cc6SSantosh Shilimkar clk_put(mpu_clk); 204731e0cc6SSantosh Shilimkar return 0; 205731e0cc6SSantosh Shilimkar } 206731e0cc6SSantosh Shilimkar 207731e0cc6SSantosh Shilimkar static struct freq_attr *omap_cpufreq_attr[] = { 208731e0cc6SSantosh Shilimkar &cpufreq_freq_attr_scaling_available_freqs, 209731e0cc6SSantosh Shilimkar NULL, 210731e0cc6SSantosh Shilimkar }; 211731e0cc6SSantosh Shilimkar 212731e0cc6SSantosh Shilimkar static struct cpufreq_driver omap_driver = { 213731e0cc6SSantosh Shilimkar .flags = CPUFREQ_STICKY, 214731e0cc6SSantosh Shilimkar .verify = omap_verify_speed, 215731e0cc6SSantosh Shilimkar .target = omap_target, 216731e0cc6SSantosh Shilimkar .get = omap_getspeed, 217731e0cc6SSantosh Shilimkar .init = omap_cpu_init, 218731e0cc6SSantosh Shilimkar .exit = omap_cpu_exit, 219731e0cc6SSantosh Shilimkar .name = "omap", 220731e0cc6SSantosh Shilimkar .attr = omap_cpufreq_attr, 221731e0cc6SSantosh Shilimkar }; 222731e0cc6SSantosh Shilimkar 223731e0cc6SSantosh Shilimkar static int __init omap_cpufreq_init(void) 224731e0cc6SSantosh Shilimkar { 22508ca3e3bSNishanth Menon if (cpu_is_omap24xx()) 22608ca3e3bSNishanth Menon mpu_clk_name = "virt_prcm_set"; 22708ca3e3bSNishanth Menon else if (cpu_is_omap34xx()) 22808ca3e3bSNishanth Menon mpu_clk_name = "dpll1_ck"; 22908ca3e3bSNishanth Menon else if (cpu_is_omap44xx()) 23008ca3e3bSNishanth Menon mpu_clk_name = "dpll_mpu_ck"; 23108ca3e3bSNishanth Menon 23208ca3e3bSNishanth Menon if (!mpu_clk_name) { 23308ca3e3bSNishanth Menon pr_err("%s: unsupported Silicon?\n", __func__); 23408ca3e3bSNishanth Menon return -EINVAL; 23508ca3e3bSNishanth Menon } 236*a820ffa8SNishanth Menon 237*a820ffa8SNishanth Menon mpu_dev = omap2_get_mpuss_device(); 238*a820ffa8SNishanth Menon if (!mpu_dev) { 239*a820ffa8SNishanth Menon pr_warning("%s: unable to get the mpu device\n", __func__); 240*a820ffa8SNishanth Menon return -EINVAL; 241*a820ffa8SNishanth Menon } 242*a820ffa8SNishanth Menon 243731e0cc6SSantosh Shilimkar return cpufreq_register_driver(&omap_driver); 244731e0cc6SSantosh Shilimkar } 245731e0cc6SSantosh Shilimkar 246731e0cc6SSantosh Shilimkar static void __exit omap_cpufreq_exit(void) 247731e0cc6SSantosh Shilimkar { 248731e0cc6SSantosh Shilimkar cpufreq_unregister_driver(&omap_driver); 249731e0cc6SSantosh Shilimkar } 250731e0cc6SSantosh Shilimkar 251731e0cc6SSantosh Shilimkar MODULE_DESCRIPTION("cpufreq driver for OMAP SoCs"); 252731e0cc6SSantosh Shilimkar MODULE_LICENSE("GPL"); 253731e0cc6SSantosh Shilimkar module_init(omap_cpufreq_init); 254731e0cc6SSantosh Shilimkar module_exit(omap_cpufreq_exit); 255