import of upstream 2.4.34.4 from kernel.org
[linux-2.4.git] / arch / arm / mach-integrator / cpu.c
1 /*
2  *  linux/arch/arm/mach-integrator/cpu.c
3  *
4  *  Copyright (C) 2001 Deep Blue Solutions Ltd.
5  *
6  *  $Id: cpu.c,v 1.2.2.1 2002/05/30 15:08:03 db Exp $
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  *
12  * CPU support functions
13  */
14 #include <linux/config.h>
15 #include <linux/types.h>
16 #include <linux/kernel.h>
17 #include <linux/cpufreq.h>
18 #include <linux/init.h>
19
20 #include <asm/hardware.h>
21 #include <asm/io.h>
22
23 #define CM_ID   (IO_ADDRESS(INTEGRATOR_HDR_BASE)+INTEGRATOR_HDR_ID_OFFSET)
24 #define CM_OSC  (IO_ADDRESS(INTEGRATOR_HDR_BASE)+INTEGRATOR_HDR_OSC_OFFSET)
25 #define CM_STAT (IO_ADDRESS(INTEGRATOR_HDR_BASE)+INTEGRATOR_HDR_STAT_OFFSET)
26 #define CM_LOCK (IO_ADDRESS(INTEGRATOR_HDR_BASE)+INTEGRATOR_HDR_LOCK_OFFSET)
27
28 struct vco {
29         unsigned char vdw;
30         unsigned char od;
31 };
32
33 /*
34  * Divisors for each OD setting.
35  */
36 static unsigned char cc_divisor[8] = { 10, 2, 8, 4, 5, 7, 9, 6 };
37
38 static unsigned int vco_to_freq(struct vco vco, int factor)
39 {
40         return 2000 * (vco.vdw + 8) / cc_divisor[vco.od] / factor;
41 }
42
43 #ifdef CONFIG_CPU_FREQ
44 /*
45  * Divisor indexes for in ascending divisor order
46  */
47 static unsigned char s2od[] = { 1, 3, 4, 7, 5, 2, 6, 0 };
48
49 static struct vco freq_to_vco(unsigned int freq_khz, int factor)
50 {
51         struct vco vco = {0, 0};
52         unsigned int i, f;
53
54         freq_khz *= factor;
55
56         for (i = 0; i < 8; i++) {
57                 f = freq_khz * cc_divisor[s2od[i]];
58                 /* f must be between 10MHz and 320MHz */
59                 if (f > 10000 && f <= 320000)
60                         break;
61         }
62
63         vco.od  = s2od[i];
64         vco.vdw = f / 2000 - 8;
65
66         return vco;
67 }
68
69 /*
70  * Validate the speed in khz.  If it is outside our
71  * range, then return the lowest.
72  */
73 unsigned int integrator_validatespeed(unsigned int freq_khz)
74 {
75         struct vco vco;
76
77         if (freq_khz < 12000)
78                 freq_khz = 12000;
79         if (freq_khz > 160000)
80                 freq_khz = 160000;
81
82         vco = freq_to_vco(freq_khz, 1);
83
84         if (vco.vdw < 4 || vco.vdw > 152)
85                 return -EINVAL;
86
87         return vco_to_freq(vco, 1);
88 }
89
90 void integrator_setspeed(unsigned int freq_khz)
91 {
92         struct vco vco = freq_to_vco(freq_khz, 1);
93         u_int cm_osc;
94
95         cm_osc = __raw_readl(CM_OSC);
96         cm_osc &= 0xfffff800;
97         cm_osc |= vco.vdw | vco.od << 8;
98
99         __raw_writel(0xa05f, CM_LOCK);
100         __raw_writel(cm_osc, CM_OSC);
101         __raw_writel(0, CM_LOCK);
102 }
103 #endif
104
105 static int __init cpu_init(void)
106 {
107         u_int cm_osc, cm_stat, cpu_freq_khz, mem_freq_khz;
108         struct vco vco;
109
110         cm_osc = __raw_readl(CM_OSC);
111
112         vco.od  = (cm_osc >> 20) & 7;
113         vco.vdw = (cm_osc >> 12) & 255;
114         mem_freq_khz = vco_to_freq(vco, 2);
115
116         printk(KERN_INFO "Memory clock = %d.%03d MHz\n",
117                 mem_freq_khz / 1000, mem_freq_khz % 1000);
118
119         vco.od = (cm_osc >> 8) & 7;
120         vco.vdw = cm_osc & 255;
121         cpu_freq_khz = vco_to_freq(vco, 1);
122
123 #ifdef CONFIG_CPU_FREQ
124         cpufreq_init(cpu_freq_khz, 1000, 0);
125         cpufreq_setfunctions(integrator_validatespeed, integrator_setspeed);
126 #endif
127
128         cm_stat = __raw_readl(CM_STAT);
129         printk("Module id: %d\n", cm_stat & 255);
130
131         return 0;
132 }
133
134 __initcall(cpu_init);