Path: blob/master/arch/blackfin/mach-common/cpufreq.c
10817 views
/*1* Blackfin core clock scaling2*3* Copyright 2008-2011 Analog Devices Inc.4*5* Licensed under the GPL-2 or later.6*/78#include <linux/kernel.h>9#include <linux/types.h>10#include <linux/init.h>11#include <linux/cpufreq.h>12#include <linux/fs.h>13#include <linux/delay.h>14#include <asm/blackfin.h>15#include <asm/time.h>16#include <asm/dpmc.h>1718/* this is the table of CCLK frequencies, in Hz */19/* .index is the entry in the auxiliary dpm_state_table[] */20static struct cpufreq_frequency_table bfin_freq_table[] = {21{22.frequency = CPUFREQ_TABLE_END,23.index = 0,24},25{26.frequency = CPUFREQ_TABLE_END,27.index = 1,28},29{30.frequency = CPUFREQ_TABLE_END,31.index = 2,32},33{34.frequency = CPUFREQ_TABLE_END,35.index = 0,36},37};3839static struct bfin_dpm_state {40unsigned int csel; /* system clock divider */41unsigned int tscale; /* change the divider on the core timer interrupt */42} dpm_state_table[3];4344#if defined(CONFIG_CYCLES_CLOCKSOURCE)45/*46* normalized to maximum frequency offset for CYCLES,47* used in time-ts cycles clock source, but could be used48* somewhere also.49*/50unsigned long long __bfin_cycles_off;51unsigned int __bfin_cycles_mod;52#endif5354/**************************************************************************/55static void __init bfin_init_tables(unsigned long cclk, unsigned long sclk)56{5758unsigned long csel, min_cclk;59int index;6061/* Anomaly 273 seems to still exist on non-BF54x w/dcache turned on */62#if ANOMALY_05000273 || ANOMALY_05000274 || \63(!defined(CONFIG_BF54x) && defined(CONFIG_BFIN_EXTMEM_DCACHEABLE))64min_cclk = sclk * 2;65#else66min_cclk = sclk;67#endif68csel = ((bfin_read_PLL_DIV() & CSEL) >> 4);6970for (index = 0; (cclk >> index) >= min_cclk && csel <= 3; index++, csel++) {71bfin_freq_table[index].frequency = cclk >> index;72dpm_state_table[index].csel = csel << 4; /* Shift now into PLL_DIV bitpos */73dpm_state_table[index].tscale = (TIME_SCALE / (1 << csel)) - 1;7475pr_debug("cpufreq: freq:%d csel:0x%x tscale:%d\n",76bfin_freq_table[index].frequency,77dpm_state_table[index].csel,78dpm_state_table[index].tscale);79}80return;81}8283static void bfin_adjust_core_timer(void *info)84{85unsigned int tscale;86unsigned int index = *(unsigned int *)info;8788/* we have to adjust the core timer, because it is using cclk */89tscale = dpm_state_table[index].tscale;90bfin_write_TSCALE(tscale);91return;92}9394static unsigned int bfin_getfreq_khz(unsigned int cpu)95{96/* Both CoreA/B have the same core clock */97return get_cclk() / 1000;98}99100static int bfin_target(struct cpufreq_policy *poli,101unsigned int target_freq, unsigned int relation)102{103unsigned int index, plldiv, cpu;104unsigned long flags, cclk_hz;105struct cpufreq_freqs freqs;106static unsigned long lpj_ref;107static unsigned int lpj_ref_freq;108109#if defined(CONFIG_CYCLES_CLOCKSOURCE)110cycles_t cycles;111#endif112113for_each_online_cpu(cpu) {114struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);115116if (!policy)117continue;118119if (cpufreq_frequency_table_target(policy, bfin_freq_table,120target_freq, relation, &index))121return -EINVAL;122123cclk_hz = bfin_freq_table[index].frequency;124125freqs.old = bfin_getfreq_khz(0);126freqs.new = cclk_hz;127freqs.cpu = cpu;128129pr_debug("cpufreq: changing cclk to %lu; target = %u, oldfreq = %u\n",130cclk_hz, target_freq, freqs.old);131132cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);133if (cpu == CPUFREQ_CPU) {134flags = hard_local_irq_save();135plldiv = (bfin_read_PLL_DIV() & SSEL) |136dpm_state_table[index].csel;137bfin_write_PLL_DIV(plldiv);138on_each_cpu(bfin_adjust_core_timer, &index, 1);139#if defined(CONFIG_CYCLES_CLOCKSOURCE)140cycles = get_cycles();141SSYNC();142cycles += 10; /* ~10 cycles we lose after get_cycles() */143__bfin_cycles_off +=144(cycles << __bfin_cycles_mod) - (cycles << index);145__bfin_cycles_mod = index;146#endif147if (!lpj_ref_freq) {148lpj_ref = loops_per_jiffy;149lpj_ref_freq = freqs.old;150}151if (freqs.new != freqs.old) {152loops_per_jiffy = cpufreq_scale(lpj_ref,153lpj_ref_freq, freqs.new);154}155hard_local_irq_restore(flags);156}157/* TODO: just test case for cycles clock source, remove later */158cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);159}160161pr_debug("cpufreq: done\n");162return 0;163}164165static int bfin_verify_speed(struct cpufreq_policy *policy)166{167return cpufreq_frequency_table_verify(policy, bfin_freq_table);168}169170static int __init __bfin_cpu_init(struct cpufreq_policy *policy)171{172173unsigned long cclk, sclk;174175cclk = get_cclk() / 1000;176sclk = get_sclk() / 1000;177178if (policy->cpu == CPUFREQ_CPU)179bfin_init_tables(cclk, sclk);180181policy->cpuinfo.transition_latency = 50000; /* 50us assumed */182183policy->cur = cclk;184cpufreq_frequency_table_get_attr(bfin_freq_table, policy->cpu);185return cpufreq_frequency_table_cpuinfo(policy, bfin_freq_table);186}187188static struct freq_attr *bfin_freq_attr[] = {189&cpufreq_freq_attr_scaling_available_freqs,190NULL,191};192193static struct cpufreq_driver bfin_driver = {194.verify = bfin_verify_speed,195.target = bfin_target,196.get = bfin_getfreq_khz,197.init = __bfin_cpu_init,198.name = "bfin cpufreq",199.owner = THIS_MODULE,200.attr = bfin_freq_attr,201};202203static int __init bfin_cpu_init(void)204{205return cpufreq_register_driver(&bfin_driver);206}207208static void __exit bfin_cpu_exit(void)209{210cpufreq_unregister_driver(&bfin_driver);211}212213MODULE_AUTHOR("Michael Hennerich <[email protected]>");214MODULE_DESCRIPTION("cpufreq driver for Blackfin");215MODULE_LICENSE("GPL");216217module_init(bfin_cpu_init);218module_exit(bfin_cpu_exit);219220221