Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
8a1da912
Commit
8a1da912
authored
Mar 28, 2003
by
Benjamin Herrenschmidt
Committed by
Paul Mackerras
Mar 28, 2003
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
PPC32: Add support for CPU frequency scaling on some PowerMacs
parent
76b6688a
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
413 additions
and
1 deletion
+413
-1
arch/ppc/Kconfig
arch/ppc/Kconfig
+46
-0
arch/ppc/platforms/Makefile
arch/ppc/platforms/Makefile
+3
-0
arch/ppc/platforms/pmac_cpufreq.c
arch/ppc/platforms/pmac_cpufreq.c
+349
-0
arch/ppc/platforms/pmac_setup.c
arch/ppc/platforms/pmac_setup.c
+15
-1
No files found.
arch/ppc/Kconfig
View file @
8a1da912
...
@@ -666,6 +666,52 @@ config MATH_EMULATION
...
@@ -666,6 +666,52 @@ config MATH_EMULATION
here. Saying Y here will not hurt performance (on any machine) but
here. Saying Y here will not hurt performance (on any machine) but
will increase the size of the kernel.
will increase the size of the kernel.
config CPU_FREQ
bool "CPU Frequency scaling"
help
Clock scaling allows you to change the clock speed of CPUs on the
fly. This is a nice method to save battery power on notebooks,
because the lower the clock speed, the less power the CPU consumes.
For more information, take a look at linux/Documentation/cpufreq or
at <http://www.brodo.de/cpufreq/>
If in doubt, say N.
config CPU_FREQ_PROC_INTF
bool "/proc/cpufreq interface (DEPRECATED)"
depends on CPU_FREQ && PROC_FS
help
This enables the /proc/cpufreq interface for controlling
CPUFreq. Please note that it is recommended to use the sysfs
interface instead (which is built automatically).
For details, take a look at linux/Documentation/cpufreq.
If in doubt, say N.
config CPU_FREQ_24_API
bool "/proc/sys/cpu/ interface (2.4. / OLD)"
depends on CPU_FREQ
help
This enables the /proc/sys/cpu/ sysctl interface for controlling
CPUFreq, as known from the 2.4.-kernel patches for CPUFreq. 2.5
uses a sysfs interface instead. Please note that some drivers do
not work well with the 2.4. /proc/sys/cpu sysctl interface,
so if in doubt, say N here.
For details, take a look at linux/Documentation/cpufreq.
If in doubt, say N.
config CPU_FREQ_PMAC
bool "Support for Apple PowerBooks"
depends on CPU_FREQ && ADB_PMU
help
This adds support for frequency switching on Apple PowerBooks,
this currently includes some models of iBook & Titanium
PowerBook.
endmenu
endmenu
menu "General setup"
menu "General setup"
...
...
arch/ppc/platforms/Makefile
View file @
8a1da912
...
@@ -24,6 +24,9 @@ ifeq ($(CONFIG_ALL_PPC),y)
...
@@ -24,6 +24,9 @@ ifeq ($(CONFIG_ALL_PPC),y)
obj-$(CONFIG_NVRAM)
+=
pmac_nvram.o
obj-$(CONFIG_NVRAM)
+=
pmac_nvram.o
endif
endif
obj-$(CONFIG_PMAC_BACKLIGHT)
+=
pmac_backlight.o
obj-$(CONFIG_PMAC_BACKLIGHT)
+=
pmac_backlight.o
ifeq
($(CONFIG_ALL_PPC),y)
obj-$(CONFIG_CPU_FREQ_PMAC)
+=
pmac_cpufreq.o
endif
obj-$(CONFIG_PPC_RTAS)
+=
error_log.o proc_rtas.o
obj-$(CONFIG_PPC_RTAS)
+=
error_log.o proc_rtas.o
obj-$(CONFIG_PREP_RESIDUAL)
+=
residual.o
obj-$(CONFIG_PREP_RESIDUAL)
+=
residual.o
obj-$(CONFIG_ADIR)
+=
adir_setup.o adir_pic.o adir_pci.o
obj-$(CONFIG_ADIR)
+=
adir_setup.o adir_pic.o adir_pci.o
...
...
arch/ppc/platforms/pmac_cpufreq.c
0 → 100644
View file @
8a1da912
#include <linux/config.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/adb.h>
#include <linux/pmu.h>
#include <linux/slab.h>
#include <linux/cpufreq.h>
#include <linux/init.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/irq.h>
#include <asm/hardirq.h>
#include <asm/pmac_feature.h>
#include <asm/mmu_context.h>
#include <asm/sections.h>
#include <asm/cputable.h>
#include <asm/time.h>
#undef DEBUG_FREQ
extern
void
low_choose_750fx_pll
(
int
pll
);
extern
void
low_sleep_handler
(
void
);
extern
void
openpic_sleep_save_intrs
(
void
);
extern
void
openpic_sleep_restore_intrs
(
void
);
extern
void
enable_kernel_altivec
(
void
);
extern
void
enable_kernel_fp
(
void
);
static
unsigned
int
low_freq
;
static
unsigned
int
hi_freq
;
static
unsigned
int
cur_freq
;
static
int
cpufreq_uses_pmu
;
#define PMAC_CPU_LOW_SPEED 1
#define PMAC_CPU_HIGH_SPEED 0
static
inline
void
wakeup_decrementer
(
void
)
{
set_dec
(
tb_ticks_per_jiffy
);
/* No currently-supported powerbook has a 601,
* so use get_tbl, not native
*/
last_jiffy_stamp
(
0
)
=
tb_last_stamp
=
get_tbl
();
}
#ifdef DEBUG_FREQ
static
inline
void
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.
*/
extern
void
calibrate_delay
(
void
);
unsigned
long
save_lpj
=
loops_per_jiffy
;
calibrate_delay
();
loops_per_jiffy
=
save_lpj
;
}
#endif
/* Switch CPU speed under 750FX CPU control
*/
static
int
__pmac
cpu_750fx_cpu_speed
(
int
low_speed
)
{
#ifdef DEBUG_FREQ
printk
(
KERN_DEBUG
"HID1, before: %x
\n
"
,
mfspr
(
SPRN_HID1
));
#endif
low_choose_750fx_pll
(
low_speed
);
#ifdef DEBUG_FREQ
printk
(
KERN_DEBUG
"HID1, after: %x
\n
"
,
mfspr
(
SPRN_HID1
));
debug_calc_bogomips
();
#endif
return
0
;
}
/* Switch CPU speed under PMU control
*/
static
int
__pmac
pmu_set_cpu_speed
(
unsigned
int
low_speed
)
{
struct
adb_request
req
;
unsigned
long
save_l2cr
;
unsigned
long
save_l3cr
;
#ifdef DEBUG_FREQ
printk
(
KERN_DEBUG
"HID1, before: %x
\n
"
,
mfspr
(
SPRN_HID1
));
#endif
/* Disable all interrupt sources on openpic */
openpic_sleep_save_intrs
();
/* Make sure the PMU is idle */
pmu_suspend
();
/* Make sure the decrementer won't interrupt us */
asm
volatile
(
"mtdec %0"
:
:
"r"
(
0x7fffffff
));
/* Make sure any pending DEC interrupt occuring while we did
* the above didn't re-enable the DEC */
mb
();
asm
volatile
(
"mtdec %0"
:
:
"r"
(
0x7fffffff
));
/* We can now disable MSR_EE */
local_irq_disable
();
/* Giveup the FPU & vec */
enable_kernel_fp
();
#ifdef CONFIG_ALTIVEC
if
(
cur_cpu_spec
[
0
]
->
cpu_features
&
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) */
if
(
save_l3cr
!=
0xffffffff
&&
(
save_l3cr
&
L3CR_L3E
)
!=
0
)
_set_L3CR
(
save_l3cr
&
0x7fffffff
);
if
(
save_l2cr
!=
0xffffffff
&&
(
save_l2cr
&
L2CR_L2E
)
!=
0
)
_set_L2CR
(
save_l2cr
&
0x7fffffff
);
/* 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
();
pmac_call_feature
(
PMAC_FTR_SLEEP_STATE
,
NULL
,
1
,
1
);
low_sleep_handler
();
pmac_call_feature
(
PMAC_FTR_SLEEP_STATE
,
NULL
,
1
,
0
);
/* Restore L2 cache */
if
(
save_l2cr
!=
0xffffffff
&&
(
save_l2cr
&
L2CR_L2E
)
!=
0
)
_set_L2CR
(
save_l2cr
);
/* Restore L3 cache */
if
(
save_l3cr
!=
0xffffffff
&&
(
save_l3cr
&
L3CR_L3E
)
!=
0
)
_set_L3CR
(
save_l3cr
);
/* Restore userland MMU context */
set_context
(
current
->
active_mm
->
context
,
current
->
active_mm
->
pgd
);
#ifdef DEBUG_FREQ
printk
(
KERN_DEBUG
"HID1, after: %x
\n
"
,
mfspr
(
SPRN_HID1
));
#endif
/* Restore decrementer */
wakeup_decrementer
();
/* Restore interrupts */
openpic_sleep_restore_intrs
();
pmu_resume
();
/* Let interrupts flow again ... */
local_irq_enable
();
#ifdef DEBUG_FREQ
debug_calc_bogomips
();
#endif
return
0
;
}
static
int
__pmac
do_set_cpu_speed
(
int
speed_mode
)
{
struct
cpufreq_freqs
freqs
;
int
rc
;
freqs
.
old
=
cur_freq
;
freqs
.
new
=
(
speed_mode
==
PMAC_CPU_HIGH_SPEED
)
?
hi_freq
:
low_freq
;
freqs
.
cpu
=
CPUFREQ_ALL_CPUS
;
cpufreq_notify_transition
(
&
freqs
,
CPUFREQ_PRECHANGE
);
if
(
cpufreq_uses_pmu
)
rc
=
pmu_set_cpu_speed
(
speed_mode
);
else
rc
=
cpu_750fx_cpu_speed
(
speed_mode
);
cpufreq_notify_transition
(
&
freqs
,
CPUFREQ_POSTCHANGE
);
cur_freq
=
(
speed_mode
==
PMAC_CPU_HIGH_SPEED
)
?
hi_freq
:
low_freq
;
return
rc
;
}
static
int
__pmac
pmac_cpufreq_verify
(
struct
cpufreq_policy
*
policy
)
{
if
(
!
policy
)
return
-
EINVAL
;
policy
->
cpu
=
0
;
/* UP only */
cpufreq_verify_within_limits
(
policy
,
low_freq
,
hi_freq
);
if
((
policy
->
min
>
low_freq
)
&&
(
policy
->
max
<
hi_freq
))
policy
->
max
=
hi_freq
;
return
0
;
}
static
int
__pmac
pmac_cpufreq_setpolicy
(
struct
cpufreq_policy
*
policy
)
{
int
rc
;
if
(
!
policy
)
return
-
EINVAL
;
if
(
policy
->
min
>
low_freq
)
rc
=
do_set_cpu_speed
(
PMAC_CPU_HIGH_SPEED
);
else
if
(
policy
->
max
<
hi_freq
)
rc
=
do_set_cpu_speed
(
PMAC_CPU_LOW_SPEED
);
else
if
(
policy
->
policy
==
CPUFREQ_POLICY_POWERSAVE
)
rc
=
do_set_cpu_speed
(
PMAC_CPU_LOW_SPEED
);
else
rc
=
do_set_cpu_speed
(
PMAC_CPU_HIGH_SPEED
);
return
rc
;
}
unsigned
int
__pmac
pmac_get_one_cpufreq
(
int
i
)
{
/* Supports only one CPU for now */
return
(
i
==
0
)
?
cur_freq
:
0
;
}
/* Currently, we support the following machines:
*
* - Titanium PowerBook 800 (PMU based, 667Mhz & 800Mhz)
* - Titanium PowerBook 500 (PMU based, 300Mhz & 500Mhz)
* - iBook2 500 (PMU based, 400Mhz & 500Mhz)
* - iBook2 700 (CPU based, 400Mhz & 700Mhz, support low voltage)
*/
static
int
__init
pmac_cpufreq_setup
(
void
)
{
struct
device_node
*
cpunode
;
struct
cpufreq_driver
*
driver
;
u32
*
value
;
int
has_freq_ctl
=
0
;
int
rc
;
memset
(
&
driver
,
0
,
sizeof
(
driver
));
/* Assume only one CPU */
cpunode
=
find_type_devices
(
"cpu"
);
if
(
!
cpunode
)
goto
out
;
/* Get current cpu clock freq */
value
=
(
u32
*
)
get_property
(
cpunode
,
"clock-frequency"
,
NULL
);
if
(
!
value
)
goto
out
;
cur_freq
=
(
*
value
)
/
1000
;
/* Check for tibook 800Mhz or 1Ghz */
if
(
machine_is_compatible
(
"PowerBook3,4"
)
||
machine_is_compatible
(
"PowerBook3,5"
))
{
value
=
(
u32
*
)
get_property
(
cpunode
,
"min-clock-frequency"
,
NULL
);
if
(
!
value
)
goto
out
;
low_freq
=
(
*
value
)
/
1000
;
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
;
}
/* Else check for iBook2 500 */
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
;
}
/* Else check for TiPb 500 */
else
if
(
machine_is_compatible
(
"PowerBook3,2"
))
{
/* We only know about 500Mhz model */
if
(
cur_freq
<
450000
||
cur_freq
>
550000
)
goto
out
;
hi_freq
=
cur_freq
;
low_freq
=
300000
;
has_freq_ctl
=
1
;
cpufreq_uses_pmu
=
1
;
}
/* Else check for 750FX */
else
if
(
PVR_VER
(
mfspr
(
PVR
))
==
0x7000
)
{
if
(
get_property
(
cpunode
,
"dynamic-power-step"
,
NULL
)
==
NULL
)
goto
out
;
hi_freq
=
cur_freq
;
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
;
}
out:
if
(
!
has_freq_ctl
)
return
-
ENODEV
;
/* initialization of main "cpufreq" code*/
driver
=
kmalloc
(
sizeof
(
struct
cpufreq_driver
)
+
NR_CPUS
*
sizeof
(
struct
cpufreq_policy
),
GFP_KERNEL
);
if
(
!
driver
)
return
-
ENOMEM
;
driver
->
policy
=
(
struct
cpufreq_policy
*
)
(
driver
+
1
);
#ifdef CONFIG_CPU_FREQ_24_API
driver
->
cpu_cur_freq
[
0
]
=
cur_freq
;
#endif
driver
->
verify
=
&
pmac_cpufreq_verify
;
driver
->
setpolicy
=
&
pmac_cpufreq_setpolicy
;
driver
->
init
=
NULL
;
driver
->
exit
=
NULL
;
strncpy
(
driver
->
name
,
"powermac"
,
CPUFREQ_NAME_LEN
);
driver
->
policy
[
0
].
cpu
=
0
;
driver
->
policy
[
0
].
cpuinfo
.
transition_latency
=
CPUFREQ_ETERNAL
;
driver
->
policy
[
0
].
cpuinfo
.
min_freq
=
low_freq
;
driver
->
policy
[
0
].
min
=
low_freq
;
driver
->
policy
[
0
].
max
=
cur_freq
;
driver
->
policy
[
0
].
cpuinfo
.
max_freq
=
cur_freq
;
driver
->
policy
[
0
].
policy
=
(
cur_freq
==
low_freq
)
?
CPUFREQ_POLICY_POWERSAVE
:
CPUFREQ_POLICY_PERFORMANCE
;
rc
=
cpufreq_register_driver
(
driver
);
if
(
rc
)
kfree
(
driver
);
return
rc
;
}
__initcall
(
pmac_cpufreq_setup
);
arch/ppc/platforms/pmac_setup.c
View file @
8a1da912
...
@@ -225,6 +225,20 @@ pmac_show_cpuinfo(struct seq_file *m)
...
@@ -225,6 +225,20 @@ pmac_show_cpuinfo(struct seq_file *m)
return
0
;
return
0
;
}
}
int
__openfirmware
pmac_show_percpuinfo
(
struct
seq_file
*
m
,
int
i
)
{
#ifdef CONFIG_CPU_FREQ_PMAC
extern
unsigned
int
pmac_get_one_cpufreq
(
int
i
);
unsigned
int
freq
=
pmac_get_one_cpufreq
(
i
);
if
(
freq
!=
0
)
{
seq_printf
(
m
,
"clock
\t\t
: %dMHz
\n
"
,
freq
/
1000
);
return
0
;
}
#endif
/* CONFIG_CPU_FREQ_PMAC */
return
of_show_percpuinfo
(
m
,
i
);
}
static
volatile
u32
*
sysctrl_regs
;
static
volatile
u32
*
sysctrl_regs
;
void
__init
void
__init
...
@@ -604,7 +618,7 @@ pmac_init(unsigned long r3, unsigned long r4, unsigned long r5,
...
@@ -604,7 +618,7 @@ pmac_init(unsigned long r3, unsigned long r4, unsigned long r5,
ppc_md
.
setup_arch
=
pmac_setup_arch
;
ppc_md
.
setup_arch
=
pmac_setup_arch
;
ppc_md
.
show_cpuinfo
=
pmac_show_cpuinfo
;
ppc_md
.
show_cpuinfo
=
pmac_show_cpuinfo
;
ppc_md
.
show_percpuinfo
=
of
_show_percpuinfo
;
ppc_md
.
show_percpuinfo
=
pmac
_show_percpuinfo
;
ppc_md
.
irq_cannonicalize
=
NULL
;
ppc_md
.
irq_cannonicalize
=
NULL
;
ppc_md
.
init_IRQ
=
pmac_pic_init
;
ppc_md
.
init_IRQ
=
pmac_pic_init
;
ppc_md
.
get_irq
=
pmac_get_irq
;
/* Changed later on ... */
ppc_md
.
get_irq
=
pmac_get_irq
;
/* Changed later on ... */
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment