1*731e0cc6SSantosh Shilimkar /* 2*731e0cc6SSantosh Shilimkar * CPU frequency scaling for OMAP 3*731e0cc6SSantosh Shilimkar * 4*731e0cc6SSantosh Shilimkar * Copyright (C) 2005 Nokia Corporation 5*731e0cc6SSantosh Shilimkar * Written by Tony Lindgren <tony@atomide.com> 6*731e0cc6SSantosh Shilimkar * 7*731e0cc6SSantosh Shilimkar * Based on cpu-sa1110.c, Copyright (C) 2001 Russell King 8*731e0cc6SSantosh Shilimkar * 9*731e0cc6SSantosh Shilimkar * Copyright (C) 2007-2011 Texas Instruments, Inc. 10*731e0cc6SSantosh Shilimkar * - OMAP3/4 support by Rajendra Nayak, Santosh Shilimkar 11*731e0cc6SSantosh Shilimkar * 12*731e0cc6SSantosh Shilimkar * This program is free software; you can redistribute it and/or modify 13*731e0cc6SSantosh Shilimkar * it under the terms of the GNU General Public License version 2 as 14*731e0cc6SSantosh Shilimkar * published by the Free Software Foundation. 15*731e0cc6SSantosh Shilimkar */ 16*731e0cc6SSantosh Shilimkar #include <linux/types.h> 17*731e0cc6SSantosh Shilimkar #include <linux/kernel.h> 18*731e0cc6SSantosh Shilimkar #include <linux/sched.h> 19*731e0cc6SSantosh Shilimkar #include <linux/cpufreq.h> 20*731e0cc6SSantosh Shilimkar #include <linux/delay.h> 21*731e0cc6SSantosh Shilimkar #include <linux/init.h> 22*731e0cc6SSantosh Shilimkar #include <linux/err.h> 23*731e0cc6SSantosh Shilimkar #include <linux/clk.h> 24*731e0cc6SSantosh Shilimkar #include <linux/io.h> 25*731e0cc6SSantosh Shilimkar #include <linux/opp.h> 26*731e0cc6SSantosh Shilimkar 27*731e0cc6SSantosh Shilimkar #include <asm/system.h> 28*731e0cc6SSantosh Shilimkar #include <asm/smp_plat.h> 29*731e0cc6SSantosh Shilimkar 30*731e0cc6SSantosh Shilimkar #include <plat/clock.h> 31*731e0cc6SSantosh Shilimkar #include <plat/omap-pm.h> 32*731e0cc6SSantosh Shilimkar #include <plat/common.h> 33*731e0cc6SSantosh Shilimkar 34*731e0cc6SSantosh Shilimkar #include <mach/hardware.h> 35*731e0cc6SSantosh Shilimkar 36*731e0cc6SSantosh Shilimkar #define VERY_HI_RATE 900000000 37*731e0cc6SSantosh Shilimkar 38*731e0cc6SSantosh Shilimkar static struct cpufreq_frequency_table *freq_table; 39*731e0cc6SSantosh Shilimkar static struct clk *mpu_clk; 40*731e0cc6SSantosh Shilimkar 41*731e0cc6SSantosh Shilimkar static int omap_verify_speed(struct cpufreq_policy *policy) 42*731e0cc6SSantosh Shilimkar { 43*731e0cc6SSantosh Shilimkar if (freq_table) 44*731e0cc6SSantosh Shilimkar return cpufreq_frequency_table_verify(policy, freq_table); 45*731e0cc6SSantosh Shilimkar 46*731e0cc6SSantosh Shilimkar if (policy->cpu) 47*731e0cc6SSantosh Shilimkar return -EINVAL; 48*731e0cc6SSantosh Shilimkar 49*731e0cc6SSantosh Shilimkar cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, 50*731e0cc6SSantosh Shilimkar policy->cpuinfo.max_freq); 51*731e0cc6SSantosh Shilimkar 52*731e0cc6SSantosh Shilimkar policy->min = clk_round_rate(mpu_clk, policy->min * 1000) / 1000; 53*731e0cc6SSantosh Shilimkar policy->max = clk_round_rate(mpu_clk, policy->max * 1000) / 1000; 54*731e0cc6SSantosh Shilimkar cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, 55*731e0cc6SSantosh Shilimkar policy->cpuinfo.max_freq); 56*731e0cc6SSantosh Shilimkar return 0; 57*731e0cc6SSantosh Shilimkar } 58*731e0cc6SSantosh Shilimkar 59*731e0cc6SSantosh Shilimkar static unsigned int omap_getspeed(unsigned int cpu) 60*731e0cc6SSantosh Shilimkar { 61*731e0cc6SSantosh Shilimkar unsigned long rate; 62*731e0cc6SSantosh Shilimkar 63*731e0cc6SSantosh Shilimkar if (cpu) 64*731e0cc6SSantosh Shilimkar return 0; 65*731e0cc6SSantosh Shilimkar 66*731e0cc6SSantosh Shilimkar rate = clk_get_rate(mpu_clk) / 1000; 67*731e0cc6SSantosh Shilimkar return rate; 68*731e0cc6SSantosh Shilimkar } 69*731e0cc6SSantosh Shilimkar 70*731e0cc6SSantosh Shilimkar static int omap_target(struct cpufreq_policy *policy, 71*731e0cc6SSantosh Shilimkar unsigned int target_freq, 72*731e0cc6SSantosh Shilimkar unsigned int relation) 73*731e0cc6SSantosh Shilimkar { 74*731e0cc6SSantosh Shilimkar int ret = 0; 75*731e0cc6SSantosh Shilimkar struct cpufreq_freqs freqs; 76*731e0cc6SSantosh Shilimkar 77*731e0cc6SSantosh Shilimkar /* Ensure desired rate is within allowed range. Some govenors 78*731e0cc6SSantosh Shilimkar * (ondemand) will just pass target_freq=0 to get the minimum. */ 79*731e0cc6SSantosh Shilimkar if (target_freq < policy->min) 80*731e0cc6SSantosh Shilimkar target_freq = policy->min; 81*731e0cc6SSantosh Shilimkar if (target_freq > policy->max) 82*731e0cc6SSantosh Shilimkar target_freq = policy->max; 83*731e0cc6SSantosh Shilimkar 84*731e0cc6SSantosh Shilimkar freqs.old = omap_getspeed(0); 85*731e0cc6SSantosh Shilimkar freqs.new = clk_round_rate(mpu_clk, target_freq * 1000) / 1000; 86*731e0cc6SSantosh Shilimkar freqs.cpu = 0; 87*731e0cc6SSantosh Shilimkar 88*731e0cc6SSantosh Shilimkar if (freqs.old == freqs.new) 89*731e0cc6SSantosh Shilimkar return ret; 90*731e0cc6SSantosh Shilimkar 91*731e0cc6SSantosh Shilimkar cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); 92*731e0cc6SSantosh Shilimkar 93*731e0cc6SSantosh Shilimkar #ifdef CONFIG_CPU_FREQ_DEBUG 94*731e0cc6SSantosh Shilimkar pr_info("cpufreq-omap: transition: %u --> %u\n", freqs.old, freqs.new); 95*731e0cc6SSantosh Shilimkar #endif 96*731e0cc6SSantosh Shilimkar 97*731e0cc6SSantosh Shilimkar ret = clk_set_rate(mpu_clk, freqs.new * 1000); 98*731e0cc6SSantosh Shilimkar 99*731e0cc6SSantosh Shilimkar cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); 100*731e0cc6SSantosh Shilimkar 101*731e0cc6SSantosh Shilimkar return ret; 102*731e0cc6SSantosh Shilimkar } 103*731e0cc6SSantosh Shilimkar 104*731e0cc6SSantosh Shilimkar static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) 105*731e0cc6SSantosh Shilimkar { 106*731e0cc6SSantosh Shilimkar int result = 0; 107*731e0cc6SSantosh Shilimkar struct device *mpu_dev; 108*731e0cc6SSantosh Shilimkar 109*731e0cc6SSantosh Shilimkar if (cpu_is_omap24xx()) 110*731e0cc6SSantosh Shilimkar mpu_clk = clk_get(NULL, "virt_prcm_set"); 111*731e0cc6SSantosh Shilimkar else if (cpu_is_omap34xx()) 112*731e0cc6SSantosh Shilimkar mpu_clk = clk_get(NULL, "dpll1_ck"); 113*731e0cc6SSantosh Shilimkar else if (cpu_is_omap44xx()) 114*731e0cc6SSantosh Shilimkar mpu_clk = clk_get(NULL, "dpll_mpu_ck"); 115*731e0cc6SSantosh Shilimkar 116*731e0cc6SSantosh Shilimkar if (IS_ERR(mpu_clk)) 117*731e0cc6SSantosh Shilimkar return PTR_ERR(mpu_clk); 118*731e0cc6SSantosh Shilimkar 119*731e0cc6SSantosh Shilimkar if (policy->cpu != 0) 120*731e0cc6SSantosh Shilimkar return -EINVAL; 121*731e0cc6SSantosh Shilimkar 122*731e0cc6SSantosh Shilimkar policy->cur = policy->min = policy->max = omap_getspeed(0); 123*731e0cc6SSantosh Shilimkar 124*731e0cc6SSantosh Shilimkar mpu_dev = omap2_get_mpuss_device(); 125*731e0cc6SSantosh Shilimkar if (!mpu_dev) { 126*731e0cc6SSantosh Shilimkar pr_warning("%s: unable to get the mpu device\n", __func__); 127*731e0cc6SSantosh Shilimkar return -EINVAL; 128*731e0cc6SSantosh Shilimkar } 129*731e0cc6SSantosh Shilimkar opp_init_cpufreq_table(mpu_dev, &freq_table); 130*731e0cc6SSantosh Shilimkar 131*731e0cc6SSantosh Shilimkar if (freq_table) { 132*731e0cc6SSantosh Shilimkar result = cpufreq_frequency_table_cpuinfo(policy, freq_table); 133*731e0cc6SSantosh Shilimkar if (!result) 134*731e0cc6SSantosh Shilimkar cpufreq_frequency_table_get_attr(freq_table, 135*731e0cc6SSantosh Shilimkar policy->cpu); 136*731e0cc6SSantosh Shilimkar } else { 137*731e0cc6SSantosh Shilimkar policy->cpuinfo.min_freq = clk_round_rate(mpu_clk, 0) / 1000; 138*731e0cc6SSantosh Shilimkar policy->cpuinfo.max_freq = clk_round_rate(mpu_clk, 139*731e0cc6SSantosh Shilimkar VERY_HI_RATE) / 1000; 140*731e0cc6SSantosh Shilimkar } 141*731e0cc6SSantosh Shilimkar 142*731e0cc6SSantosh Shilimkar policy->min = policy->cpuinfo.min_freq; 143*731e0cc6SSantosh Shilimkar policy->max = policy->cpuinfo.max_freq; 144*731e0cc6SSantosh Shilimkar policy->cur = omap_getspeed(0); 145*731e0cc6SSantosh Shilimkar 146*731e0cc6SSantosh Shilimkar /* FIXME: what's the actual transition time? */ 147*731e0cc6SSantosh Shilimkar policy->cpuinfo.transition_latency = 300 * 1000; 148*731e0cc6SSantosh Shilimkar 149*731e0cc6SSantosh Shilimkar return 0; 150*731e0cc6SSantosh Shilimkar } 151*731e0cc6SSantosh Shilimkar 152*731e0cc6SSantosh Shilimkar static int omap_cpu_exit(struct cpufreq_policy *policy) 153*731e0cc6SSantosh Shilimkar { 154*731e0cc6SSantosh Shilimkar clk_exit_cpufreq_table(&freq_table); 155*731e0cc6SSantosh Shilimkar clk_put(mpu_clk); 156*731e0cc6SSantosh Shilimkar return 0; 157*731e0cc6SSantosh Shilimkar } 158*731e0cc6SSantosh Shilimkar 159*731e0cc6SSantosh Shilimkar static struct freq_attr *omap_cpufreq_attr[] = { 160*731e0cc6SSantosh Shilimkar &cpufreq_freq_attr_scaling_available_freqs, 161*731e0cc6SSantosh Shilimkar NULL, 162*731e0cc6SSantosh Shilimkar }; 163*731e0cc6SSantosh Shilimkar 164*731e0cc6SSantosh Shilimkar static struct cpufreq_driver omap_driver = { 165*731e0cc6SSantosh Shilimkar .flags = CPUFREQ_STICKY, 166*731e0cc6SSantosh Shilimkar .verify = omap_verify_speed, 167*731e0cc6SSantosh Shilimkar .target = omap_target, 168*731e0cc6SSantosh Shilimkar .get = omap_getspeed, 169*731e0cc6SSantosh Shilimkar .init = omap_cpu_init, 170*731e0cc6SSantosh Shilimkar .exit = omap_cpu_exit, 171*731e0cc6SSantosh Shilimkar .name = "omap", 172*731e0cc6SSantosh Shilimkar .attr = omap_cpufreq_attr, 173*731e0cc6SSantosh Shilimkar }; 174*731e0cc6SSantosh Shilimkar 175*731e0cc6SSantosh Shilimkar static int __init omap_cpufreq_init(void) 176*731e0cc6SSantosh Shilimkar { 177*731e0cc6SSantosh Shilimkar return cpufreq_register_driver(&omap_driver); 178*731e0cc6SSantosh Shilimkar } 179*731e0cc6SSantosh Shilimkar 180*731e0cc6SSantosh Shilimkar static void __exit omap_cpufreq_exit(void) 181*731e0cc6SSantosh Shilimkar { 182*731e0cc6SSantosh Shilimkar cpufreq_unregister_driver(&omap_driver); 183*731e0cc6SSantosh Shilimkar } 184*731e0cc6SSantosh Shilimkar 185*731e0cc6SSantosh Shilimkar MODULE_DESCRIPTION("cpufreq driver for OMAP SoCs"); 186*731e0cc6SSantosh Shilimkar MODULE_LICENSE("GPL"); 187*731e0cc6SSantosh Shilimkar module_init(omap_cpufreq_init); 188*731e0cc6SSantosh Shilimkar module_exit(omap_cpufreq_exit); 189