// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2002 - 2005 Benjamin Herrenschmidt <benh@kernel.crashing.org> * Copyright (C) 2004 John Steele Scott <toojays@toojays.net> * * TODO: Need a big cleanup here. Basically, we need to have different * cpufreq_driver structures for the different type of HW instead of the * current mess. We also need to better deal with the detection of the * type of machine.
*/
/* WARNING !!! This will cause calibrate_delay() to be called, * but this is an __init function ! So you MUST go edit * init/main.c to make it non-init before enabling DEBUG_FREQ
*/ #undef DEBUG_FREQ
/* * Currently, PowerMac cpufreq supports only high & low frequencies * that are set by the firmware
*/ staticunsignedint low_freq; staticunsignedint hi_freq; staticunsignedint cur_freq; staticunsignedint sleep_freq; staticunsignedlong transition_latency;
/* * Different models uses different mechanisms to switch the frequency
*/ staticint (*set_speed_proc)(int low_speed); staticunsignedint (*get_speed_proc)(void);
/* * Some definitions used by the various speedprocs
*/ static u32 voltage_gpio; static u32 frequency_gpio; static u32 slew_done_gpio; staticint no_schedule; staticint has_cpu_l2lve; staticint is_pmu_based;
/* There are only two frequency states for each processor. Values * are in kHz for the time being.
*/ #define CPUFREQ_HIGH 0 #define CPUFREQ_LOW 1
staticinlinevoid local_delay(unsignedlong ms)
{ if (no_schedule)
mdelay(ms); else
msleep(ms);
}
#ifdef DEBUG_FREQ staticinlinevoid debug_calc_bogomips(void)
{ /* This will cause a recalc of bogomips and display the * result. We backup/restore the value to avoid affecting the * core cpufreq framework's own calculation.
*/ unsignedlong save_lpj = loops_per_jiffy;
calibrate_delay();
loops_per_jiffy = save_lpj;
} #endif/* DEBUG_FREQ */
/* Switch CPU speed under 750FX CPU control
*/ staticint cpu_750fx_cpu_speed(int low_speed)
{
u32 hid2;
if (low_speed == 0) { /* ramping up, set voltage first */
pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05); /* Make sure we sleep for at least 1ms */
local_delay(10);
/* tweak L2 for high voltage */ if (has_cpu_l2lve) {
hid2 = mfspr(SPRN_HID2_750FX);
hid2 &= ~0x2000;
mtspr(SPRN_HID2_750FX, hid2);
}
} #ifdef CONFIG_PPC_BOOK3S_32
low_choose_750fx_pll(low_speed); #endif if (low_speed == 1) { /* tweak L2 for low voltage */ if (has_cpu_l2lve) {
hid2 = mfspr(SPRN_HID2_750FX);
hid2 |= 0x2000;
mtspr(SPRN_HID2_750FX, hid2);
}
/* ramping down, set voltage last */
pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04);
local_delay(10);
}
/* Switch CPU speed using DFS */ staticint dfs_set_cpu_speed(int low_speed)
{ if (low_speed == 0) { /* ramping up, set voltage first */
pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05); /* Make sure we sleep for at least 1ms */
local_delay(1);
}
/* set frequency */ #ifdef CONFIG_PPC_BOOK3S_32
low_choose_7447a_dfs(low_speed); #endif
udelay(100);
if (low_speed == 1) { /* ramping down, set voltage last */
pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04);
local_delay(1);
}
/* Switch CPU speed using slewing GPIOs
*/ staticint gpios_set_cpu_speed(int low_speed)
{ int gpio, timeout = 0;
/* If ramping up, set voltage first */ if (low_speed == 0) {
pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05); /* Delay is way too big but it's ok, we schedule */
local_delay(10);
}
/* Set frequency */
gpio = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, frequency_gpio, 0); if (low_speed == ((gpio & 0x01) == 0)) goto skip;
pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, frequency_gpio,
low_speed ? 0x04 : 0x05);
udelay(200); do { if (++timeout > 100) break;
local_delay(1);
gpio = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, slew_done_gpio, 0);
} while((gpio & 0x02) == 0);
skip: /* If ramping down, set voltage last */ if (low_speed == 1) {
pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04); /* Delay is way too big but it's ok, we schedule */
local_delay(10);
}
#ifdef DEBUG_FREQ
debug_calc_bogomips(); #endif
return 0;
}
/* Switch CPU speed under PMU control
*/ staticint pmu_set_cpu_speed(int low_speed)
{ struct adb_request req; unsignedlong save_l2cr; unsignedlong save_l3cr; unsignedint pic_prio; unsignedlong flags;
/* Disable all interrupt sources on openpic */
pic_prio = mpic_cpu_get_priority();
mpic_cpu_set_priority(0xf);
/* Make sure the decrementer won't interrupt us */ asmvolatile("mtdec %0" : : "r" (0x7fffffff)); /* Make sure any pending DEC interrupt occurring while we did
* the above didn't re-enable the DEC */
mb(); asmvolatile("mtdec %0" : : "r" (0x7fffffff));
/* We can now disable MSR_EE */
local_irq_save(flags);
/* Giveup the FPU & vec */
enable_kernel_fp();
#ifdef CONFIG_ALTIVEC if (cpu_has_feature(CPU_FTR_ALTIVEC))
enable_kernel_altivec(); #endif/* CONFIG_ALTIVEC */
/* Save & disable L2 and L3 caches */
save_l3cr = _get_L3CR(); /* (returns -1 if not available) */
save_l2cr = _get_L2CR(); /* (returns -1 if not available) */
/* Send the new speed command. My assumption is that this command * will cause PLL_CFG[0..3] to be changed next time CPU goes to sleep
*/
pmu_request(&req, NULL, 6, PMU_CPU_SPEED, 'W', 'O', 'O', 'F', low_speed); while (!req.complete)
pmu_poll();
/* Prepare the northbridge for the speed transition */
pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,1,1);
/* Call low level code to backup CPU state and recover from * hardware reset
*/
low_sleep_handler();
/* Restore the northbridge */
pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,1,0);
/* * Restore decrementer; we'll take a decrementer interrupt * as soon as interrupts are re-enabled and the generic * clockevents code will reprogram it with the right value.
*/
set_dec(1);
if (of_property_read_reg(np, 0, &offset, NULL) < 0) return 0; /* That works for all keylargos but shall be fixed properly * some day... The problem is that it seems we can't rely * on the "reg" property of the GPIO nodes, they are either * relative to the base of KeyLargo or to the base of the * GPIO space, and the device-tree doesn't help.
*/ if (offset < KEYLARGO_GPIO_LEVELS0)
offset += KEYLARGO_GPIO_LEVELS0; return offset;
}
staticint pmac_cpufreq_suspend(struct cpufreq_policy *policy)
{ /* Ok, this could be made a bit smarter, but let's be robust for now. We * always force a speed change to high speed before sleep, to make sure * we have appropriate voltage and/or bus speed for the wakeup process, * and to make sure our loops_per_jiffies are "good enough", that is will * not cause too short delays if we sleep in low speed and wake in high * speed..
*/
no_schedule = 1;
sleep_freq = cur_freq; if (cur_freq == low_freq && !is_pmu_based)
do_set_cpu_speed(policy, CPUFREQ_HIGH); return 0;
}
staticint pmac_cpufreq_resume(struct cpufreq_policy *policy)
{ /* If we resume, first check if we have a get() function */ if (get_speed_proc)
cur_freq = get_speed_proc(); else
cur_freq = 0;
/* We don't, hrm... we don't really know our speed here, best * is that we force a switch to whatever it was, which is * probably high speed due to our suspend() routine
*/
do_set_cpu_speed(policy, sleep_freq == low_freq ?
CPUFREQ_LOW : CPUFREQ_HIGH);
/* * Check to see if it's GPIO driven or PMU only * * The way we extract the GPIO address is slightly hackish, but it * works well enough for now. We need to abstract the whole GPIO * stuff sooner or later anyway
*/
if (volt_gpio_np)
voltage_gpio = read_gpio(volt_gpio_np); if (freq_gpio_np)
frequency_gpio = read_gpio(freq_gpio_np); if (slew_done_gpio_np)
slew_done_gpio = read_gpio(slew_done_gpio_np);
/* If we use the frequency GPIOs, calculate the min/max speeds based * on the bus frequencies
*/ if (frequency_gpio && slew_done_gpio) { int lenp, rc; const u32 *freqs, *ratio;
freqs = of_get_property(cpunode, "bus-frequencies", &lenp);
lenp /= sizeof(u32); if (freqs == NULL || lenp != 2) {
pr_err("bus-frequencies incorrect or missing\n"); return 1;
}
ratio = of_get_property(cpunode, "processor-to-bus-ratio*2",
NULL); if (ratio == NULL) {
pr_err("processor-to-bus-ratio*2 missing\n"); return 1;
}
/* Get the min/max bus frequencies */
low_freq = min(freqs[0], freqs[1]);
hi_freq = max(freqs[0], freqs[1]);
/* Grrrr.. It _seems_ that the device-tree is lying on the low bus * frequency, it claims it to be around 84Mhz on some models while * it appears to be approx. 101Mhz on all. Let's hack around here... * fortunately, we don't need to be too precise
*/ if (low_freq < 98000000)
low_freq = 101000000;
/* Convert those to CPU core clocks */
low_freq = (low_freq * (*ratio)) / 2000;
hi_freq = (hi_freq * (*ratio)) / 2000;
/* Now we get the frequencies, we read the GPIO to see what is out current * speed
*/
rc = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, frequency_gpio, 0);
cur_freq = (rc & 0x01) ? hi_freq : low_freq;
set_speed_proc = gpios_set_cpu_speed; return 1;
}
/* If we use the PMU, look for the min & max frequencies in the * device-tree
*/
value = of_get_property(cpunode, "min-clock-frequency", NULL); if (!value) return 1;
low_freq = (*value) / 1000; /* The PowerBook G4 12" (PowerBook6,1) has an error in the device-tree
* here */ if (low_freq < 100000)
low_freq *= 10;
value = of_get_property(cpunode, "max-clock-frequency", NULL); if (!value) return 1;
hi_freq = (*value) / 1000;
set_speed_proc = pmu_set_cpu_speed;
is_pmu_based = 1;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.