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