Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openanolis
cloud-kernel
提交
f157f596
cloud-kernel
项目概览
openanolis
/
cloud-kernel
1 年多 前同步成功
通知
161
Star
36
Fork
7
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
10
列表
看板
标记
里程碑
合并请求
2
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
cloud-kernel
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
10
Issue
10
列表
看板
标记
里程碑
合并请求
2
合并请求
2
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
f157f596
编写于
6月 18, 2013
作者:
Z
Zhang Rui
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'cpu-package-thermal' of .git into next
Conflicts: drivers/thermal/Kconfig drivers/thermal/Makefile
上级
30072fb9
23be63f4
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
769 addition
and
4 deletion
+769
-4
Documentation/thermal/x86_pkg_temperature_thermal
Documentation/thermal/x86_pkg_temperature_thermal
+47
-0
arch/x86/include/asm/mce.h
arch/x86/include/asm/mce.h
+7
-0
arch/x86/kernel/cpu/mcheck/therm_throt.c
arch/x86/kernel/cpu/mcheck/therm_throt.c
+59
-4
drivers/thermal/Kconfig
drivers/thermal/Kconfig
+13
-0
drivers/thermal/Makefile
drivers/thermal/Makefile
+1
-0
drivers/thermal/x86_pkg_temp_thermal.c
drivers/thermal/x86_pkg_temp_thermal.c
+642
-0
未找到文件。
Documentation/thermal/x86_pkg_temperature_thermal
0 → 100644
浏览文件 @
f157f596
Kernel driver: x86_pkg_temp_thermal
===================
Supported chips:
* x86: with package level thermal management
(Verify using: CPUID.06H:EAX[bit 6] =1)
Authors: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Reference
---
Intel® 64 and IA-32 Architectures Software Developer’s Manual (Jan, 2013):
Chapter 14.6: PACKAGE LEVEL THERMAL MANAGEMENT
Description
---------
This driver register CPU digital temperature package level sensor as a thermal
zone with maximum two user mode configurable trip points. Number of trip points
depends on the capability of the package. Once the trip point is violated,
user mode can receive notification via thermal notification mechanism and can
take any action to control temperature.
Threshold management
--------------------
Each package will register as a thermal zone under /sys/class/thermal.
Example:
/sys/class/thermal/thermal_zone1
This contains two trip points:
- trip_point_0_temp
- trip_point_1_temp
User can set any temperature between 0 to TJ-Max temperature. Temperature units
are in milli-degree Celsius. Refer to "Documentation/thermal/sysfs-api.txt" for
thermal sys-fs details.
Any value other than 0 in these trip points, can trigger thermal notifications.
Setting 0, stops sending thermal notifications.
Thermal notifications: To get kobject-uevent notifications, set the thermal zone
policy to "user_space". For example: echo -n "user_space" > policy
arch/x86/include/asm/mce.h
浏览文件 @
f157f596
...
...
@@ -214,6 +214,13 @@ void mce_log_therm_throt_event(__u64 status);
/* Interrupt Handler for core thermal thresholds */
extern
int
(
*
platform_thermal_notify
)(
__u64
msr_val
);
/* Interrupt Handler for package thermal thresholds */
extern
int
(
*
platform_thermal_package_notify
)(
__u64
msr_val
);
/* Callback support of rate control, return true, if
* callback has rate control */
extern
bool
(
*
platform_thermal_package_rate_control
)(
void
);
#ifdef CONFIG_X86_THERMAL_VECTOR
extern
void
mcheck_intel_therm_init
(
void
);
#else
...
...
arch/x86/kernel/cpu/mcheck/therm_throt.c
浏览文件 @
f157f596
...
...
@@ -54,12 +54,24 @@ struct thermal_state {
struct
_thermal_state
package_power_limit
;
struct
_thermal_state
core_thresh0
;
struct
_thermal_state
core_thresh1
;
struct
_thermal_state
pkg_thresh0
;
struct
_thermal_state
pkg_thresh1
;
};
/* Callback to handle core threshold interrupts */
int
(
*
platform_thermal_notify
)(
__u64
msr_val
);
EXPORT_SYMBOL
(
platform_thermal_notify
);
/* Callback to handle core package threshold_interrupts */
int
(
*
platform_thermal_package_notify
)(
__u64
msr_val
);
EXPORT_SYMBOL_GPL
(
platform_thermal_package_notify
);
/* Callback support of rate control, return true, if
* callback has rate control */
bool
(
*
platform_thermal_package_rate_control
)(
void
);
EXPORT_SYMBOL_GPL
(
platform_thermal_package_rate_control
);
static
DEFINE_PER_CPU
(
struct
thermal_state
,
thermal_state
);
static
atomic_t
therm_throt_en
=
ATOMIC_INIT
(
0
);
...
...
@@ -203,19 +215,25 @@ static int therm_throt_process(bool new_event, int event, int level)
return
0
;
}
static
int
thresh_event_valid
(
int
event
)
static
int
thresh_event_valid
(
int
level
,
int
event
)
{
struct
_thermal_state
*
state
;
unsigned
int
this_cpu
=
smp_processor_id
();
struct
thermal_state
*
pstate
=
&
per_cpu
(
thermal_state
,
this_cpu
);
u64
now
=
get_jiffies_64
();
state
=
(
event
==
0
)
?
&
pstate
->
core_thresh0
:
&
pstate
->
core_thresh1
;
if
(
level
==
PACKAGE_LEVEL
)
state
=
(
event
==
0
)
?
&
pstate
->
pkg_thresh0
:
&
pstate
->
pkg_thresh1
;
else
state
=
(
event
==
0
)
?
&
pstate
->
core_thresh0
:
&
pstate
->
core_thresh1
;
if
(
time_before64
(
now
,
state
->
next_check
))
return
0
;
state
->
next_check
=
now
+
CHECK_INTERVAL
;
return
1
;
}
...
...
@@ -321,6 +339,39 @@ device_initcall(thermal_throttle_init_device);
#endif
/* CONFIG_SYSFS */
static
void
notify_package_thresholds
(
__u64
msr_val
)
{
bool
notify_thres_0
=
false
;
bool
notify_thres_1
=
false
;
if
(
!
platform_thermal_package_notify
)
return
;
/* lower threshold check */
if
(
msr_val
&
THERM_LOG_THRESHOLD0
)
notify_thres_0
=
true
;
/* higher threshold check */
if
(
msr_val
&
THERM_LOG_THRESHOLD1
)
notify_thres_1
=
true
;
if
(
!
notify_thres_0
&&
!
notify_thres_1
)
return
;
if
(
platform_thermal_package_rate_control
&&
platform_thermal_package_rate_control
())
{
/* Rate control is implemented in callback */
platform_thermal_package_notify
(
msr_val
);
return
;
}
/* lower threshold reached */
if
(
notify_thres_0
&&
thresh_event_valid
(
PACKAGE_LEVEL
,
0
))
platform_thermal_package_notify
(
msr_val
);
/* higher threshold reached */
if
(
notify_thres_1
&&
thresh_event_valid
(
PACKAGE_LEVEL
,
1
))
platform_thermal_package_notify
(
msr_val
);
}
static
void
notify_thresholds
(
__u64
msr_val
)
{
/* check whether the interrupt handler is defined;
...
...
@@ -330,10 +381,12 @@ static void notify_thresholds(__u64 msr_val)
return
;
/* lower threshold reached */
if
((
msr_val
&
THERM_LOG_THRESHOLD0
)
&&
thresh_event_valid
(
0
))
if
((
msr_val
&
THERM_LOG_THRESHOLD0
)
&&
thresh_event_valid
(
CORE_LEVEL
,
0
))
platform_thermal_notify
(
msr_val
);
/* higher threshold reached */
if
((
msr_val
&
THERM_LOG_THRESHOLD1
)
&&
thresh_event_valid
(
1
))
if
((
msr_val
&
THERM_LOG_THRESHOLD1
)
&&
thresh_event_valid
(
CORE_LEVEL
,
1
))
platform_thermal_notify
(
msr_val
);
}
...
...
@@ -359,6 +412,8 @@ static void intel_thermal_interrupt(void)
if
(
this_cpu_has
(
X86_FEATURE_PTS
))
{
rdmsrl
(
MSR_IA32_PACKAGE_THERM_STATUS
,
msr_val
);
/* check violations of package thermal thresholds */
notify_package_thresholds
(
msr_val
);
therm_throt_process
(
msr_val
&
PACKAGE_THERM_STATUS_PROCHOT
,
THERMAL_THROTTLING_EVENT
,
PACKAGE_LEVEL
);
...
...
drivers/thermal/Kconfig
浏览文件 @
f157f596
...
...
@@ -169,7 +169,20 @@ config INTEL_POWERCLAMP
enforce idle time which results in more package C-state residency. The
user interface is exposed via generic thermal framework.
config X86_PKG_TEMP_THERMAL
tristate "X86 package temperature thermal driver"
depends on THERMAL
depends on X86
select THERMAL_GOV_USER_SPACE
default m
help
Enable this to register CPU digital sensor for package temperature as
thermal zone. Each package will have its own thermal zone. There are
two trip points which can be set by user to get notifications via thermal
notification methods.
menu "Texas Instruments thermal drivers"
source "drivers/thermal/ti-soc-thermal/Kconfig"
endmenu
endif
drivers/thermal/Makefile
浏览文件 @
f157f596
...
...
@@ -23,4 +23,5 @@ obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
obj-$(CONFIG_ARMADA_THERMAL)
+=
armada_thermal.o
obj-$(CONFIG_DB8500_CPUFREQ_COOLING)
+=
db8500_cpufreq_cooling.o
obj-$(CONFIG_INTEL_POWERCLAMP)
+=
intel_powerclamp.o
obj-$(CONFIG_X86_PKG_TEMP_THERMAL)
+=
x86_pkg_temp_thermal.o
obj-$(CONFIG_TI_SOC_THERMAL)
+=
ti-soc-thermal/
drivers/thermal/x86_pkg_temp_thermal.c
0 → 100644
浏览文件 @
f157f596
/*
* x86_pkg_temp_thermal driver
* Copyright (c) 2013, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.
*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/param.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/cpu.h>
#include <linux/smp.h>
#include <linux/slab.h>
#include <linux/pm.h>
#include <linux/thermal.h>
#include <linux/debugfs.h>
#include <asm/cpu_device_id.h>
#include <asm/mce.h>
/*
* Rate control delay: Idea is to introduce denounce effect
* This should be long enough to avoid reduce events, when
* threshold is set to a temperature, which is constantly
* violated, but at the short enough to take any action.
* The action can be remove threshold or change it to next
* interesting setting. Based on experiments, in around
* every 5 seconds under load will give us a significant
* temperature change.
*/
#define PKG_TEMP_THERMAL_NOTIFY_DELAY 5000
static
int
notify_delay_ms
=
PKG_TEMP_THERMAL_NOTIFY_DELAY
;
module_param
(
notify_delay_ms
,
int
,
0644
);
MODULE_PARM_DESC
(
notify_delay_ms
,
"User space notification delay in milli seconds."
);
/* Number of trip points in thermal zone. Currently it can't
* be more than 2. MSR can allow setting and getting notifications
* for only 2 thresholds. This define enforces this, if there
* is some wrong values returned by cpuid for number of thresholds.
*/
#define MAX_NUMBER_OF_TRIPS 2
struct
phy_dev_entry
{
struct
list_head
list
;
u16
phys_proc_id
;
u16
first_cpu
;
u32
tj_max
;
int
ref_cnt
;
u32
start_pkg_therm_low
;
u32
start_pkg_therm_high
;
struct
thermal_zone_device
*
tzone
;
};
/* List maintaining number of package instances */
static
LIST_HEAD
(
phy_dev_list
);
static
DEFINE_MUTEX
(
phy_dev_list_mutex
);
/* Interrupt to work function schedule queue */
static
DEFINE_PER_CPU
(
struct
delayed_work
,
pkg_temp_thermal_threshold_work
);
/* To track if the work is already scheduled on a package */
static
u8
*
pkg_work_scheduled
;
/* Spin lock to prevent races with pkg_work_scheduled */
static
spinlock_t
pkg_work_lock
;
static
u16
max_phy_id
;
/* Debug counters to show using debugfs */
static
struct
dentry
*
debugfs
;
static
unsigned
int
pkg_interrupt_cnt
;
static
unsigned
int
pkg_work_cnt
;
static
int
pkg_temp_debugfs_init
(
void
)
{
struct
dentry
*
d
;
debugfs
=
debugfs_create_dir
(
"pkg_temp_thermal"
,
NULL
);
if
(
!
debugfs
)
return
-
ENOENT
;
d
=
debugfs_create_u32
(
"pkg_thres_interrupt"
,
S_IRUGO
,
debugfs
,
(
u32
*
)
&
pkg_interrupt_cnt
);
if
(
!
d
)
goto
err_out
;
d
=
debugfs_create_u32
(
"pkg_thres_work"
,
S_IRUGO
,
debugfs
,
(
u32
*
)
&
pkg_work_cnt
);
if
(
!
d
)
goto
err_out
;
return
0
;
err_out:
debugfs_remove_recursive
(
debugfs
);
return
-
ENOENT
;
}
static
struct
phy_dev_entry
*
pkg_temp_thermal_get_phy_entry
(
unsigned
int
cpu
)
{
u16
phys_proc_id
=
topology_physical_package_id
(
cpu
);
struct
phy_dev_entry
*
phy_ptr
;
mutex_lock
(
&
phy_dev_list_mutex
);
list_for_each_entry
(
phy_ptr
,
&
phy_dev_list
,
list
)
if
(
phy_ptr
->
phys_proc_id
==
phys_proc_id
)
{
mutex_unlock
(
&
phy_dev_list_mutex
);
return
phy_ptr
;
}
mutex_unlock
(
&
phy_dev_list_mutex
);
return
NULL
;
}
/*
* tj-max is is interesting because threshold is set relative to this
* temperature.
*/
static
int
get_tj_max
(
int
cpu
,
u32
*
tj_max
)
{
u32
eax
,
edx
;
u32
val
;
int
err
;
err
=
rdmsr_safe_on_cpu
(
cpu
,
MSR_IA32_TEMPERATURE_TARGET
,
&
eax
,
&
edx
);
if
(
err
)
goto
err_ret
;
else
{
val
=
(
eax
>>
16
)
&
0xff
;
if
(
val
)
*
tj_max
=
val
*
1000
;
else
{
err
=
-
EINVAL
;
goto
err_ret
;
}
}
return
0
;
err_ret:
*
tj_max
=
0
;
return
err
;
}
static
int
sys_get_curr_temp
(
struct
thermal_zone_device
*
tzd
,
unsigned
long
*
temp
)
{
u32
eax
,
edx
;
struct
phy_dev_entry
*
phy_dev_entry
;
phy_dev_entry
=
tzd
->
devdata
;
rdmsr_on_cpu
(
phy_dev_entry
->
first_cpu
,
MSR_IA32_PACKAGE_THERM_STATUS
,
&
eax
,
&
edx
);
if
(
eax
&
0x80000000
)
{
*
temp
=
phy_dev_entry
->
tj_max
-
((
eax
>>
16
)
&
0x7f
)
*
1000
;
pr_debug
(
"sys_get_curr_temp %ld
\n
"
,
*
temp
);
return
0
;
}
return
-
EINVAL
;
}
static
int
sys_get_trip_temp
(
struct
thermal_zone_device
*
tzd
,
int
trip
,
unsigned
long
*
temp
)
{
u32
eax
,
edx
;
struct
phy_dev_entry
*
phy_dev_entry
;
u32
mask
,
shift
;
unsigned
long
thres_reg_value
;
int
ret
;
if
(
trip
>=
MAX_NUMBER_OF_TRIPS
)
return
-
EINVAL
;
phy_dev_entry
=
tzd
->
devdata
;
if
(
trip
)
{
mask
=
THERM_MASK_THRESHOLD1
;
shift
=
THERM_SHIFT_THRESHOLD1
;
}
else
{
mask
=
THERM_MASK_THRESHOLD0
;
shift
=
THERM_SHIFT_THRESHOLD0
;
}
ret
=
rdmsr_on_cpu
(
phy_dev_entry
->
first_cpu
,
MSR_IA32_PACKAGE_THERM_INTERRUPT
,
&
eax
,
&
edx
);
if
(
ret
<
0
)
return
-
EINVAL
;
thres_reg_value
=
(
eax
&
mask
)
>>
shift
;
if
(
thres_reg_value
)
*
temp
=
phy_dev_entry
->
tj_max
-
thres_reg_value
*
1000
;
else
*
temp
=
0
;
pr_debug
(
"sys_get_trip_temp %ld
\n
"
,
*
temp
);
return
0
;
}
int
sys_set_trip_temp
(
struct
thermal_zone_device
*
tzd
,
int
trip
,
unsigned
long
temp
)
{
u32
l
,
h
;
struct
phy_dev_entry
*
phy_dev_entry
;
u32
mask
,
shift
,
intr
;
int
ret
;
phy_dev_entry
=
tzd
->
devdata
;
if
(
trip
>=
MAX_NUMBER_OF_TRIPS
||
temp
>=
phy_dev_entry
->
tj_max
)
return
-
EINVAL
;
ret
=
rdmsr_on_cpu
(
phy_dev_entry
->
first_cpu
,
MSR_IA32_PACKAGE_THERM_INTERRUPT
,
&
l
,
&
h
);
if
(
ret
<
0
)
return
-
EINVAL
;
if
(
trip
)
{
mask
=
THERM_MASK_THRESHOLD1
;
shift
=
THERM_SHIFT_THRESHOLD1
;
intr
=
THERM_INT_THRESHOLD1_ENABLE
;
}
else
{
mask
=
THERM_MASK_THRESHOLD0
;
shift
=
THERM_SHIFT_THRESHOLD0
;
intr
=
THERM_INT_THRESHOLD0_ENABLE
;
}
l
&=
~
mask
;
/*
* When users space sets a trip temperature == 0, which is indication
* that, it is no longer interested in receiving notifications.
*/
if
(
!
temp
)
l
&=
~
intr
;
else
{
l
|=
(
phy_dev_entry
->
tj_max
-
temp
)
/
1000
<<
shift
;
l
|=
intr
;
}
return
wrmsr_on_cpu
(
phy_dev_entry
->
first_cpu
,
MSR_IA32_PACKAGE_THERM_INTERRUPT
,
l
,
h
);
}
static
int
sys_get_trip_type
(
struct
thermal_zone_device
*
thermal
,
int
trip
,
enum
thermal_trip_type
*
type
)
{
*
type
=
THERMAL_TRIP_PASSIVE
;
return
0
;
}
/* Thermal zone callback registry */
static
struct
thermal_zone_device_ops
tzone_ops
=
{
.
get_temp
=
sys_get_curr_temp
,
.
get_trip_temp
=
sys_get_trip_temp
,
.
get_trip_type
=
sys_get_trip_type
,
.
set_trip_temp
=
sys_set_trip_temp
,
};
static
bool
pkg_temp_thermal_platform_thermal_rate_control
(
void
)
{
return
true
;
}
/* Enable threshold interrupt on local package/cpu */
static
inline
void
enable_pkg_thres_interrupt
(
void
)
{
u32
l
,
h
;
u8
thres_0
,
thres_1
;
rdmsr
(
MSR_IA32_PACKAGE_THERM_INTERRUPT
,
l
,
h
);
/* only enable/disable if it had valid threshold value */
thres_0
=
(
l
&
THERM_MASK_THRESHOLD0
)
>>
THERM_SHIFT_THRESHOLD0
;
thres_1
=
(
l
&
THERM_MASK_THRESHOLD1
)
>>
THERM_SHIFT_THRESHOLD1
;
if
(
thres_0
)
l
|=
THERM_INT_THRESHOLD0_ENABLE
;
if
(
thres_1
)
l
|=
THERM_INT_THRESHOLD1_ENABLE
;
wrmsr
(
MSR_IA32_PACKAGE_THERM_INTERRUPT
,
l
,
h
);
}
/* Disable threshold interrupt on local package/cpu */
static
inline
void
disable_pkg_thres_interrupt
(
void
)
{
u32
l
,
h
;
rdmsr
(
MSR_IA32_PACKAGE_THERM_INTERRUPT
,
l
,
h
);
wrmsr
(
MSR_IA32_PACKAGE_THERM_INTERRUPT
,
l
&
(
~
THERM_INT_THRESHOLD0_ENABLE
)
&
(
~
THERM_INT_THRESHOLD1_ENABLE
),
h
);
}
static
void
pkg_temp_thermal_threshold_work_fn
(
struct
work_struct
*
work
)
{
__u64
msr_val
;
int
cpu
=
smp_processor_id
();
int
phy_id
=
topology_physical_package_id
(
cpu
);
struct
phy_dev_entry
*
phdev
=
pkg_temp_thermal_get_phy_entry
(
cpu
);
bool
notify
=
false
;
if
(
!
phdev
)
return
;
spin_lock
(
&
pkg_work_lock
);
++
pkg_work_cnt
;
if
(
unlikely
(
phy_id
>
max_phy_id
))
{
spin_unlock
(
&
pkg_work_lock
);
return
;
}
pkg_work_scheduled
[
phy_id
]
=
0
;
spin_unlock
(
&
pkg_work_lock
);
enable_pkg_thres_interrupt
();
rdmsrl
(
MSR_IA32_PACKAGE_THERM_STATUS
,
msr_val
);
if
(
msr_val
&
THERM_LOG_THRESHOLD0
)
{
wrmsrl
(
MSR_IA32_PACKAGE_THERM_STATUS
,
msr_val
&
~
THERM_LOG_THRESHOLD0
);
notify
=
true
;
}
if
(
msr_val
&
THERM_LOG_THRESHOLD1
)
{
wrmsrl
(
MSR_IA32_PACKAGE_THERM_STATUS
,
msr_val
&
~
THERM_LOG_THRESHOLD1
);
notify
=
true
;
}
if
(
notify
)
{
pr_debug
(
"thermal_zone_device_update
\n
"
);
thermal_zone_device_update
(
phdev
->
tzone
);
}
}
static
int
pkg_temp_thermal_platform_thermal_notify
(
__u64
msr_val
)
{
unsigned
long
flags
;
int
cpu
=
smp_processor_id
();
int
phy_id
=
topology_physical_package_id
(
cpu
);
/*
* When a package is in interrupted state, all CPU's in that package
* are in the same interrupt state. So scheduling on any one CPU in
* the package is enough and simply return for others.
*/
spin_lock_irqsave
(
&
pkg_work_lock
,
flags
);
++
pkg_interrupt_cnt
;
if
(
unlikely
(
phy_id
>
max_phy_id
)
||
unlikely
(
!
pkg_work_scheduled
)
||
pkg_work_scheduled
[
phy_id
])
{
disable_pkg_thres_interrupt
();
spin_unlock_irqrestore
(
&
pkg_work_lock
,
flags
);
return
-
EINVAL
;
}
pkg_work_scheduled
[
phy_id
]
=
1
;
spin_unlock_irqrestore
(
&
pkg_work_lock
,
flags
);
disable_pkg_thres_interrupt
();
schedule_delayed_work_on
(
cpu
,
&
per_cpu
(
pkg_temp_thermal_threshold_work
,
cpu
),
msecs_to_jiffies
(
notify_delay_ms
));
return
0
;
}
static
int
find_siblings_cpu
(
int
cpu
)
{
int
i
;
int
id
=
topology_physical_package_id
(
cpu
);
for_each_online_cpu
(
i
)
if
(
i
!=
cpu
&&
topology_physical_package_id
(
i
)
==
id
)
return
i
;
return
0
;
}
static
int
pkg_temp_thermal_device_add
(
unsigned
int
cpu
)
{
int
err
;
u32
tj_max
;
struct
phy_dev_entry
*
phy_dev_entry
;
char
buffer
[
30
];
int
thres_count
;
u32
eax
,
ebx
,
ecx
,
edx
;
cpuid
(
6
,
&
eax
,
&
ebx
,
&
ecx
,
&
edx
);
thres_count
=
ebx
&
0x07
;
if
(
!
thres_count
)
return
-
ENODEV
;
thres_count
=
clamp_val
(
thres_count
,
0
,
MAX_NUMBER_OF_TRIPS
);
err
=
get_tj_max
(
cpu
,
&
tj_max
);
if
(
err
)
goto
err_ret
;
mutex_lock
(
&
phy_dev_list_mutex
);
phy_dev_entry
=
kzalloc
(
sizeof
(
*
phy_dev_entry
),
GFP_KERNEL
);
if
(
!
phy_dev_entry
)
{
err
=
-
ENOMEM
;
goto
err_ret_unlock
;
}
spin_lock
(
&
pkg_work_lock
);
if
(
topology_physical_package_id
(
cpu
)
>
max_phy_id
)
max_phy_id
=
topology_physical_package_id
(
cpu
);
pkg_work_scheduled
=
krealloc
(
pkg_work_scheduled
,
(
max_phy_id
+
1
)
*
sizeof
(
u8
),
GFP_ATOMIC
);
if
(
!
pkg_work_scheduled
)
{
spin_unlock
(
&
pkg_work_lock
);
err
=
-
ENOMEM
;
goto
err_ret_free
;
}
pkg_work_scheduled
[
topology_physical_package_id
(
cpu
)]
=
0
;
spin_unlock
(
&
pkg_work_lock
);
phy_dev_entry
->
phys_proc_id
=
topology_physical_package_id
(
cpu
);
phy_dev_entry
->
first_cpu
=
cpu
;
phy_dev_entry
->
tj_max
=
tj_max
;
phy_dev_entry
->
ref_cnt
=
1
;
snprintf
(
buffer
,
sizeof
(
buffer
),
"pkg-temp-%d
\n
"
,
phy_dev_entry
->
phys_proc_id
);
phy_dev_entry
->
tzone
=
thermal_zone_device_register
(
buffer
,
thres_count
,
(
thres_count
==
MAX_NUMBER_OF_TRIPS
)
?
0x03
:
0x01
,
phy_dev_entry
,
&
tzone_ops
,
NULL
,
0
,
0
);
if
(
IS_ERR
(
phy_dev_entry
->
tzone
))
{
err
=
PTR_ERR
(
phy_dev_entry
->
tzone
);
goto
err_ret_free
;
}
/* Store MSR value for package thermal interrupt, to restore at exit */
rdmsr_on_cpu
(
cpu
,
MSR_IA32_PACKAGE_THERM_INTERRUPT
,
&
phy_dev_entry
->
start_pkg_therm_low
,
&
phy_dev_entry
->
start_pkg_therm_high
);
list_add_tail
(
&
phy_dev_entry
->
list
,
&
phy_dev_list
);
pr_debug
(
"pkg_temp_thermal_device_add :phy_id %d cpu %d
\n
"
,
phy_dev_entry
->
phys_proc_id
,
cpu
);
mutex_unlock
(
&
phy_dev_list_mutex
);
return
0
;
err_ret_free:
kfree
(
phy_dev_entry
);
err_ret_unlock:
mutex_unlock
(
&
phy_dev_list_mutex
);
err_ret:
return
err
;
}
static
int
pkg_temp_thermal_device_remove
(
unsigned
int
cpu
)
{
struct
phy_dev_entry
*
n
;
u16
phys_proc_id
=
topology_physical_package_id
(
cpu
);
struct
phy_dev_entry
*
phdev
=
pkg_temp_thermal_get_phy_entry
(
cpu
);
if
(
!
phdev
)
return
-
ENODEV
;
mutex_lock
(
&
phy_dev_list_mutex
);
/* If we are loosing the first cpu for this package, we need change */
if
(
phdev
->
first_cpu
==
cpu
)
{
phdev
->
first_cpu
=
find_siblings_cpu
(
cpu
);
pr_debug
(
"thermal_device_remove: first cpu switched %d
\n
"
,
phdev
->
first_cpu
);
}
/*
* It is possible that no siblings left as this was the last cpu
* going offline. We don't need to worry about this assignment
* as the phydev entry will be removed in this case and
* thermal zone is removed.
*/
--
phdev
->
ref_cnt
;
pr_debug
(
"thermal_device_remove: pkg: %d cpu %d ref_cnt %d
\n
"
,
phys_proc_id
,
cpu
,
phdev
->
ref_cnt
);
if
(
!
phdev
->
ref_cnt
)
list_for_each_entry_safe
(
phdev
,
n
,
&
phy_dev_list
,
list
)
{
if
(
phdev
->
phys_proc_id
==
phys_proc_id
)
{
thermal_zone_device_unregister
(
phdev
->
tzone
);
list_del
(
&
phdev
->
list
);
kfree
(
phdev
);
break
;
}
}
mutex_unlock
(
&
phy_dev_list_mutex
);
return
0
;
}
static
int
get_core_online
(
unsigned
int
cpu
)
{
struct
cpuinfo_x86
*
c
=
&
cpu_data
(
cpu
);
struct
phy_dev_entry
*
phdev
=
pkg_temp_thermal_get_phy_entry
(
cpu
);
/* Check if there is already an instance for this package */
if
(
!
phdev
)
{
if
(
!
cpu_has
(
c
,
X86_FEATURE_DTHERM
)
&&
!
cpu_has
(
c
,
X86_FEATURE_PTS
))
return
-
ENODEV
;
if
(
pkg_temp_thermal_device_add
(
cpu
))
return
-
ENODEV
;
}
else
{
mutex_lock
(
&
phy_dev_list_mutex
);
++
phdev
->
ref_cnt
;
pr_debug
(
"get_core_online: cpu %d ref_cnt %d
\n
"
,
cpu
,
phdev
->
ref_cnt
);
mutex_unlock
(
&
phy_dev_list_mutex
);
}
INIT_DELAYED_WORK
(
&
per_cpu
(
pkg_temp_thermal_threshold_work
,
cpu
),
pkg_temp_thermal_threshold_work_fn
);
pr_debug
(
"get_core_online: cpu %d successful
\n
"
,
cpu
);
return
0
;
}
static
void
put_core_offline
(
unsigned
int
cpu
)
{
if
(
!
pkg_temp_thermal_device_remove
(
cpu
))
cancel_delayed_work_sync
(
&
per_cpu
(
pkg_temp_thermal_threshold_work
,
cpu
));
pr_debug
(
"put_core_offline: cpu %d
\n
"
,
cpu
);
}
static
int
pkg_temp_thermal_cpu_callback
(
struct
notifier_block
*
nfb
,
unsigned
long
action
,
void
*
hcpu
)
{
unsigned
int
cpu
=
(
unsigned
long
)
hcpu
;
switch
(
action
)
{
case
CPU_ONLINE
:
case
CPU_DOWN_FAILED
:
get_core_online
(
cpu
);
break
;
case
CPU_DOWN_PREPARE
:
put_core_offline
(
cpu
);
break
;
}
return
NOTIFY_OK
;
}
static
struct
notifier_block
pkg_temp_thermal_notifier
__refdata
=
{
.
notifier_call
=
pkg_temp_thermal_cpu_callback
,
};
static
const
struct
x86_cpu_id
__initconst
pkg_temp_thermal_ids
[]
=
{
{
X86_VENDOR_INTEL
,
X86_FAMILY_ANY
,
X86_MODEL_ANY
,
X86_FEATURE_DTHERM
},
{}
};
MODULE_DEVICE_TABLE
(
x86cpu
,
pkg_temp_thermal_ids
);
static
int
__init
pkg_temp_thermal_init
(
void
)
{
int
i
;
if
(
!
x86_match_cpu
(
pkg_temp_thermal_ids
))
return
-
ENODEV
;
spin_lock_init
(
&
pkg_work_lock
);
platform_thermal_package_notify
=
pkg_temp_thermal_platform_thermal_notify
;
platform_thermal_package_rate_control
=
pkg_temp_thermal_platform_thermal_rate_control
;
get_online_cpus
();
for_each_online_cpu
(
i
)
if
(
get_core_online
(
i
))
goto
err_ret
;
register_hotcpu_notifier
(
&
pkg_temp_thermal_notifier
);
put_online_cpus
();
pkg_temp_debugfs_init
();
/* Don't care if fails */
return
0
;
err_ret:
get_online_cpus
();
for_each_online_cpu
(
i
)
put_core_offline
(
i
);
put_online_cpus
();
kfree
(
pkg_work_scheduled
);
platform_thermal_package_notify
=
NULL
;
platform_thermal_package_rate_control
=
NULL
;
return
-
ENODEV
;
}
static
void
__exit
pkg_temp_thermal_exit
(
void
)
{
struct
phy_dev_entry
*
phdev
,
*
n
;
int
i
;
get_online_cpus
();
unregister_hotcpu_notifier
(
&
pkg_temp_thermal_notifier
);
mutex_lock
(
&
phy_dev_list_mutex
);
list_for_each_entry_safe
(
phdev
,
n
,
&
phy_dev_list
,
list
)
{
/* Retore old MSR value for package thermal interrupt */
wrmsr_on_cpu
(
phdev
->
first_cpu
,
MSR_IA32_PACKAGE_THERM_INTERRUPT
,
phdev
->
start_pkg_therm_low
,
phdev
->
start_pkg_therm_high
);
thermal_zone_device_unregister
(
phdev
->
tzone
);
list_del
(
&
phdev
->
list
);
kfree
(
phdev
);
}
mutex_unlock
(
&
phy_dev_list_mutex
);
platform_thermal_package_notify
=
NULL
;
platform_thermal_package_rate_control
=
NULL
;
for_each_online_cpu
(
i
)
cancel_delayed_work_sync
(
&
per_cpu
(
pkg_temp_thermal_threshold_work
,
i
));
put_online_cpus
();
kfree
(
pkg_work_scheduled
);
debugfs_remove_recursive
(
debugfs
);
}
module_init
(
pkg_temp_thermal_init
)
module_exit
(
pkg_temp_thermal_exit
)
MODULE_DESCRIPTION
(
"X86 PKG TEMP Thermal Driver"
);
MODULE_AUTHOR
(
"Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"
);
MODULE_LICENSE
(
"GPL v2"
);
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录