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