]> git.saurik.com Git - apple/xnu.git/blame - pexpert/ppc/pe_clock_speed.c
xnu-1228.5.18.tar.gz
[apple/xnu.git] / pexpert / ppc / pe_clock_speed.c
CommitLineData
1c79356b
A
1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
1c79356b 5 *
2d21ac55
A
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.
8f6c56a5 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
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
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
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.
8f6c56a5 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b
A
27 */
28/*
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.
31 *
90556fb8 32 * (c) Apple Computer, Inc. 1998-2002
1c79356b
A
33 *
34 * Writen by: Josh de Cesare
35 *
36 */
37
38#include <pexpert/pexpert.h>
39
40#include <ppc/machine_routines.h>
41
42// prototypes
43extern void pe_run_clock_test(void *tmp);
44void pe_do_clock_test(unsigned int via_addr,
45 int num_speeds, unsigned long *speed_list);
2d21ac55 46void PE_Determine_Clock_Speeds(unsigned int via_addr, int num_speeds, unsigned long *speed_list);
1c79356b
A
47
48// Threshold for bus speed matches.
49#define kMaxFreqDiff (30000)
50
51// This is the structure for the data that get passed to pe_run_clock_test.
52struct clock_test_data {
53 unsigned int via_addr;
54 unsigned int via_ticks;
55 unsigned int dec_ticks;
56};
57
58// glocal variables to simplify some stuff.
59static long bus_freq_num, bus_freq_den, cpu_pll;
60
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.
64void PE_Determine_Clock_Speeds(unsigned int via_addr, int num_speeds,
65 unsigned long *speed_list)
66{
67 boolean_t oldLevel;
68 unsigned long tmp_bus_speed, tmp_cpu_speed;
69 unsigned long long tmp;
70
71 oldLevel = ml_set_interrupts_enabled(FALSE);
72 pe_do_clock_test(via_addr, num_speeds, speed_list);
73 ml_set_interrupts_enabled(oldLevel);
74
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;
78
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;
82
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;
86
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;
90
90556fb8
A
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;
94
1c79356b
A
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;
90556fb8 99 gPEClockFrequencyInfo.timebase_frequency_hz = tmp_bus_speed / 4;
1c79356b 100
43866e37
A
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;
107
1c79356b
A
108 PE_call_timebase_callback();
109}
110
111// pe_do_clock_test uses the number from pe_run_clock_test to
112// find a best fit guess for the bus speed.
113void pe_do_clock_test(unsigned int via_addr,
114 int num_speeds, unsigned long *speed_list)
115{
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;
119
120 // Save the via addr so the asm part can use it.
121 clock_test_data.via_addr = via_addr;
122
123 // Keep looping until it matches the last try.
124 bus_freq_num = 0;
125 do {
126 last_bus_freq = bus_freq_num;
127
128 // The the asm part to do the real work.
129 pe_run_clock_test((void *)&clock_test_data);
130
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);
134
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;
139
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;
142
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;
150
151 if (diff < kMaxFreqDiff * bus_freq_den) break;
152 }
153 if (cnt != num_speeds) continue;
154
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;
157
158 diff = tmp_bus_freq - raw_bus_freq;
159 if (diff < 0) diff = -diff;
160
161 if (diff < kMaxFreqDiff) {
162 bus_freq_num = tmp_bus_freq;
163 bus_freq_den = 1;
164 continue;
165 }
166
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;
169
170 diff = tmp_bus_freq - raw_bus_freq * 3;
171 if (diff < 0) diff = -diff;
172
173 if (diff < kMaxFreqDiff * 3) {
174 bus_freq_num = tmp_bus_freq;
175 bus_freq_den = 3;
176 continue;
177 }
178
179 // Since all else failed return the raw bus speed
180 bus_freq_num = raw_bus_freq;
181 bus_freq_den = 1;
182 } while ((bus_freq_num != last_bus_freq) && tries--);
183}