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