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
vco_to_freq(struct vco vco,int factor)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
freq_to_vco(unsigned int freq_khz,int factor)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 */
integrator_validatespeed(unsigned int freq_khz)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
integrator_setspeed(unsigned int freq_khz)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
cpu_init(void)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);
135