Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openeuler
Kernel
提交
ba3ec578
K
Kernel
项目概览
openeuler
/
Kernel
1 年多 前同步成功
通知
8
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
K
Kernel
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
ba3ec578
编写于
7月 22, 2014
作者:
J
Jason Cooper
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'mvebu/soc-cpufreq' into mvebu/soc
上级
ba364fc7
ee2d8ea1
变更
5
隐藏空白更改
内联
并排
Showing
5 changed file
with
261 addition
and
7 deletion
+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
未找到文件。
Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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__ */
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录