ppc32: Update PowerMac cpufreq driver

Add support for Apple latest laptops, add a warning when building
an SMP kernel (the core cpufreq doesn't scale loops_per_jiffies
properly on SMP kernels), cleanup some code.
parent c2f6ad3e
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/cpufreq.h> #include <linux/cpufreq.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/sysdev.h> #include <linux/sysdev.h>
#include <linux/i2c.h>
#include <asm/prom.h> #include <asm/prom.h>
#include <asm/machdep.h> #include <asm/machdep.h>
#include <asm/irq.h> #include <asm/irq.h>
...@@ -38,6 +39,14 @@ ...@@ -38,6 +39,14 @@
*/ */
#undef DEBUG_FREQ #undef DEBUG_FREQ
/*
* There is a problem with the core cpufreq code on SMP kernels,
* it won't recalculate the Bogomips properly
*/
#ifdef CONFIG_SMP
#warning "WARNING, CPUFREQ not recommended on SMP kernels"
#endif
extern void low_choose_750fx_pll(int pll); extern void low_choose_750fx_pll(int pll);
extern void low_sleep_handler(void); extern void low_sleep_handler(void);
extern void openpic_suspend(struct sys_device *sysdev, u32 state); extern void openpic_suspend(struct sys_device *sysdev, u32 state);
...@@ -48,7 +57,14 @@ extern void enable_kernel_fp(void); ...@@ -48,7 +57,14 @@ extern void enable_kernel_fp(void);
static unsigned int low_freq; static unsigned int low_freq;
static unsigned int hi_freq; static unsigned int hi_freq;
static unsigned int cur_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_pmu;
static int cpufreq_uses_gpios;
static u32 voltage_gpio;
static u32 frequency_gpio;
static u32 slew_done_gpio;
#define PMAC_CPU_LOW_SPEED 1 #define PMAC_CPU_LOW_SPEED 1
#define PMAC_CPU_HIGH_SPEED 0 #define PMAC_CPU_HIGH_SPEED 0
...@@ -65,8 +81,7 @@ static struct cpufreq_frequency_table pmac_cpu_freqs[] = { ...@@ -65,8 +81,7 @@ static struct cpufreq_frequency_table pmac_cpu_freqs[] = {
{0, CPUFREQ_TABLE_END}, {0, CPUFREQ_TABLE_END},
}; };
static inline void static inline void wakeup_decrementer(void)
wakeup_decrementer(void)
{ {
set_dec(tb_ticks_per_jiffy); set_dec(tb_ticks_per_jiffy);
/* No currently-supported powerbook has a 601, /* No currently-supported powerbook has a 601,
...@@ -76,8 +91,7 @@ wakeup_decrementer(void) ...@@ -76,8 +91,7 @@ wakeup_decrementer(void)
} }
#ifdef DEBUG_FREQ #ifdef DEBUG_FREQ
static inline void static inline void debug_calc_bogomips(void)
debug_calc_bogomips(void)
{ {
/* This will cause a recalc of bogomips and display the /* This will cause a recalc of bogomips and display the
* result. We backup/restore the value to avoid affecting the * result. We backup/restore the value to avoid affecting the
...@@ -89,17 +103,18 @@ debug_calc_bogomips(void) ...@@ -89,17 +103,18 @@ debug_calc_bogomips(void)
calibrate_delay(); calibrate_delay();
loops_per_jiffy = save_lpj; loops_per_jiffy = save_lpj;
} }
#endif #endif /* DEBUG_FREQ */
/* Switch CPU speed under 750FX CPU control /* Switch CPU speed under 750FX CPU control
*/ */
static int __pmac static int __pmac cpu_750fx_cpu_speed(int low_speed)
cpu_750fx_cpu_speed(int low_speed)
{ {
#ifdef DEBUG_FREQ #ifdef DEBUG_FREQ
printk(KERN_DEBUG "HID1, before: %x\n", mfspr(SPRN_HID1)); printk(KERN_DEBUG "HID1, before: %x\n", mfspr(SPRN_HID1));
#endif #endif
#ifdef CONFIG_6xx
low_choose_750fx_pll(low_speed); low_choose_750fx_pll(low_speed);
#endif
#ifdef DEBUG_FREQ #ifdef DEBUG_FREQ
printk(KERN_DEBUG "HID1, after: %x\n", mfspr(SPRN_HID1)); printk(KERN_DEBUG "HID1, after: %x\n", mfspr(SPRN_HID1));
debug_calc_bogomips(); debug_calc_bogomips();
...@@ -108,15 +123,54 @@ cpu_750fx_cpu_speed(int low_speed) ...@@ -108,15 +123,54 @@ cpu_750fx_cpu_speed(int low_speed)
return 0; return 0;
} }
/* Switch CPU speed using slewing GPIOs
*/
static int __pmac gpios_set_cpu_speed(unsigned int low_speed)
{
int gpio;
/* 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 */
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ/100);
}
/* Set frequency */
pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, frequency_gpio, low_speed ? 0x04 : 0x05);
udelay(200);
do {
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
gpio = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, slew_done_gpio, 0);
} while((gpio & 0x02) == 0);
/* 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 */
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ/100);
}
#ifdef DEBUG_FREQ
debug_calc_bogomips();
#endif
return 0;
}
/* Switch CPU speed under PMU control /* Switch CPU speed under PMU control
*/ */
static int __pmac static int __pmac pmu_set_cpu_speed(unsigned int low_speed)
pmu_set_cpu_speed(unsigned int low_speed)
{ {
struct adb_request req; struct adb_request req;
unsigned long save_l2cr; unsigned long save_l2cr;
unsigned long save_l3cr; unsigned long save_l3cr;
preempt_disable();
#ifdef DEBUG_FREQ #ifdef DEBUG_FREQ
printk(KERN_DEBUG "HID1, before: %x\n", mfspr(SPRN_HID1)); printk(KERN_DEBUG "HID1, before: %x\n", mfspr(SPRN_HID1));
#endif #endif
...@@ -197,11 +251,12 @@ pmu_set_cpu_speed(unsigned int low_speed) ...@@ -197,11 +251,12 @@ pmu_set_cpu_speed(unsigned int low_speed)
debug_calc_bogomips(); debug_calc_bogomips();
#endif #endif
preempt_enable();
return 0; return 0;
} }
static int __pmac static int __pmac do_set_cpu_speed(int speed_mode)
do_set_cpu_speed(int speed_mode)
{ {
struct cpufreq_freqs freqs; struct cpufreq_freqs freqs;
int rc; int rc;
...@@ -216,6 +271,8 @@ do_set_cpu_speed(int speed_mode) ...@@ -216,6 +271,8 @@ do_set_cpu_speed(int speed_mode)
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
if (cpufreq_uses_pmu) if (cpufreq_uses_pmu)
rc = pmu_set_cpu_speed(speed_mode); rc = pmu_set_cpu_speed(speed_mode);
else if (cpufreq_uses_gpios)
rc = gpios_set_cpu_speed(speed_mode);
else else
rc = cpu_750fx_cpu_speed(speed_mode); rc = cpu_750fx_cpu_speed(speed_mode);
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
...@@ -224,16 +281,14 @@ do_set_cpu_speed(int speed_mode) ...@@ -224,16 +281,14 @@ do_set_cpu_speed(int speed_mode)
return rc; return rc;
} }
static int __pmac static int __pmac pmac_cpufreq_verify(struct cpufreq_policy *policy)
pmac_cpufreq_verify(struct cpufreq_policy *policy)
{ {
return cpufreq_frequency_table_verify(policy, pmac_cpu_freqs); return cpufreq_frequency_table_verify(policy, pmac_cpu_freqs);
} }
static int __pmac static int __pmac pmac_cpufreq_target( struct cpufreq_policy *policy,
pmac_cpufreq_target( struct cpufreq_policy *policy, unsigned int target_freq,
unsigned int target_freq, unsigned int relation)
unsigned int relation)
{ {
unsigned int newstate = 0; unsigned int newstate = 0;
...@@ -244,15 +299,13 @@ pmac_cpufreq_target( struct cpufreq_policy *policy, ...@@ -244,15 +299,13 @@ pmac_cpufreq_target( struct cpufreq_policy *policy,
return do_set_cpu_speed(newstate); return do_set_cpu_speed(newstate);
} }
unsigned int __pmac unsigned int __pmac pmac_get_one_cpufreq(int i)
pmac_get_one_cpufreq(int i)
{ {
/* Supports only one CPU for now */ /* Supports only one CPU for now */
return (i == 0) ? cur_freq : 0; return (i == 0) ? cur_freq : 0;
} }
static int __pmac static int __pmac pmac_cpufreq_cpu_init(struct cpufreq_policy *policy)
pmac_cpufreq_cpu_init(struct cpufreq_policy *policy)
{ {
if (policy->cpu != 0) if (policy->cpu != 0)
return -ENODEV; return -ENODEV;
...@@ -264,6 +317,18 @@ pmac_cpufreq_cpu_init(struct cpufreq_policy *policy) ...@@ -264,6 +317,18 @@ pmac_cpufreq_cpu_init(struct cpufreq_policy *policy)
return cpufreq_frequency_table_cpuinfo(policy, &pmac_cpu_freqs[0]); return cpufreq_frequency_table_cpuinfo(policy, &pmac_cpu_freqs[0]);
} }
static u32 __pmac read_gpio(struct device_node *np)
{
u32 *reg = (u32 *)get_property(np, "reg", NULL);
if (reg == NULL)
return 0;
/* That works for all keylargos but shall be fixed properly
* some day...
*/
return 0x50 + (*reg);
}
static struct cpufreq_driver pmac_cpufreq_driver = { static struct cpufreq_driver pmac_cpufreq_driver = {
.verify = pmac_cpufreq_verify, .verify = pmac_cpufreq_verify,
.target = pmac_cpufreq_target, .target = pmac_cpufreq_target,
...@@ -272,15 +337,17 @@ static struct cpufreq_driver pmac_cpufreq_driver = { ...@@ -272,15 +337,17 @@ static struct cpufreq_driver pmac_cpufreq_driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
/* Currently, we support the following machines: /* Currently, we support the following machines:
* *
* - Titanium PowerBook 1Ghz (PMU based, 667Mhz & 1Ghz)
* - Titanium PowerBook 800 (PMU based, 667Mhz & 800Mhz) * - Titanium PowerBook 800 (PMU based, 667Mhz & 800Mhz)
* - Titanium PowerBook 500 (PMU based, 300Mhz & 500Mhz) * - Titanium PowerBook 500 (PMU based, 300Mhz & 500Mhz)
* - iBook2 500 (PMU based, 400Mhz & 500Mhz) * - iBook2 500 (PMU based, 400Mhz & 500Mhz)
* - iBook2 700 (CPU based, 400Mhz & 700Mhz, support low voltage) * - iBook2 700 (CPU based, 400Mhz & 700Mhz, support low voltage)
* - Recent MacRISC3 machines
*/ */
static int __init static int __init pmac_cpufreq_setup(void)
pmac_cpufreq_setup(void)
{ {
struct device_node *cpunode; struct device_node *cpunode;
u32 *value; u32 *value;
...@@ -304,6 +371,74 @@ pmac_cpufreq_setup(void) ...@@ -304,6 +371,74 @@ pmac_cpufreq_setup(void)
if (machine_is_compatible("PowerBook3,4") || if (machine_is_compatible("PowerBook3,4") ||
machine_is_compatible("PowerBook3,5") || machine_is_compatible("PowerBook3,5") ||
machine_is_compatible("MacRISC3")) { 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); value = (u32 *)get_property(cpunode, "min-clock-frequency", NULL);
if (!value) if (!value)
goto out; goto out;
...@@ -359,6 +494,11 @@ pmac_cpufreq_setup(void) ...@@ -359,6 +494,11 @@ pmac_cpufreq_setup(void)
pmac_cpu_freqs[CPUFREQ_LOW].frequency = low_freq; pmac_cpu_freqs[CPUFREQ_LOW].frequency = low_freq;
pmac_cpu_freqs[CPUFREQ_HIGH].frequency = hi_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"));
return cpufreq_register_driver(&pmac_cpufreq_driver); return cpufreq_register_driver(&pmac_cpufreq_driver);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment