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