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
ba3ec578
Commit
ba3ec578
authored
Jul 22, 2014
by
Jason Cooper
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'mvebu/soc-cpufreq' into mvebu/soc
parents
ba364fc7
ee2d8ea1
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
261 additions
and
7 deletions
+261
-7
Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt
Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt
+3
-2
arch/arm/mach-mvebu/platsmp.c
arch/arm/mach-mvebu/platsmp.c
+1
-0
arch/arm/mach-mvebu/pmsu.c
arch/arm/mach-mvebu/pmsu.c
+162
-0
drivers/clk/mvebu/clk-cpu.c
drivers/clk/mvebu/clk-cpu.c
+75
-5
include/linux/mvebu-pmsu.h
include/linux/mvebu-pmsu.h
+20
-0
No files found.
Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt
View file @
ba3ec578
...
...
@@ -3,14 +3,15 @@ Device Tree Clock bindings for cpu clock of Marvell EBU platforms
Required properties:
- compatible : shall be one of the following:
"marvell,armada-xp-cpu-clock" - cpu clocks for Armada XP
- reg : Address and length of the clock complex register set
- reg : Address and length of the clock complex register set, followed
by address and length of the PMU DFS registers
- #clock-cells : should be set to 1.
- clocks : shall be the input parent clock phandle for the clock.
cpuclk: clock-complex@d0018700 {
#clock-cells = <1>;
compatible = "marvell,armada-xp-cpu-clock";
reg = <0xd0018700 0xA0>;
reg = <0xd0018700 0xA0>
, <0x1c054 0x10>
;
clocks = <&coreclk 1>;
}
...
...
arch/arm/mach-mvebu/platsmp.c
View file @
ba3ec578
...
...
@@ -67,6 +67,7 @@ static void __init set_secondary_cpus_clock(void)
if
(
!
cpu_clk
)
return
;
clk_set_rate
(
cpu_clk
,
rate
);
clk_prepare_enable
(
cpu_clk
);
}
}
...
...
arch/arm/mach-mvebu/pmsu.c
View file @
ba3ec578
...
...
@@ -18,20 +18,26 @@
#define pr_fmt(fmt) "mvebu-pmsu: " fmt
#include <linux/clk.h>
#include <linux/cpu_pm.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/smp.h>
#include <linux/resource.h>
#include <linux/slab.h>
#include <asm/cacheflush.h>
#include <asm/cp15.h>
#include <asm/smp_plat.h>
#include <asm/suspend.h>
#include <asm/tlbflush.h>
#include "common.h"
#include "armada-370-xp.h"
static
void
__iomem
*
pmsu_mp_base
;
...
...
@@ -57,6 +63,10 @@ static void __iomem *pmsu_mp_base;
#define PMSU_STATUS_AND_MASK_IRQ_MASK BIT(24)
#define PMSU_STATUS_AND_MASK_FIQ_MASK BIT(25)
#define PMSU_EVENT_STATUS_AND_MASK(cpu) ((cpu * 0x100) + 0x120)
#define PMSU_EVENT_STATUS_AND_MASK_DFS_DONE BIT(1)
#define PMSU_EVENT_STATUS_AND_MASK_DFS_DONE_MASK BIT(17)
#define PMSU_BOOT_ADDR_REDIRECT_OFFSET(cpu) ((cpu * 0x100) + 0x124)
/* PMSU fabric registers */
...
...
@@ -291,3 +301,155 @@ static int __init armada_370_xp_cpu_pm_init(void)
arch_initcall
(
armada_370_xp_cpu_pm_init
);
early_initcall
(
armada_370_xp_pmsu_init
);
static
void
mvebu_pmsu_dfs_request_local
(
void
*
data
)
{
u32
reg
;
u32
cpu
=
smp_processor_id
();
unsigned
long
flags
;
local_irq_save
(
flags
);
/* Prepare to enter idle */
reg
=
readl
(
pmsu_mp_base
+
PMSU_STATUS_AND_MASK
(
cpu
));
reg
|=
PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT
|
PMSU_STATUS_AND_MASK_IRQ_MASK
|
PMSU_STATUS_AND_MASK_FIQ_MASK
;
writel
(
reg
,
pmsu_mp_base
+
PMSU_STATUS_AND_MASK
(
cpu
));
/* Request the DFS transition */
reg
=
readl
(
pmsu_mp_base
+
PMSU_CONTROL_AND_CONFIG
(
cpu
));
reg
|=
PMSU_CONTROL_AND_CONFIG_DFS_REQ
;
writel
(
reg
,
pmsu_mp_base
+
PMSU_CONTROL_AND_CONFIG
(
cpu
));
/* The fact of entering idle will trigger the DFS transition */
wfi
();
/*
* We're back from idle, the DFS transition has completed,
* clear the idle wait indication.
*/
reg
=
readl
(
pmsu_mp_base
+
PMSU_STATUS_AND_MASK
(
cpu
));
reg
&=
~
PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT
;
writel
(
reg
,
pmsu_mp_base
+
PMSU_STATUS_AND_MASK
(
cpu
));
local_irq_restore
(
flags
);
}
int
mvebu_pmsu_dfs_request
(
int
cpu
)
{
unsigned
long
timeout
;
int
hwcpu
=
cpu_logical_map
(
cpu
);
u32
reg
;
/* Clear any previous DFS DONE event */
reg
=
readl
(
pmsu_mp_base
+
PMSU_EVENT_STATUS_AND_MASK
(
hwcpu
));
reg
&=
~
PMSU_EVENT_STATUS_AND_MASK_DFS_DONE
;
writel
(
reg
,
pmsu_mp_base
+
PMSU_EVENT_STATUS_AND_MASK
(
hwcpu
));
/* Mask the DFS done interrupt, since we are going to poll */
reg
=
readl
(
pmsu_mp_base
+
PMSU_EVENT_STATUS_AND_MASK
(
hwcpu
));
reg
|=
PMSU_EVENT_STATUS_AND_MASK_DFS_DONE_MASK
;
writel
(
reg
,
pmsu_mp_base
+
PMSU_EVENT_STATUS_AND_MASK
(
hwcpu
));
/* Trigger the DFS on the appropriate CPU */
smp_call_function_single
(
cpu
,
mvebu_pmsu_dfs_request_local
,
NULL
,
false
);
/* Poll until the DFS done event is generated */
timeout
=
jiffies
+
HZ
;
while
(
time_before
(
jiffies
,
timeout
))
{
reg
=
readl
(
pmsu_mp_base
+
PMSU_EVENT_STATUS_AND_MASK
(
hwcpu
));
if
(
reg
&
PMSU_EVENT_STATUS_AND_MASK_DFS_DONE
)
break
;
udelay
(
10
);
}
if
(
time_after
(
jiffies
,
timeout
))
return
-
ETIME
;
/* Restore the DFS mask to its original state */
reg
=
readl
(
pmsu_mp_base
+
PMSU_EVENT_STATUS_AND_MASK
(
hwcpu
));
reg
&=
~
PMSU_EVENT_STATUS_AND_MASK_DFS_DONE_MASK
;
writel
(
reg
,
pmsu_mp_base
+
PMSU_EVENT_STATUS_AND_MASK
(
hwcpu
));
return
0
;
}
static
int
__init
armada_xp_pmsu_cpufreq_init
(
void
)
{
struct
device_node
*
np
;
struct
resource
res
;
int
ret
,
cpu
;
if
(
!
of_machine_is_compatible
(
"marvell,armadaxp"
))
return
0
;
/*
* In order to have proper cpufreq handling, we need to ensure
* that the Device Tree description of the CPU clock includes
* the definition of the PMU DFS registers. If not, we do not
* register the clock notifier and the cpufreq driver. This
* piece of code is only for compatibility with old Device
* Trees.
*/
np
=
of_find_compatible_node
(
NULL
,
NULL
,
"marvell,armada-xp-cpu-clock"
);
if
(
!
np
)
return
0
;
ret
=
of_address_to_resource
(
np
,
1
,
&
res
);
if
(
ret
)
{
pr_warn
(
FW_WARN
"not enabling cpufreq, deprecated armada-xp-cpu-clock binding
\n
"
);
of_node_put
(
np
);
return
0
;
}
of_node_put
(
np
);
/*
* For each CPU, this loop registers the operating points
* supported (which are the nominal CPU frequency and half of
* it), and registers the clock notifier that will take care
* of doing the PMSU part of a frequency transition.
*/
for_each_possible_cpu
(
cpu
)
{
struct
device
*
cpu_dev
;
struct
clk
*
clk
;
int
ret
;
cpu_dev
=
get_cpu_device
(
cpu
);
if
(
!
cpu_dev
)
{
pr_err
(
"Cannot get CPU %d
\n
"
,
cpu
);
continue
;
}
clk
=
clk_get
(
cpu_dev
,
0
);
if
(
!
clk
)
{
pr_err
(
"Cannot get clock for CPU %d
\n
"
,
cpu
);
return
-
ENODEV
;
}
/*
* In case of a failure of dev_pm_opp_add(), we don't
* bother with cleaning up the registered OPP (there's
* no function to do so), and simply cancel the
* registration of the cpufreq device.
*/
ret
=
dev_pm_opp_add
(
cpu_dev
,
clk_get_rate
(
clk
),
0
);
if
(
ret
)
{
clk_put
(
clk
);
return
ret
;
}
ret
=
dev_pm_opp_add
(
cpu_dev
,
clk_get_rate
(
clk
)
/
2
,
0
);
if
(
ret
)
{
clk_put
(
clk
);
return
ret
;
}
}
platform_device_register_simple
(
"cpufreq-generic"
,
-
1
,
NULL
,
0
);
return
0
;
}
device_initcall
(
armada_xp_pmsu_cpufreq_init
);
drivers/clk/mvebu/clk-cpu.c
View file @
ba3ec578
...
...
@@ -16,10 +16,19 @@
#include <linux/io.h>
#include <linux/of.h>
#include <linux/delay.h>
#include <linux/mvebu-pmsu.h>
#include <asm/smp_plat.h>
#define SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET 0x0
#define SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET 0xC
#define SYS_CTRL_CLK_DIVIDER_MASK 0x3F
#define SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET 0x0
#define SYS_CTRL_CLK_DIVIDER_CTRL_RESET_ALL 0xff
#define SYS_CTRL_CLK_DIVIDER_CTRL_RESET_SHIFT 8
#define SYS_CTRL_CLK_DIVIDER_CTRL2_OFFSET 0x8
#define SYS_CTRL_CLK_DIVIDER_CTRL2_NBCLK_RATIO_SHIFT 16
#define SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET 0xC
#define SYS_CTRL_CLK_DIVIDER_MASK 0x3F
#define PMU_DFS_RATIO_SHIFT 16
#define PMU_DFS_RATIO_MASK 0x3F
#define MAX_CPU 4
struct
cpu_clk
{
...
...
@@ -28,6 +37,7 @@ struct cpu_clk {
const
char
*
clk_name
;
const
char
*
parent_name
;
void
__iomem
*
reg_base
;
void
__iomem
*
pmu_dfs
;
};
static
struct
clk
**
clks
;
...
...
@@ -62,8 +72,9 @@ static long clk_cpu_round_rate(struct clk_hw *hwclk, unsigned long rate,
return
*
parent_rate
/
div
;
}
static
int
clk_cpu_set_rate
(
struct
clk_hw
*
hwclk
,
unsigned
long
rate
,
unsigned
long
parent_rate
)
static
int
clk_cpu_off_set_rate
(
struct
clk_hw
*
hwclk
,
unsigned
long
rate
,
unsigned
long
parent_rate
)
{
struct
cpu_clk
*
cpuclk
=
to_cpu_clk
(
hwclk
);
u32
reg
,
div
;
...
...
@@ -95,6 +106,58 @@ static int clk_cpu_set_rate(struct clk_hw *hwclk, unsigned long rate,
return
0
;
}
static
int
clk_cpu_on_set_rate
(
struct
clk_hw
*
hwclk
,
unsigned
long
rate
,
unsigned
long
parent_rate
)
{
u32
reg
;
unsigned
long
fabric_div
,
target_div
,
cur_rate
;
struct
cpu_clk
*
cpuclk
=
to_cpu_clk
(
hwclk
);
/*
* PMU DFS registers are not mapped, Device Tree does not
* describes them. We cannot change the frequency dynamically.
*/
if
(
!
cpuclk
->
pmu_dfs
)
return
-
ENODEV
;
cur_rate
=
__clk_get_rate
(
hwclk
->
clk
);
reg
=
readl
(
cpuclk
->
reg_base
+
SYS_CTRL_CLK_DIVIDER_CTRL2_OFFSET
);
fabric_div
=
(
reg
>>
SYS_CTRL_CLK_DIVIDER_CTRL2_NBCLK_RATIO_SHIFT
)
&
SYS_CTRL_CLK_DIVIDER_MASK
;
/* Frequency is going up */
if
(
rate
==
2
*
cur_rate
)
target_div
=
fabric_div
/
2
;
/* Frequency is going down */
else
target_div
=
fabric_div
;
if
(
target_div
==
0
)
target_div
=
1
;
reg
=
readl
(
cpuclk
->
pmu_dfs
);
reg
&=
~
(
PMU_DFS_RATIO_MASK
<<
PMU_DFS_RATIO_SHIFT
);
reg
|=
(
target_div
<<
PMU_DFS_RATIO_SHIFT
);
writel
(
reg
,
cpuclk
->
pmu_dfs
);
reg
=
readl
(
cpuclk
->
reg_base
+
SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET
);
reg
|=
(
SYS_CTRL_CLK_DIVIDER_CTRL_RESET_ALL
<<
SYS_CTRL_CLK_DIVIDER_CTRL_RESET_SHIFT
);
writel
(
reg
,
cpuclk
->
reg_base
+
SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET
);
return
mvebu_pmsu_dfs_request
(
cpuclk
->
cpu
);
}
static
int
clk_cpu_set_rate
(
struct
clk_hw
*
hwclk
,
unsigned
long
rate
,
unsigned
long
parent_rate
)
{
if
(
__clk_is_enabled
(
hwclk
->
clk
))
return
clk_cpu_on_set_rate
(
hwclk
,
rate
,
parent_rate
);
else
return
clk_cpu_off_set_rate
(
hwclk
,
rate
,
parent_rate
);
}
static
const
struct
clk_ops
cpu_ops
=
{
.
recalc_rate
=
clk_cpu_recalc_rate
,
.
round_rate
=
clk_cpu_round_rate
,
...
...
@@ -105,6 +168,7 @@ static void __init of_cpu_clk_setup(struct device_node *node)
{
struct
cpu_clk
*
cpuclk
;
void
__iomem
*
clock_complex_base
=
of_iomap
(
node
,
0
);
void
__iomem
*
pmu_dfs_base
=
of_iomap
(
node
,
1
);
int
ncpus
=
0
;
struct
device_node
*
dn
;
...
...
@@ -114,6 +178,10 @@ static void __init of_cpu_clk_setup(struct device_node *node)
return
;
}
if
(
pmu_dfs_base
==
NULL
)
pr_warn
(
"%s: pmu-dfs base register not set, dynamic frequency scaling not available
\n
"
,
__func__
);
for_each_node_by_type
(
dn
,
"cpu"
)
ncpus
++
;
...
...
@@ -146,6 +214,8 @@ static void __init of_cpu_clk_setup(struct device_node *node)
cpuclk
[
cpu
].
clk_name
=
clk_name
;
cpuclk
[
cpu
].
cpu
=
cpu
;
cpuclk
[
cpu
].
reg_base
=
clock_complex_base
;
if
(
pmu_dfs_base
)
cpuclk
[
cpu
].
pmu_dfs
=
pmu_dfs_base
+
4
*
cpu
;
cpuclk
[
cpu
].
hw
.
init
=
&
init
;
init
.
name
=
cpuclk
[
cpu
].
clk_name
;
...
...
include/linux/mvebu-pmsu.h
0 → 100644
View file @
ba3ec578
/*
* Copyright (C) 2012 Marvell
*
* Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#ifndef __MVEBU_PMSU_H__
#define __MVEBU_PMSU_H__
#ifdef CONFIG_MACH_MVEBU_V7
int
mvebu_pmsu_dfs_request
(
int
cpu
);
#else
static
inline
int
mvebu_pmsu_dfs_request
(
int
cpu
)
{
return
-
ENODEV
;
}
#endif
#endif
/* __MVEBU_PMSU_H__ */
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