#include <pexpert/pexpert.h>
#include <ppc/machine_routines.h>
extern void pe_run_clock_test(void *tmp);
void pe_do_clock_test(unsigned int via_addr,
int num_speeds, unsigned long *speed_list);
#define kMaxFreqDiff (30000)
struct clock_test_data {
unsigned int via_addr;
unsigned int via_ticks;
unsigned int dec_ticks;
};
static long bus_freq_num, bus_freq_den, cpu_pll;
void PE_Determine_Clock_Speeds(unsigned int via_addr, int num_speeds,
unsigned long *speed_list)
{
boolean_t oldLevel;
unsigned long tmp_bus_speed, tmp_cpu_speed;
unsigned long long tmp;
oldLevel = ml_set_interrupts_enabled(FALSE);
pe_do_clock_test(via_addr, num_speeds, speed_list);
ml_set_interrupts_enabled(oldLevel);
tmp_bus_speed = bus_freq_num / bus_freq_den;
tmp = ((unsigned long long)bus_freq_num * cpu_pll) / (bus_freq_den * 2);
tmp_cpu_speed = (unsigned long)tmp;
gPEClockFrequencyInfo.bus_clock_rate_num = bus_freq_num;
gPEClockFrequencyInfo.bus_clock_rate_den = bus_freq_den;
gPEClockFrequencyInfo.bus_to_cpu_rate_num = cpu_pll;
gPEClockFrequencyInfo.bus_to_cpu_rate_den = 2;
gPEClockFrequencyInfo.bus_to_dec_rate_num = 1;
gPEClockFrequencyInfo.bus_to_dec_rate_den = 4;
gPEClockFrequencyInfo.bus_clock_rate_hz = tmp_bus_speed;
gPEClockFrequencyInfo.cpu_clock_rate_hz = tmp_cpu_speed;
gPEClockFrequencyInfo.dec_clock_rate_hz = tmp_bus_speed / 4;
PE_call_timebase_callback();
}
void pe_do_clock_test(unsigned int via_addr,
int num_speeds, unsigned long *speed_list)
{
struct clock_test_data clock_test_data;
long cnt, diff, raw_cpu_freq, raw_bus_freq, tmp_bus_freq,
last_bus_freq, tries = 10;
clock_test_data.via_addr = via_addr;
bus_freq_num = 0;
do {
last_bus_freq = bus_freq_num;
pe_run_clock_test((void *)&clock_test_data);
cpu_pll = 10000000 / clock_test_data.dec_ticks;
cpu_pll = (cpu_pll / 2) + (cpu_pll & 1);
raw_bus_freq = ((0xBF401675E5DULL * clock_test_data.dec_ticks) /
clock_test_data.via_ticks) >> 22;
raw_cpu_freq = raw_bus_freq * cpu_pll / 2;
for (cnt = 0; cnt < num_speeds; cnt++) {
bus_freq_num = speed_list[cnt * 2];
bus_freq_den = speed_list[cnt * 2 + 1];
diff = bus_freq_num - raw_bus_freq * bus_freq_den;
if (diff < 0) diff = -diff;
if (diff < kMaxFreqDiff * bus_freq_den) break;
}
if (cnt != num_speeds) continue;
tmp_bus_freq = ((raw_bus_freq + 250000) / 500000) * 500000;
diff = tmp_bus_freq - raw_bus_freq;
if (diff < 0) diff = -diff;
if (diff < kMaxFreqDiff) {
bus_freq_num = tmp_bus_freq;
bus_freq_den = 1;
continue;
}
tmp_bus_freq = ((raw_bus_freq * 3 + 25000000) / 50000000) * 50000000;
diff = tmp_bus_freq - raw_bus_freq * 3;
if (diff < 0) diff = -diff;
if (diff < kMaxFreqDiff * 3) {
bus_freq_num = tmp_bus_freq;
bus_freq_den = 3;
continue;
}
bus_freq_num = raw_bus_freq;
bus_freq_den = 1;
} while ((bus_freq_num != last_bus_freq) && tries--);
}