Merge branch 'fix/hda' into for-linus
[pandora-kernel.git] / arch / blackfin / mach-common / cpufreq.c
1 /*
2  * Blackfin core clock scaling
3  *
4  * Copyright 2008-2009 Analog Devices Inc.
5  *
6  * Licensed under the GPL-2 or later.
7  */
8
9 #include <linux/kernel.h>
10 #include <linux/types.h>
11 #include <linux/init.h>
12 #include <linux/cpufreq.h>
13 #include <linux/fs.h>
14 #include <asm/blackfin.h>
15 #include <asm/time.h>
16 #include <asm/dpmc.h>
17
18 /* this is the table of CCLK frequencies, in Hz */
19 /* .index is the entry in the auxillary dpm_state_table[] */
20 static 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 };
38
39 static struct bfin_dpm_state {
40         unsigned int csel; /* system clock divider */
41         unsigned int tscale; /* change the divider on the core timer interrupt */
42 } dpm_state_table[3];
43
44 /*
45    normalized to maximum frequncy offset for CYCLES,
46    used in time-ts cycles clock source, but could be used
47    somewhere also.
48  */
49 unsigned long long __bfin_cycles_off;
50 unsigned int __bfin_cycles_mod;
51
52 /**************************************************************************/
53
54 static unsigned int bfin_getfreq_khz(unsigned int cpu)
55 {
56         /* The driver only support single cpu */
57         if (cpu != 0)
58                 return -1;
59
60         return get_cclk() / 1000;
61 }
62
63
64 static int bfin_target(struct cpufreq_policy *policy,
65                         unsigned int target_freq, unsigned int relation)
66 {
67         unsigned int index, plldiv, tscale;
68         unsigned long flags, cclk_hz;
69         struct cpufreq_freqs freqs;
70         cycles_t cycles;
71
72         if (cpufreq_frequency_table_target(policy, bfin_freq_table,
73                  target_freq, relation, &index))
74                 return -EINVAL;
75
76         cclk_hz = bfin_freq_table[index].frequency;
77
78         freqs.old = bfin_getfreq_khz(0);
79         freqs.new = cclk_hz;
80         freqs.cpu = 0;
81
82         pr_debug("cpufreq: changing cclk to %lu; target = %u, oldfreq = %u\n",
83                  cclk_hz, target_freq, freqs.old);
84
85         cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
86         local_irq_save_hw(flags);
87                 plldiv = (bfin_read_PLL_DIV() & SSEL) | dpm_state_table[index].csel;
88                 tscale = dpm_state_table[index].tscale;
89                 bfin_write_PLL_DIV(plldiv);
90                 /* we have to adjust the core timer, because it is using cclk */
91                 bfin_write_TSCALE(tscale);
92                 cycles = get_cycles();
93                 SSYNC();
94         cycles += 10; /* ~10 cycles we lose after get_cycles() */
95         __bfin_cycles_off += (cycles << __bfin_cycles_mod) - (cycles << index);
96         __bfin_cycles_mod = index;
97         local_irq_restore_hw(flags);
98         /* TODO: just test case for cycles clock source, remove later */
99         pr_debug("cpufreq: done\n");
100         cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
101
102         return 0;
103 }
104
105 static int bfin_verify_speed(struct cpufreq_policy *policy)
106 {
107         return cpufreq_frequency_table_verify(policy, bfin_freq_table);
108 }
109
110 static int __init __bfin_cpu_init(struct cpufreq_policy *policy)
111 {
112
113         unsigned long cclk, sclk, csel, min_cclk;
114         int index;
115
116         if (policy->cpu != 0)
117                 return -EINVAL;
118
119         cclk = get_cclk() / 1000;
120         sclk = get_sclk() / 1000;
121
122 #if ANOMALY_05000273 || ANOMALY_05000274 || \
123         (!defined(CONFIG_BF54x) && defined(CONFIG_BFIN_EXTMEM_DCACHEABLE))
124         min_cclk = sclk * 2;
125 #else
126         min_cclk = sclk;
127 #endif
128         csel = ((bfin_read_PLL_DIV() & CSEL) >> 4);
129
130         for (index = 0;  (cclk >> index) >= min_cclk && csel <= 3; index++, csel++) {
131                 bfin_freq_table[index].frequency = cclk >> index;
132                 dpm_state_table[index].csel = csel << 4; /* Shift now into PLL_DIV bitpos */
133                 dpm_state_table[index].tscale =  (TIME_SCALE / (1 << csel)) - 1;
134
135                 pr_debug("cpufreq: freq:%d csel:0x%x tscale:%d\n",
136                                                  bfin_freq_table[index].frequency,
137                                                  dpm_state_table[index].csel,
138                                                  dpm_state_table[index].tscale);
139         }
140
141         policy->cpuinfo.transition_latency = 50000; /* 50us assumed */
142
143         /*Now ,only support one cpu */
144         policy->cur = cclk;
145         cpufreq_frequency_table_get_attr(bfin_freq_table, policy->cpu);
146         return cpufreq_frequency_table_cpuinfo(policy, bfin_freq_table);
147 }
148
149 static struct freq_attr *bfin_freq_attr[] = {
150         &cpufreq_freq_attr_scaling_available_freqs,
151         NULL,
152 };
153
154 static struct cpufreq_driver bfin_driver = {
155         .verify = bfin_verify_speed,
156         .target = bfin_target,
157         .get = bfin_getfreq_khz,
158         .init = __bfin_cpu_init,
159         .name = "bfin cpufreq",
160         .owner = THIS_MODULE,
161         .attr = bfin_freq_attr,
162 };
163
164 static int __init bfin_cpu_init(void)
165 {
166         return cpufreq_register_driver(&bfin_driver);
167 }
168
169 static void __exit bfin_cpu_exit(void)
170 {
171         cpufreq_unregister_driver(&bfin_driver);
172 }
173
174 MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
175 MODULE_DESCRIPTION("cpufreq driver for Blackfin");
176 MODULE_LICENSE("GPL");
177
178 module_init(bfin_cpu_init);
179 module_exit(bfin_cpu_exit);