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
Kirill Smelkov
linux
Commits
da9f0ac2
Commit
da9f0ac2
authored
Jul 20, 2007
by
Paul Mundt
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'clkfwk'
parents
bd5f0d1c
cb5ec75b
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
113 additions
and
135 deletions
+113
-135
arch/sh/Kconfig
arch/sh/Kconfig
+1
-1
arch/sh/kernel/cpu/clock.c
arch/sh/kernel/cpu/clock.c
+16
-0
arch/sh/kernel/cpu/sh4a/clock-sh7722.c
arch/sh/kernel/cpu/sh4a/clock-sh7722.c
+15
-0
arch/sh/kernel/cpufreq.c
arch/sh/kernel/cpufreq.c
+80
-134
include/asm-sh/clock.h
include/asm-sh/clock.h
+1
-0
No files found.
arch/sh/Kconfig
View file @
da9f0ac2
...
...
@@ -475,7 +475,7 @@ source "drivers/cpufreq/Kconfig"
config SH_CPU_FREQ
tristate "SuperH CPU Frequency driver"
depends on CPU_FREQ
&& CPU_SH4 && BROKEN
depends on CPU_FREQ
select CPU_FREQ_TABLE
help
This adds the cpufreq driver for SuperH. At present, only
...
...
arch/sh/kernel/cpu/clock.c
View file @
da9f0ac2
...
...
@@ -229,6 +229,22 @@ void clk_recalc_rate(struct clk *clk)
}
EXPORT_SYMBOL_GPL
(
clk_recalc_rate
);
long
clk_round_rate
(
struct
clk
*
clk
,
unsigned
long
rate
)
{
if
(
likely
(
clk
->
ops
&&
clk
->
ops
->
round_rate
))
{
unsigned
long
flags
,
rounded
;
spin_lock_irqsave
(
&
clock_lock
,
flags
);
rounded
=
clk
->
ops
->
round_rate
(
clk
,
rate
);
spin_unlock_irqrestore
(
&
clock_lock
,
flags
);
return
rounded
;
}
return
clk_get_rate
(
clk
);
}
EXPORT_SYMBOL_GPL
(
clk_round_rate
);
/*
* Returns a clock. Note that we first try to use device id on the bus
* and clock name. If this fails, we try to use clock name only.
...
...
arch/sh/kernel/cpu/sh4a/clock-sh7722.c
View file @
da9f0ac2
...
...
@@ -387,9 +387,24 @@ static int sh7722_frqcr_set_rate(struct clk *clk, unsigned long rate,
return
err
;
}
static
long
sh7722_frqcr_round_rate
(
struct
clk
*
clk
,
unsigned
long
rate
)
{
unsigned
long
parent_rate
=
clk
->
parent
->
rate
;
int
div
;
/* look for multiplier/divisor pair */
div
=
sh7722_find_divisors
(
parent_rate
,
rate
);
if
(
div
<
0
)
return
clk
->
rate
;
/* calculate new value of clock rate */
return
parent_rate
*
2
/
div
;
}
static
struct
clk_ops
sh7722_frqcr_clk_ops
=
{
.
recalc
=
sh7722_frqcr_recalc
,
.
set_rate
=
sh7722_frqcr_set_rate
,
.
round_rate
=
sh7722_frqcr_round_rate
,
};
/*
...
...
arch/sh/kernel/cpufreq.c
View file @
da9f0ac2
...
...
@@ -3,89 +3,51 @@
*
* cpufreq driver for the SuperH processors.
*
* Copyright (C) 2002
, 2003, 2004, 2005
Paul Mundt
* Copyright (C) 2002
- 2007
Paul Mundt
* Copyright (C) 2002 M. R. Brown
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
* Clock framework bits from arch/avr32/mach-at32ap/cpufreq.c
*
* Copyright (C) 2004-2007 Atmel Corporation
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#include <linux/types.h>
#include <linux/cpufreq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/
delay
.h>
#include <linux/
err
.h>
#include <linux/cpumask.h>
#include <linux/smp.h>
#include <linux/sched.h>
/* set_cpus_allowed() */
#include <linux/io.h>
#include <linux/clk.h>
#include <asm/processor.h>
#include <asm/watchdog.h>
#include <asm/freq.h>
#include <asm/io.h>
/*
* For SuperH, each policy change requires that we change the IFC, BFC, and
* PFC at the same time. Here we define sane values that won't trash the
* system.
*
* Note the max set is computed at runtime, we use the divisors that we booted
* with to setup our maximum operating frequencies.
*/
struct
clock_set
{
unsigned
int
ifc
;
unsigned
int
bfc
;
unsigned
int
pfc
;
}
clock_sets
[]
=
{
#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH2)
{
0
,
0
,
0
},
/* not implemented yet */
#elif defined(CONFIG_CPU_SH4)
{
4
,
8
,
8
},
/* min - IFC: 1/4, BFC: 1/8, PFC: 1/8 */
{
1
,
2
,
2
},
/* max - IFC: 1, BFC: 1/2, PFC: 1/2 */
#endif
};
#define MIN_CLOCK_SET 0
#define MAX_CLOCK_SET (ARRAY_SIZE(clock_sets) - 1)
/*
* For the time being, we only support two frequencies, which in turn are
* aimed at the POWERSAVE and PERFORMANCE policies, which in turn are derived
* directly from the respective min/max clock sets. Technically we could
* support a wider range of frequencies, but these vary far too much for each
* CPU subtype (and we'd have to construct a frequency table for each subtype).
*
* Maybe something to implement in the future..
*/
#define SH_FREQ_MAX 0
#define SH_FREQ_MIN 1
#include <asm/clock.h>
static
struct
cpufreq_frequency_table
sh_freqs
[]
=
{
{
SH_FREQ_MAX
,
0
},
{
SH_FREQ_MIN
,
0
},
{
0
,
CPUFREQ_TABLE_END
},
};
static
struct
clk
*
cpuclk
;
static
void
sh_cpufreq_update_clocks
(
unsigned
int
set
)
static
unsigned
int
sh_cpufreq_get
(
unsigned
int
cpu
)
{
current_cpu_data
.
cpu_clock
=
current_cpu_data
.
master_clock
/
clock_sets
[
set
].
ifc
;
current_cpu_data
.
bus_clock
=
current_cpu_data
.
master_clock
/
clock_sets
[
set
].
bfc
;
current_cpu_data
.
module_clock
=
current_cpu_data
.
master_clock
/
clock_sets
[
set
].
pfc
;
current_cpu_data
.
loops_per_jiffy
=
loops_per_jiffy
;
return
(
clk_get_rate
(
cpuclk
)
+
500
)
/
1000
;
}
/* XXX: This needs to be split out per CPU and CPU subtype. */
/*
* Here we notify other drivers of the proposed change and the final change.
*/
static
int
sh_cpufreq_setstate
(
unsigned
int
cpu
,
unsigned
int
set
)
static
int
sh_cpufreq_target
(
struct
cpufreq_policy
*
policy
,
unsigned
int
target_freq
,
unsigned
int
relation
)
{
unsigned
short
frqcr
=
ctrl_inw
(
FRQCR
)
;
unsigned
int
cpu
=
policy
->
cpu
;
cpumask_t
cpus_allowed
;
struct
cpufreq_freqs
freqs
;
long
freq
;
if
(
!
cpu_online
(
cpu
))
return
-
ENODEV
;
...
...
@@ -95,125 +57,109 @@ static int sh_cpufreq_setstate(unsigned int cpu, unsigned int set)
BUG_ON
(
smp_processor_id
()
!=
cpu
);
freqs
.
cpu
=
cpu
;
freqs
.
old
=
current_cpu_data
.
cpu_clock
/
1000
;
freqs
.
new
=
(
current_cpu_data
.
master_clock
/
clock_sets
[
set
].
ifc
)
/
1000
;
/* Convert target_freq from kHz to Hz */
freq
=
clk_round_rate
(
cpuclk
,
target_freq
*
1000
);
cpufreq_notify_transition
(
&
freqs
,
CPUFREQ_PRECHANGE
);
#if defined(CONFIG_CPU_SH3)
frqcr
|=
(
newstate
&
0x4000
)
<<
14
;
frqcr
|=
(
newstate
&
0x000c
)
<<
2
;
#elif defined(CONFIG_CPU_SH4)
/*
* FRQCR.PLL2EN is 1, we need to allow the PLL to stabilize by
* initializing the WDT.
*/
if
(
frqcr
&
(
1
<<
9
))
{
__u8
csr
;
/*
* Set the overflow period to the highest available,
* in this case a 1/4096 division ratio yields a 5.25ms
* overflow period. See asm-sh/watchdog.h for more
* information and a range of other divisors.
*/
csr
=
sh_wdt_read_csr
();
csr
|=
WTCSR_CKS_4096
;
sh_wdt_write_csr
(
csr
);
sh_wdt_write_cnt
(
0
);
}
frqcr
&=
0x0e00
;
/* Clear ifc, bfc, pfc */
frqcr
|=
get_ifc_value
(
clock_sets
[
set
].
ifc
)
<<
6
;
frqcr
|=
get_bfc_value
(
clock_sets
[
set
].
bfc
)
<<
3
;
frqcr
|=
get_pfc_value
(
clock_sets
[
set
].
pfc
);
#endif
ctrl_outw
(
frqcr
,
FRQCR
);
sh_cpufreq_update_clocks
(
set
);
if
(
freq
<
(
policy
->
min
*
1000
)
||
freq
>
(
policy
->
max
*
1000
))
return
-
EINVAL
;
pr_debug
(
"cpufreq: requested frequency %u Hz
\n
"
,
target_freq
*
1000
);
freqs
.
cpu
=
cpu
;
freqs
.
old
=
sh_cpufreq_get
(
cpu
);
freqs
.
new
=
(
freq
+
500
)
/
1000
;
freqs
.
flags
=
0
;
cpufreq_notify_transition
(
&
freqs
,
CPUFREQ_PRECHANGE
);
set_cpus_allowed
(
current
,
cpus_allowed
);
clk_set_rate
(
cpuclk
,
freq
);
cpufreq_notify_transition
(
&
freqs
,
CPUFREQ_POSTCHANGE
);
pr_debug
(
"cpufreq: set frequency %lu Hz
\n
"
,
freq
);
return
0
;
}
static
int
sh_cpufreq_cpu_init
(
struct
cpufreq_policy
*
policy
)
{
unsigned
int
min_freq
,
max_freq
;
unsigned
int
ifc
,
bfc
,
pfc
;
printk
(
KERN_INFO
"cpufreq: SuperH CPU frequency driver.
\n
"
);
if
(
!
cpu_online
(
policy
->
cpu
))
return
-
ENODEV
;
/* Update our maximum clock set */
get_current_frequency_divisors
(
&
ifc
,
&
bfc
,
&
pfc
);
clock_sets
[
MAX_CLOCK_SET
].
ifc
=
ifc
;
clock_sets
[
MAX_CLOCK_SET
].
bfc
=
bfc
;
clock_sets
[
MAX_CLOCK_SET
].
pfc
=
pfc
;
/* Convert from Hz to kHz */
max_freq
=
current_cpu_data
.
cpu_clock
/
1000
;
min_freq
=
(
current_cpu_data
.
master_clock
/
clock_sets
[
MIN_CLOCK_SET
].
ifc
)
/
1000
;
sh_freqs
[
SH_FREQ_MAX
].
frequency
=
max_freq
;
sh_freqs
[
SH_FREQ_MIN
].
frequency
=
min_freq
;
cpuclk
=
clk_get
(
NULL
,
"cpu_clk"
);
if
(
IS_ERR
(
cpuclk
))
{
printk
(
KERN_ERR
"cpufreq: couldn't get CPU clk
\n
"
);
return
PTR_ERR
(
cpuclk
);
}
/* cpuinfo and default policy values */
policy
->
governor
=
CPUFREQ_DEFAULT_GOVERNOR
;
policy
->
cpuinfo
.
min_freq
=
(
clk_round_rate
(
cpuclk
,
1
)
+
500
)
/
1000
;
policy
->
cpuinfo
.
max_freq
=
(
clk_round_rate
(
cpuclk
,
~
0UL
)
+
500
)
/
1000
;
policy
->
cpuinfo
.
transition_latency
=
CPUFREQ_ETERNAL
;
policy
->
cur
=
max_freq
;
return
cpufreq_frequency_table_cpuinfo
(
policy
,
&
sh_freqs
[
0
]);
}
policy
->
governor
=
CPUFREQ_DEFAULT_GOVERNOR
;
policy
->
cur
=
sh_cpufreq_get
(
policy
->
cpu
);
policy
->
min
=
policy
->
cpuinfo
.
min_freq
;
policy
->
max
=
policy
->
cpuinfo
.
max_freq
;
static
int
sh_cpufreq_verify
(
struct
cpufreq_policy
*
policy
)
{
return
cpufreq_frequency_table_verify
(
policy
,
&
sh_freqs
[
0
]);
}
static
int
sh_cpufreq_target
(
struct
cpufreq_policy
*
policy
,
unsigned
int
target_freq
,
unsigned
int
relation
)
{
unsigned
int
set
,
idx
=
0
;
/*
* Catch the cases where the clock framework hasn't been wired up
* properly to support scaling.
*/
if
(
unlikely
(
policy
->
min
==
policy
->
max
))
{
printk
(
KERN_ERR
"cpufreq: clock framework rate rounding "
"not supported on this CPU.
\n
"
);
if
(
cpufreq_frequency_table_target
(
policy
,
&
sh_freqs
[
0
],
target_freq
,
relation
,
&
idx
))
clk_put
(
cpuclk
);
return
-
EINVAL
;
}
set
=
(
idx
==
SH_FREQ_MIN
)
?
MIN_CLOCK_SET
:
MAX_CLOCK_SET
;
printk
(
KERN_INFO
"cpufreq: Frequencies - Minimum %u.%03u MHz, "
"Maximum %u.%03u MHz.
\n
"
,
policy
->
min
/
1000
,
policy
->
min
%
1000
,
policy
->
max
/
1000
,
policy
->
max
%
1000
);
sh_cpufreq_setstate
(
policy
->
cpu
,
set
);
return
0
;
}
static
int
sh_cpufreq_verify
(
struct
cpufreq_policy
*
policy
)
{
cpufreq_verify_within_limits
(
policy
,
policy
->
cpuinfo
.
min_freq
,
policy
->
cpuinfo
.
max_freq
);
return
0
;
}
static
int
sh_cpufreq_exit
(
struct
cpufreq_policy
*
policy
)
{
clk_put
(
cpuclk
);
return
0
;
}
static
struct
cpufreq_driver
sh_cpufreq_driver
=
{
.
owner
=
THIS_MODULE
,
.
name
=
"
SH cpufreq
"
,
.
name
=
"
sh
"
,
.
init
=
sh_cpufreq_cpu_init
,
.
verify
=
sh_cpufreq_verify
,
.
target
=
sh_cpufreq_target
,
.
get
=
sh_cpufreq_get
,
.
exit
=
sh_cpufreq_exit
,
};
static
int
__init
sh_cpufreq_init
(
void
)
static
int
__init
sh_cpufreq_
module_
init
(
void
)
{
if
(
!
current_cpu_data
.
cpu_clock
)
return
-
EINVAL
;
if
(
cpufreq_register_driver
(
&
sh_cpufreq_driver
))
return
-
EINVAL
;
return
0
;
return
cpufreq_register_driver
(
&
sh_cpufreq_driver
);
}
static
void
__exit
sh_cpufreq_exit
(
void
)
static
void
__exit
sh_cpufreq_
module_
exit
(
void
)
{
cpufreq_unregister_driver
(
&
sh_cpufreq_driver
);
}
module_init
(
sh_cpufreq_init
);
module_exit
(
sh_cpufreq_exit
);
module_init
(
sh_cpufreq_
module_
init
);
module_exit
(
sh_cpufreq_
module_
exit
);
MODULE_AUTHOR
(
"Paul Mundt <lethal@linux-sh.org>"
);
MODULE_DESCRIPTION
(
"cpufreq driver for SuperH"
);
MODULE_LICENSE
(
"GPL"
);
include/asm-sh/clock.h
View file @
da9f0ac2
...
...
@@ -14,6 +14,7 @@ struct clk_ops {
void
(
*
disable
)(
struct
clk
*
clk
);
void
(
*
recalc
)(
struct
clk
*
clk
);
int
(
*
set_rate
)(
struct
clk
*
clk
,
unsigned
long
rate
,
int
algo_id
);
long
(
*
round_rate
)(
struct
clk
*
clk
,
unsigned
long
rate
);
};
struct
clk
{
...
...
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