]> git.saurik.com Git - apple/xnu.git/blob - pexpert/ppc/pe_clock_speed.c
xnu-344.23.tar.gz
[apple/xnu.git] / pexpert / ppc / pe_clock_speed.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 * pe_clock_speed.c - Determine the best guess for the processor and bus
24 * speed buy using the values returned by run_clock_test.
25 *
26 * (c) Apple Computer, Inc. 1998-2000
27 *
28 * Writen by: Josh de Cesare
29 *
30 */
31
32 #include <pexpert/pexpert.h>
33
34 #include <ppc/machine_routines.h>
35
36 // prototypes
37 extern void pe_run_clock_test(void *tmp);
38 void pe_do_clock_test(unsigned int via_addr,
39 int num_speeds, unsigned long *speed_list);
40
41 // Threshold for bus speed matches.
42 #define kMaxFreqDiff (30000)
43
44 // This is the structure for the data that get passed to pe_run_clock_test.
45 struct clock_test_data {
46 unsigned int via_addr;
47 unsigned int via_ticks;
48 unsigned int dec_ticks;
49 };
50
51 // glocal variables to simplify some stuff.
52 static long bus_freq_num, bus_freq_den, cpu_pll;
53
54 // PE_Determine_Clock_Speeds is called by the via driver in IOKit
55 // It uses the numbers generated by pe_do_clock_test and reports
56 // the cleaned up values to the rest of the OS.
57 void PE_Determine_Clock_Speeds(unsigned int via_addr, int num_speeds,
58 unsigned long *speed_list)
59 {
60 boolean_t oldLevel;
61 unsigned long tmp_bus_speed, tmp_cpu_speed;
62 unsigned long long tmp;
63
64 oldLevel = ml_set_interrupts_enabled(FALSE);
65 pe_do_clock_test(via_addr, num_speeds, speed_list);
66 ml_set_interrupts_enabled(oldLevel);
67
68 tmp_bus_speed = bus_freq_num / bus_freq_den;
69 tmp = ((unsigned long long)bus_freq_num * cpu_pll) / (bus_freq_den * 2);
70 tmp_cpu_speed = (unsigned long)tmp;
71
72 // Report the bus clock rate as is.
73 gPEClockFrequencyInfo.bus_clock_rate_num = bus_freq_num;
74 gPEClockFrequencyInfo.bus_clock_rate_den = bus_freq_den;
75
76 // pll multipliers are in halfs so set the denominator to 2.
77 gPEClockFrequencyInfo.bus_to_cpu_rate_num = cpu_pll;
78 gPEClockFrequencyInfo.bus_to_cpu_rate_den = 2;
79
80 // The decrementer rate is one fourth the bus rate.
81 gPEClockFrequencyInfo.bus_to_dec_rate_num = 1;
82 gPEClockFrequencyInfo.bus_to_dec_rate_den = 4;
83
84 // Set the truncated numbers in gPEClockFrequencyInfo.
85 gPEClockFrequencyInfo.bus_clock_rate_hz = tmp_bus_speed;
86 gPEClockFrequencyInfo.cpu_clock_rate_hz = tmp_cpu_speed;
87 gPEClockFrequencyInfo.dec_clock_rate_hz = tmp_bus_speed / 4;
88
89 PE_call_timebase_callback();
90 }
91
92 // pe_do_clock_test uses the number from pe_run_clock_test to
93 // find a best fit guess for the bus speed.
94 void pe_do_clock_test(unsigned int via_addr,
95 int num_speeds, unsigned long *speed_list)
96 {
97 struct clock_test_data clock_test_data;
98 long cnt, diff, raw_cpu_freq, raw_bus_freq, tmp_bus_freq,
99 last_bus_freq, tries = 10;
100
101 // Save the via addr so the asm part can use it.
102 clock_test_data.via_addr = via_addr;
103
104 // Keep looping until it matches the last try.
105 bus_freq_num = 0;
106 do {
107 last_bus_freq = bus_freq_num;
108
109 // The the asm part to do the real work.
110 pe_run_clock_test((void *)&clock_test_data);
111
112 // First find the pll mode. Allow any integer times two.
113 cpu_pll = 10000000 / clock_test_data.dec_ticks;
114 cpu_pll = (cpu_pll / 2) + (cpu_pll & 1);
115
116 // Using 64 bit math figure out the raw bus speed.
117 // 0xBF401675E5DULL is 1 / 1.27655us times 2 ^ 24.
118 raw_bus_freq = ((0xBF401675E5DULL * clock_test_data.dec_ticks) /
119 clock_test_data.via_ticks) >> 22;
120
121 // use the pll mode and the raw bus speed to find the raw cpu speed.
122 raw_cpu_freq = raw_bus_freq * cpu_pll / 2;
123
124 // Look to see if the bus speed is close to one of the
125 // speeds in the table.
126 for (cnt = 0; cnt < num_speeds; cnt++) {
127 bus_freq_num = speed_list[cnt * 2];
128 bus_freq_den = speed_list[cnt * 2 + 1];
129 diff = bus_freq_num - raw_bus_freq * bus_freq_den;
130 if (diff < 0) diff = -diff;
131
132 if (diff < kMaxFreqDiff * bus_freq_den) break;
133 }
134 if (cnt != num_speeds) continue;
135
136 // Look to see if the bus speed is close to n * 0.5 MHz
137 tmp_bus_freq = ((raw_bus_freq + 250000) / 500000) * 500000;
138
139 diff = tmp_bus_freq - raw_bus_freq;
140 if (diff < 0) diff = -diff;
141
142 if (diff < kMaxFreqDiff) {
143 bus_freq_num = tmp_bus_freq;
144 bus_freq_den = 1;
145 continue;
146 }
147
148 // Look to see if the bus speed is close to n * 50/3 MHz
149 tmp_bus_freq = ((raw_bus_freq * 3 + 25000000) / 50000000) * 50000000;
150
151 diff = tmp_bus_freq - raw_bus_freq * 3;
152 if (diff < 0) diff = -diff;
153
154 if (diff < kMaxFreqDiff * 3) {
155 bus_freq_num = tmp_bus_freq;
156 bus_freq_den = 3;
157 continue;
158 }
159
160 // Since all else failed return the raw bus speed
161 bus_freq_num = raw_bus_freq;
162 bus_freq_den = 1;
163 } while ((bus_freq_num != last_bus_freq) && tries--);
164 }