/*
* arch/ppc/platforms/pmac_cpufreq.c
*
- * Copyright (C) 2002 - 2003 Benjamin Herrenschmidt <benh@kernel.crashing.org>
+ * Copyright (C) 2002 - 2004 Benjamin Herrenschmidt <benh@kernel.crashing.org>
+ * Copyright (C) 2004 John Steele Scott <toojays@toojays.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#warning "WARNING, CPUFREQ not recommended on SMP kernels"
#endif
+extern void low_choose_7447a_dfs(int dfs);
extern void low_choose_750fx_pll(int pll);
extern void low_sleep_handler(void);
extern void openpic_suspend(struct sys_device *sysdev, u32 state);
extern void enable_kernel_altivec(void);
extern void enable_kernel_fp(void);
+/*
+ * Currently, PowerMac cpufreq supports only high & low frequencies
+ * that are set by the firmware
+ */
static unsigned int low_freq;
static unsigned int hi_freq;
static unsigned int cur_freq;
-/* Clean that up some day ... use a func ptr or at least an enum... */
-static int cpufreq_uses_pmu;
-static int cpufreq_uses_gpios;
+/*
+ * Different models uses different mecanisms to switch the frequency
+ */
+static int (*set_speed_proc)(int low_speed);
+/*
+ * Some definitions used by the various speedprocs
+ */
static u32 voltage_gpio;
static u32 frequency_gpio;
static u32 slew_done_gpio;
+
#define PMAC_CPU_LOW_SPEED 1
#define PMAC_CPU_HIGH_SPEED 0
return 0;
}
+/* Switch CPU speed using DFS */
+static int __pmac 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);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ/1000);
+ } else {
+ /* ramping down, enable aack delay first */
+ pmac_call_feature(PMAC_FTR_AACK_DELAY_ENABLE, NULL, 1, 0);
+ }
+
+ /* set frequency */
+ low_choose_7447a_dfs(low_speed);
+
+ if (low_speed == 1) {
+ /* ramping down, set voltage last */
+ pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ/1000);
+ } else {
+ /* ramping up, disable aack delay last */
+ pmac_call_feature(PMAC_FTR_AACK_DELAY_ENABLE, NULL, 0, 0);
+ }
+
+ return 0;
+}
+
+
/* Switch CPU speed using slewing GPIOs
*/
-static int __pmac gpios_set_cpu_speed(unsigned int low_speed)
+static int __pmac gpios_set_cpu_speed(int low_speed)
{
int gpio;
}
/* Set frequency */
- pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, frequency_gpio, low_speed ? 0x04 : 0x05);
+ pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, frequency_gpio,
+ low_speed ? 0x04 : 0x05);
udelay(200);
do {
set_current_state(TASK_UNINTERRUPTIBLE);
/* Switch CPU speed under PMU control
*/
-static int __pmac pmu_set_cpu_speed(unsigned int low_speed)
+static int __pmac pmu_set_cpu_speed(int low_speed)
{
struct adb_request req;
unsigned long save_l2cr;
return 0;
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
- if (cpufreq_uses_pmu)
- rc = pmu_set_cpu_speed(speed_mode);
- else if (cpufreq_uses_gpios)
- rc = gpios_set_cpu_speed(speed_mode);
- else
- rc = cpu_750fx_cpu_speed(speed_mode);
+ set_speed_proc(speed_mode == PMAC_CPU_LOW_SPEED);
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
cur_freq = (speed_mode == PMAC_CPU_HIGH_SPEED) ? hi_freq : low_freq;
};
+static int __pmac pmac_cpufreq_init_MacRISC3(struct device_node *cpunode)
+{
+ struct device_node *volt_gpio_np = of_find_node_by_name(NULL,
+ "voltage-gpio");
+ struct device_node *freq_gpio_np = of_find_node_by_name(NULL,
+ "frequency-gpio");
+ struct device_node *slew_done_gpio_np = of_find_node_by_name(NULL,
+ "slewing-done");
+ u32 *value;
+
+ /*
+ * 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;
+ u32 *freqs, *ratio;
+
+ freqs = (u32 *)get_property(cpunode, "bus-frequencies", &lenp);
+ lenp /= sizeof(u32);
+ if (freqs == NULL || lenp != 2) {
+ printk(KERN_ERR "cpufreq: bus-frequencies incorrect or missing\n");
+ return 1;
+ }
+ ratio = (u32 *)get_property(cpunode, "processor-to-bus-ratio*2", NULL);
+ if (ratio == NULL) {
+ printk(KERN_ERR "cpufreq: 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 = (u32 *)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 = (u32 *)get_property(cpunode, "max-clock-frequency", NULL);
+ if (!value)
+ return 1;
+ hi_freq = (*value) / 1000;
+ set_speed_proc = pmu_set_cpu_speed;
+
+ return 0;
+}
+
+static int __pmac pmac_cpufreq_init_7447A(struct device_node *cpunode)
+{
+ struct device_node *volt_gpio_np;
+
+ /* OF only reports the high frequency */
+ hi_freq = cur_freq;
+ low_freq = cur_freq/2;
+ if (mfspr(HID1) & HID1_DFS)
+ cur_freq = low_freq;
+ else
+ cur_freq = hi_freq;
+
+ volt_gpio_np = of_find_node_by_name(NULL, "cpu-vcore-select");
+ if (!volt_gpio_np){
+ printk(KERN_ERR "cpufreq: missing cpu-vcore-select gpio\n");
+ return 1;
+ }
+
+ u32 *reg = (u32 *)get_property(volt_gpio_np, "reg", NULL);
+ voltage_gpio = *reg;
+ set_speed_proc = dfs_set_cpu_speed;
+
+ return 0;
+}
+
/* Currently, we support the following machines:
*
* - Titanium PowerBook 1Ghz (PMU based, 667Mhz & 1Ghz)
* - Titanium PowerBook 800 (PMU based, 667Mhz & 800Mhz)
+ * - Titanium PowerBook 400 (PMU based, 300Mhz & 400Mhz)
* - Titanium PowerBook 500 (PMU based, 300Mhz & 500Mhz)
* - iBook2 500 (PMU based, 400Mhz & 500Mhz)
* - iBook2 700 (CPU based, 400Mhz & 700Mhz, support low voltage)
- * - Recent MacRISC3 machines
+ * - Recent MacRISC3 laptops
+ * - iBook G4s and PowerBook G4s with 7447A CPUs
*/
static int __init pmac_cpufreq_setup(void)
{
struct device_node *cpunode;
u32 *value;
- int has_freq_ctl = 0;
if (strstr(cmd_line, "nocpufreq"))
return 0;
goto out;
cur_freq = (*value) / 1000;
- /* Check for newer machines */
- if (machine_is_compatible("PowerBook3,4") ||
- machine_is_compatible("PowerBook3,5") ||
- machine_is_compatible("MacRISC3")) {
- struct device_node *volt_gpio_np = of_find_node_by_name(NULL, "voltage-gpio");
- struct device_node *freq_gpio_np = of_find_node_by_name(NULL, "frequency-gpio");
- struct device_node *slew_done_gpio_np = of_find_node_by_name(NULL, "slewing-done");
-
- /*
- * 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;
- u32 *freqs, *ratio;
-
- freqs = (u32 *)get_property(cpunode, "bus-frequencies", &lenp);
- lenp /= sizeof(u32);
- if (freqs == NULL || lenp != 2) {
- printk(KERN_ERR "cpufreq: bus-frequencies incorrect or missing\n");
- goto out;
- }
- ratio = (u32 *)get_property(cpunode, "processor-to-bus-ratio*2", NULL);
- if (ratio == NULL) {
- printk(KERN_ERR "cpufreq: processor-to-bus-ratio*2 missing\n");
- goto out;
- }
-
- /* 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;
-
- has_freq_ctl = 1;
- cpufreq_uses_gpios = 1;
- goto out;
- }
-
- /* If we use the PMU, look for the min & max frequencies in the
- * device-tree
- */
- value = (u32 *)get_property(cpunode, "min-clock-frequency", NULL);
- if (!value)
- goto out;
- 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 = (u32 *)get_property(cpunode, "max-clock-frequency", NULL);
- if (!value)
- goto out;
- hi_freq = (*value) / 1000;
- has_freq_ctl = 1;
- cpufreq_uses_pmu = 1;
- }
+ /* Check for 7447A based iBook G4 or PowerBook */
+ if (machine_is_compatible("PowerBook6,5") ||
+ machine_is_compatible("PowerBook6,4") ||
+ machine_is_compatible("PowerBook5,5") ||
+ machine_is_compatible("PowerBook5,4")) {
+ pmac_cpufreq_init_7447A(cpunode);
+ /* Check for other MacRISC3 machines */
+ } else if (machine_is_compatible("PowerBook3,4") ||
+ machine_is_compatible("PowerBook3,5") ||
+ machine_is_compatible("MacRISC3")) {
+ pmac_cpufreq_init_MacRISC3(cpunode);
/* Else check for iBook2 500 */
- else if (machine_is_compatible("PowerBook4,1")) {
+ } else if (machine_is_compatible("PowerBook4,1")) {
/* We only know about 500Mhz model */
if (cur_freq < 450000 || cur_freq > 550000)
goto out;
hi_freq = cur_freq;
low_freq = 400000;
- has_freq_ctl = 1;
- cpufreq_uses_pmu = 1;
+ set_speed_proc = pmu_set_cpu_speed;
}
- /* Else check for TiPb 500 */
+ /* Else check for TiPb 400 & 500 */
else if (machine_is_compatible("PowerBook3,2")) {
- /* We only know about 500Mhz model */
- if (cur_freq < 450000 || cur_freq > 550000)
+ /* We only know about the 400 MHz and the 500Mhz model
+ * they both have 300 MHz as low frequency
+ */
+ if (cur_freq < 350000 || cur_freq > 550000)
goto out;
hi_freq = cur_freq;
low_freq = 300000;
- has_freq_ctl = 1;
- cpufreq_uses_pmu = 1;
+ set_speed_proc = pmu_set_cpu_speed;
}
/* Else check for 750FX */
else if (PVR_VER(mfspr(PVR)) == 0x7000) {
value = (u32 *)get_property(cpunode, "reduced-clock-frequency", NULL);
if (!value)
goto out;
- low_freq = (*value) / 1000;
- cpufreq_uses_pmu = 0;
- has_freq_ctl = 1;
+ low_freq = (*value) / 1000;
+ set_speed_proc = cpu_750fx_cpu_speed;
}
out:
- if (!has_freq_ctl)
+ if (set_speed_proc == NULL)
return -ENODEV;
pmac_cpu_freqs[CPUFREQ_LOW].frequency = low_freq;
pmac_cpu_freqs[CPUFREQ_HIGH].frequency = hi_freq;
printk(KERN_INFO "Registering PowerMac CPU frequency driver\n");
- printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Boot: %d Mhz, switch method: %s\n",
- low_freq/1000, hi_freq/1000, cur_freq/1000,
- cpufreq_uses_pmu ? "PMU" : (cpufreq_uses_gpios ? "GPIOs" : "CPU"));
+ printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Boot: %d Mhz\n",
+ low_freq/1000, hi_freq/1000, cur_freq/1000);
return cpufreq_register_driver(&pmac_cpufreq_driver);
}