Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openeuler
Kernel
提交
28ad4b4e
K
Kernel
项目概览
openeuler
/
Kernel
2 年多 前同步成功
通知
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看板
提交
28ad4b4e
编写于
6月 30, 2019
作者:
R
Rafael J. Wysocki
浏览文件
操作
浏览文件
下载
差异文件
Merge back PCI power management material for v5.3.
上级
471a739a
b51033e0
变更
7
隐藏空白更改
内联
并排
Showing
7 changed file
with
333 addition
and
41 deletion
+333
-41
drivers/acpi/power.c
drivers/acpi/power.c
+135
-0
drivers/pci/pci-acpi.c
drivers/pci/pci-acpi.c
+13
-1
drivers/pci/pci-driver.c
drivers/pci/pci-driver.c
+26
-5
drivers/pci/pci.c
drivers/pci/pci.c
+82
-34
drivers/pci/pci.h
drivers/pci/pci.h
+7
-1
drivers/pci/pcie/portdrv_core.c
drivers/pci/pcie/portdrv_core.c
+66
-0
include/acpi/acpi_bus.h
include/acpi/acpi_bus.h
+4
-0
未找到文件。
drivers/acpi/power.c
浏览文件 @
28ad4b4e
...
...
@@ -42,6 +42,11 @@ ACPI_MODULE_NAME("power");
#define ACPI_POWER_RESOURCE_STATE_ON 0x01
#define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF
struct
acpi_power_dependent_device
{
struct
device
*
dev
;
struct
list_head
node
;
};
struct
acpi_power_resource
{
struct
acpi_device
device
;
struct
list_head
list_node
;
...
...
@@ -51,6 +56,7 @@ struct acpi_power_resource {
unsigned
int
ref_count
;
bool
wakeup_enabled
;
struct
mutex
resource_lock
;
struct
list_head
dependents
;
};
struct
acpi_power_resource_entry
{
...
...
@@ -232,8 +238,121 @@ static int acpi_power_get_list_state(struct list_head *list, int *state)
return
0
;
}
static
int
acpi_power_resource_add_dependent
(
struct
acpi_power_resource
*
resource
,
struct
device
*
dev
)
{
struct
acpi_power_dependent_device
*
dep
;
int
ret
=
0
;
mutex_lock
(
&
resource
->
resource_lock
);
list_for_each_entry
(
dep
,
&
resource
->
dependents
,
node
)
{
/* Only add it once */
if
(
dep
->
dev
==
dev
)
goto
unlock
;
}
dep
=
kzalloc
(
sizeof
(
*
dep
),
GFP_KERNEL
);
if
(
!
dep
)
{
ret
=
-
ENOMEM
;
goto
unlock
;
}
dep
->
dev
=
dev
;
list_add_tail
(
&
dep
->
node
,
&
resource
->
dependents
);
dev_dbg
(
dev
,
"added power dependency to [%s]
\n
"
,
resource
->
name
);
unlock:
mutex_unlock
(
&
resource
->
resource_lock
);
return
ret
;
}
static
void
acpi_power_resource_remove_dependent
(
struct
acpi_power_resource
*
resource
,
struct
device
*
dev
)
{
struct
acpi_power_dependent_device
*
dep
;
mutex_lock
(
&
resource
->
resource_lock
);
list_for_each_entry
(
dep
,
&
resource
->
dependents
,
node
)
{
if
(
dep
->
dev
==
dev
)
{
list_del
(
&
dep
->
node
);
kfree
(
dep
);
dev_dbg
(
dev
,
"removed power dependency to [%s]
\n
"
,
resource
->
name
);
break
;
}
}
mutex_unlock
(
&
resource
->
resource_lock
);
}
/**
* acpi_device_power_add_dependent - Add dependent device of this ACPI device
* @adev: ACPI device pointer
* @dev: Dependent device
*
* If @adev has non-empty _PR0 the @dev is added as dependent device to all
* power resources returned by it. This means that whenever these power
* resources are turned _ON the dependent devices get runtime resumed. This
* is needed for devices such as PCI to allow its driver to re-initialize
* it after it went to D0uninitialized.
*
* If @adev does not have _PR0 this does nothing.
*
* Returns %0 in case of success and negative errno otherwise.
*/
int
acpi_device_power_add_dependent
(
struct
acpi_device
*
adev
,
struct
device
*
dev
)
{
struct
acpi_power_resource_entry
*
entry
;
struct
list_head
*
resources
;
int
ret
;
if
(
!
adev
->
flags
.
power_manageable
)
return
0
;
resources
=
&
adev
->
power
.
states
[
ACPI_STATE_D0
].
resources
;
list_for_each_entry
(
entry
,
resources
,
node
)
{
ret
=
acpi_power_resource_add_dependent
(
entry
->
resource
,
dev
);
if
(
ret
)
goto
err
;
}
return
0
;
err:
list_for_each_entry
(
entry
,
resources
,
node
)
acpi_power_resource_remove_dependent
(
entry
->
resource
,
dev
);
return
ret
;
}
/**
* acpi_device_power_remove_dependent - Remove dependent device
* @adev: ACPI device pointer
* @dev: Dependent device
*
* Does the opposite of acpi_device_power_add_dependent() and removes the
* dependent device if it is found. Can be called to @adev that does not
* have _PR0 as well.
*/
void
acpi_device_power_remove_dependent
(
struct
acpi_device
*
adev
,
struct
device
*
dev
)
{
struct
acpi_power_resource_entry
*
entry
;
struct
list_head
*
resources
;
if
(
!
adev
->
flags
.
power_manageable
)
return
;
resources
=
&
adev
->
power
.
states
[
ACPI_STATE_D0
].
resources
;
list_for_each_entry_reverse
(
entry
,
resources
,
node
)
acpi_power_resource_remove_dependent
(
entry
->
resource
,
dev
);
}
static
int
__acpi_power_on
(
struct
acpi_power_resource
*
resource
)
{
struct
acpi_power_dependent_device
*
dep
;
acpi_status
status
=
AE_OK
;
status
=
acpi_evaluate_object
(
resource
->
device
.
handle
,
"_ON"
,
NULL
,
NULL
);
...
...
@@ -243,6 +362,21 @@ static int __acpi_power_on(struct acpi_power_resource *resource)
ACPI_DEBUG_PRINT
((
ACPI_DB_INFO
,
"Power resource [%s] turned on
\n
"
,
resource
->
name
));
/*
* If there are other dependents on this power resource we need to
* resume them now so that their drivers can re-initialize the
* hardware properly after it went back to D0.
*/
if
(
list_empty
(
&
resource
->
dependents
)
||
list_is_singular
(
&
resource
->
dependents
))
return
0
;
list_for_each_entry
(
dep
,
&
resource
->
dependents
,
node
)
{
dev_dbg
(
dep
->
dev
,
"runtime resuming because [%s] turned on
\n
"
,
resource
->
name
);
pm_request_resume
(
dep
->
dev
);
}
return
0
;
}
...
...
@@ -810,6 +944,7 @@ int acpi_add_power_resource(acpi_handle handle)
ACPI_STA_DEFAULT
);
mutex_init
(
&
resource
->
resource_lock
);
INIT_LIST_HEAD
(
&
resource
->
list_node
);
INIT_LIST_HEAD
(
&
resource
->
dependents
);
resource
->
name
=
device
->
pnp
.
bus_id
;
strcpy
(
acpi_device_name
(
device
),
ACPI_POWER_DEVICE_NAME
);
strcpy
(
acpi_device_class
(
device
),
ACPI_POWER_CLASS
);
...
...
drivers/pci/pci-acpi.c
浏览文件 @
28ad4b4e
...
...
@@ -685,12 +685,21 @@ static pci_power_t acpi_pci_get_power_state(struct pci_dev *dev)
if
(
!
adev
||
!
acpi_device_power_manageable
(
adev
))
return
PCI_UNKNOWN
;
if
(
acpi_device_get_power
(
adev
,
&
state
)
||
state
==
ACPI_STATE_UNKNOWN
)
state
=
adev
->
power
.
state
;
if
(
state
==
ACPI_STATE_UNKNOWN
)
return
PCI_UNKNOWN
;
return
state_conv
[
state
];
}
static
void
acpi_pci_refresh_power_state
(
struct
pci_dev
*
dev
)
{
struct
acpi_device
*
adev
=
ACPI_COMPANION
(
&
dev
->
dev
);
if
(
adev
&&
acpi_device_power_manageable
(
adev
))
acpi_device_update_power
(
adev
,
NULL
);
}
static
int
acpi_pci_propagate_wakeup
(
struct
pci_bus
*
bus
,
bool
enable
)
{
while
(
bus
->
parent
)
{
...
...
@@ -748,6 +757,7 @@ static const struct pci_platform_pm_ops acpi_pci_platform_pm = {
.
is_manageable
=
acpi_pci_power_manageable
,
.
set_state
=
acpi_pci_set_power_state
,
.
get_state
=
acpi_pci_get_power_state
,
.
refresh_state
=
acpi_pci_refresh_power_state
,
.
choose_state
=
acpi_pci_choose_state
,
.
set_wakeup
=
acpi_pci_wakeup
,
.
need_resume
=
acpi_pci_need_resume
,
...
...
@@ -901,6 +911,7 @@ static void pci_acpi_setup(struct device *dev)
device_wakeup_enable
(
dev
);
acpi_pci_wakeup
(
pci_dev
,
false
);
acpi_device_power_add_dependent
(
adev
,
dev
);
}
static
void
pci_acpi_cleanup
(
struct
device
*
dev
)
...
...
@@ -913,6 +924,7 @@ static void pci_acpi_cleanup(struct device *dev)
pci_acpi_remove_pm_notifier
(
adev
);
if
(
adev
->
wakeup
.
flags
.
valid
)
{
acpi_device_power_remove_dependent
(
adev
,
dev
);
if
(
pci_dev
->
bridge_d3
)
device_wakeup_disable
(
dev
);
...
...
drivers/pci/pci-driver.c
浏览文件 @
28ad4b4e
...
...
@@ -678,6 +678,7 @@ static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev)
static
int
pci_pm_prepare
(
struct
device
*
dev
)
{
struct
device_driver
*
drv
=
dev
->
driver
;
struct
pci_dev
*
pci_dev
=
to_pci_dev
(
dev
);
if
(
drv
&&
drv
->
pm
&&
drv
->
pm
->
prepare
)
{
int
error
=
drv
->
pm
->
prepare
(
dev
);
...
...
@@ -687,7 +688,15 @@ static int pci_pm_prepare(struct device *dev)
if
(
!
error
&&
dev_pm_test_driver_flags
(
dev
,
DPM_FLAG_SMART_PREPARE
))
return
0
;
}
return
pci_dev_keep_suspended
(
to_pci_dev
(
dev
));
if
(
pci_dev_need_resume
(
pci_dev
))
return
0
;
/*
* The PME setting needs to be adjusted here in case the direct-complete
* optimization is used with respect to this device.
*/
pci_dev_adjust_pme
(
pci_dev
);
return
1
;
}
static
void
pci_pm_complete
(
struct
device
*
dev
)
...
...
@@ -701,7 +710,14 @@ static void pci_pm_complete(struct device *dev)
if
(
pm_runtime_suspended
(
dev
)
&&
pm_resume_via_firmware
())
{
pci_power_t
pre_sleep_state
=
pci_dev
->
current_state
;
pci_update_current_state
(
pci_dev
,
pci_dev
->
current_state
);
pci_refresh_power_state
(
pci_dev
);
/*
* On platforms with ACPI this check may also trigger for
* devices sharing power resources if one of those power
* resources has been activated as a result of a change of the
* power state of another device sharing it. However, in that
* case it is also better to resume the device, in general.
*/
if
(
pci_dev
->
current_state
<
pre_sleep_state
)
pm_request_resume
(
dev
);
}
...
...
@@ -757,9 +773,11 @@ static int pci_pm_suspend(struct device *dev)
* better to resume the device from runtime suspend here.
*/
if
(
!
dev_pm_test_driver_flags
(
dev
,
DPM_FLAG_SMART_SUSPEND
)
||
!
pci_dev_keep_suspended
(
pci_dev
))
{
pci_dev_need_resume
(
pci_dev
))
{
pm_runtime_resume
(
dev
);
pci_dev
->
state_saved
=
false
;
}
else
{
pci_dev_adjust_pme
(
pci_dev
);
}
if
(
pm
->
suspend
)
{
...
...
@@ -1130,10 +1148,13 @@ static int pci_pm_poweroff(struct device *dev)
/* The reason to do that is the same as in pci_pm_suspend(). */
if
(
!
dev_pm_test_driver_flags
(
dev
,
DPM_FLAG_SMART_SUSPEND
)
||
!
pci_dev_keep_suspended
(
pci_dev
))
pci_dev_need_resume
(
pci_dev
))
{
pm_runtime_resume
(
dev
);
pci_dev
->
state_saved
=
false
;
}
else
{
pci_dev_adjust_pme
(
pci_dev
);
}
pci_dev
->
state_saved
=
false
;
if
(
pm
->
poweroff
)
{
int
error
;
...
...
drivers/pci/pci.c
浏览文件 @
28ad4b4e
...
...
@@ -777,6 +777,12 @@ static inline pci_power_t platform_pci_get_power_state(struct pci_dev *dev)
return
pci_platform_pm
?
pci_platform_pm
->
get_state
(
dev
)
:
PCI_UNKNOWN
;
}
static
inline
void
platform_pci_refresh_power_state
(
struct
pci_dev
*
dev
)
{
if
(
pci_platform_pm
&&
pci_platform_pm
->
refresh_state
)
pci_platform_pm
->
refresh_state
(
dev
);
}
static
inline
pci_power_t
platform_pci_choose_state
(
struct
pci_dev
*
dev
)
{
return
pci_platform_pm
?
...
...
@@ -937,6 +943,21 @@ void pci_update_current_state(struct pci_dev *dev, pci_power_t state)
}
}
/**
* pci_refresh_power_state - Refresh the given device's power state data
* @dev: Target PCI device.
*
* Ask the platform to refresh the devices power state information and invoke
* pci_update_current_state() to update its current PCI power state.
*/
void
pci_refresh_power_state
(
struct
pci_dev
*
dev
)
{
if
(
platform_pci_power_manageable
(
dev
))
platform_pci_refresh_power_state
(
dev
);
pci_update_current_state
(
dev
,
dev
->
current_state
);
}
/**
* pci_power_up - Put the given device into D0 forcibly
* @dev: PCI device to power up
...
...
@@ -1004,15 +1025,10 @@ static void __pci_start_power_transition(struct pci_dev *dev, pci_power_t state)
if
(
state
==
PCI_D0
)
{
pci_platform_power_transition
(
dev
,
PCI_D0
);
/*
* Mandatory power management transition delays, see
* PCI Express Base Specification Revision 2.0 Section
* 6.6.1: Conventional Reset. Do not delay for
* devices powered on/off by corresponding bridge,
* because have already delayed for the bridge.
* Mandatory power management transition delays are
* handled in the PCIe portdrv resume hooks.
*/
if
(
dev
->
runtime_d3cold
)
{
if
(
dev
->
d3cold_delay
&&
!
dev
->
imm_ready
)
msleep
(
dev
->
d3cold_delay
);
/*
* When powering on a bridge from D3cold, the
* whole hierarchy may be powered on into
...
...
@@ -2065,6 +2081,13 @@ static void pci_pme_list_scan(struct work_struct *work)
*/
if
(
bridge
&&
bridge
->
current_state
!=
PCI_D0
)
continue
;
/*
* If the device is in D3cold it should not be
* polled either.
*/
if
(
pme_dev
->
dev
->
current_state
==
PCI_D3cold
)
continue
;
pci_pme_wakeup
(
pme_dev
->
dev
,
NULL
);
}
else
{
list_del
(
&
pme_dev
->
list
);
...
...
@@ -2459,45 +2482,56 @@ bool pci_dev_run_wake(struct pci_dev *dev)
EXPORT_SYMBOL_GPL
(
pci_dev_run_wake
);
/**
* pci_dev_
keep_suspended - Check if the device can stay in the suspended stat
e.
* pci_dev_
need_resume - Check if it is necessary to resume the devic
e.
* @pci_dev: Device to check.
*
* Return 'true' if the device is
runtime-suspended, it doesn't have
to be
* Return 'true' if the device is
not runtime-suspended or it has
to be
* reconfigured due to wakeup settings difference between system and runtime
* suspend and the current power state of it is suitable for the upcoming
* (system) transition.
*
* If the device is not configured for system wakeup, disable PME for it before
* returning 'true' to prevent it from waking up the system unnecessarily.
* suspend, or the current power state of it is not suitable for the upcoming
* (system-wide) transition.
*/
bool
pci_dev_
keep_suspended
(
struct
pci_dev
*
pci_dev
)
bool
pci_dev_
need_resume
(
struct
pci_dev
*
pci_dev
)
{
struct
device
*
dev
=
&
pci_dev
->
dev
;
bool
wakeup
=
device_may_wakeup
(
dev
)
;
pci_power_t
target_state
;
if
(
!
pm_runtime_suspended
(
dev
)
||
pci_target_state
(
pci_dev
,
wakeup
)
!=
pci_dev
->
current_state
||
platform_pci_need_resume
(
pci_dev
))
return
false
;
if
(
!
pm_runtime_suspended
(
dev
)
||
platform_pci_need_resume
(
pci_dev
))
return
true
;
target_state
=
pci_target_state
(
pci_dev
,
device_may_wakeup
(
dev
))
;
/*
* At this point the device is good to go unless it's been configured
* to generate PME at the runtime suspend time, but it is not supposed
* to wake up the system. In that case, simply disable PME for it
* (it will have to be re-enabled on exit from system resume).
*
* If the device's power state is D3cold and the platform check above
* hasn't triggered, the device's configuration is suitable and we don't
* need to manipulate it at all.
* If the earlier platform check has not triggered, D3cold is just power
* removal on top of D3hot, so no need to resume the device in that
* case.
*/
return
target_state
!=
pci_dev
->
current_state
&&
target_state
!=
PCI_D3cold
&&
pci_dev
->
current_state
!=
PCI_D3hot
;
}
/**
* pci_dev_adjust_pme - Adjust PME setting for a suspended device.
* @pci_dev: Device to check.
*
* If the device is suspended and it is not configured for system wakeup,
* disable PME for it to prevent it from waking up the system unnecessarily.
*
* Note that if the device's power state is D3cold and the platform check in
* pci_dev_need_resume() has not triggered, the device's configuration need not
* be changed.
*/
void
pci_dev_adjust_pme
(
struct
pci_dev
*
pci_dev
)
{
struct
device
*
dev
=
&
pci_dev
->
dev
;
spin_lock_irq
(
&
dev
->
power
.
lock
);
if
(
pm_runtime_suspended
(
dev
)
&&
pci_dev
->
current_state
<
PCI_D3cold
&&
!
wakeup
)
if
(
pm_runtime_suspended
(
dev
)
&&
!
device_may_wakeup
(
dev
)
&&
pci_dev
->
current_state
<
PCI_D3cold
)
__pci_pme_active
(
pci_dev
,
false
);
spin_unlock_irq
(
&
dev
->
power
.
lock
);
return
true
;
}
/**
...
...
@@ -4568,14 +4602,16 @@ static int pci_pm_reset(struct pci_dev *dev, int probe)
return
pci_dev_wait
(
dev
,
"PM D3->D0"
,
PCIE_RESET_READY_POLL_MS
);
}
/**
* pcie_wait_for_link - Wait until link is active or inactive
* pcie_wait_for_link
_delay
- Wait until link is active or inactive
* @pdev: Bridge device
* @active: waiting for active or inactive?
* @delay: Delay to wait after link has become active (in ms)
*
* Use this to wait till link becomes active or inactive.
*/
bool
pcie_wait_for_link
(
struct
pci_dev
*
pdev
,
bool
active
)
bool
pcie_wait_for_link
_delay
(
struct
pci_dev
*
pdev
,
bool
active
,
int
delay
)
{
int
timeout
=
1000
;
bool
ret
;
...
...
@@ -4612,13 +4648,25 @@ bool pcie_wait_for_link(struct pci_dev *pdev, bool active)
timeout
-=
10
;
}
if
(
active
&&
ret
)
msleep
(
100
);
msleep
(
delay
);
else
if
(
ret
!=
active
)
pci_info
(
pdev
,
"Data Link Layer Link Active not %s in 1000 msec
\n
"
,
active
?
"set"
:
"cleared"
);
return
ret
==
active
;
}
/**
* pcie_wait_for_link - Wait until link is active or inactive
* @pdev: Bridge device
* @active: waiting for active or inactive?
*
* Use this to wait till link becomes active or inactive.
*/
bool
pcie_wait_for_link
(
struct
pci_dev
*
pdev
,
bool
active
)
{
return
pcie_wait_for_link_delay
(
pdev
,
active
,
100
);
}
void
pci_reset_secondary_bus
(
struct
pci_dev
*
dev
)
{
u16
ctrl
;
...
...
drivers/pci/pci.h
浏览文件 @
28ad4b4e
...
...
@@ -51,6 +51,8 @@ int pci_bus_error_reset(struct pci_dev *dev);
*
* @get_state: queries the platform firmware for a device's current power state
*
* @refresh_state: asks the platform to refresh the device's power state data
*
* @choose_state: returns PCI power state of given device preferred by the
* platform; to be used during system-wide transitions from a
* sleeping state to the working state and vice versa
...
...
@@ -69,6 +71,7 @@ struct pci_platform_pm_ops {
bool
(
*
is_manageable
)(
struct
pci_dev
*
dev
);
int
(
*
set_state
)(
struct
pci_dev
*
dev
,
pci_power_t
state
);
pci_power_t
(
*
get_state
)(
struct
pci_dev
*
dev
);
void
(
*
refresh_state
)(
struct
pci_dev
*
dev
);
pci_power_t
(
*
choose_state
)(
struct
pci_dev
*
dev
);
int
(
*
set_wakeup
)(
struct
pci_dev
*
dev
,
bool
enable
);
bool
(
*
need_resume
)(
struct
pci_dev
*
dev
);
...
...
@@ -76,13 +79,15 @@ struct pci_platform_pm_ops {
int
pci_set_platform_pm
(
const
struct
pci_platform_pm_ops
*
ops
);
void
pci_update_current_state
(
struct
pci_dev
*
dev
,
pci_power_t
state
);
void
pci_refresh_power_state
(
struct
pci_dev
*
dev
);
void
pci_power_up
(
struct
pci_dev
*
dev
);
void
pci_disable_enabled_device
(
struct
pci_dev
*
dev
);
int
pci_finish_runtime_suspend
(
struct
pci_dev
*
dev
);
void
pcie_clear_root_pme_status
(
struct
pci_dev
*
dev
);
int
__pci_pme_wakeup
(
struct
pci_dev
*
dev
,
void
*
ign
);
void
pci_pme_restore
(
struct
pci_dev
*
dev
);
bool
pci_dev_keep_suspended
(
struct
pci_dev
*
dev
);
bool
pci_dev_need_resume
(
struct
pci_dev
*
dev
);
void
pci_dev_adjust_pme
(
struct
pci_dev
*
dev
);
void
pci_dev_complete_resume
(
struct
pci_dev
*
pci_dev
);
void
pci_config_pm_runtime_get
(
struct
pci_dev
*
dev
);
void
pci_config_pm_runtime_put
(
struct
pci_dev
*
dev
);
...
...
@@ -493,6 +498,7 @@ static inline int pci_dev_specific_disable_acs_redir(struct pci_dev *dev)
void
pcie_do_recovery
(
struct
pci_dev
*
dev
,
enum
pci_channel_state
state
,
u32
service
);
bool
pcie_wait_for_link_delay
(
struct
pci_dev
*
pdev
,
bool
active
,
int
delay
);
bool
pcie_wait_for_link
(
struct
pci_dev
*
pdev
,
bool
active
);
#ifdef CONFIG_PCIEASPM
void
pcie_aspm_init_link_state
(
struct
pci_dev
*
pdev
);
...
...
drivers/pci/pcie/portdrv_core.c
浏览文件 @
28ad4b4e
...
...
@@ -9,6 +9,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
...
...
@@ -378,6 +379,67 @@ static int pm_iter(struct device *dev, void *data)
return
0
;
}
static
int
get_downstream_delay
(
struct
pci_bus
*
bus
)
{
struct
pci_dev
*
pdev
;
int
min_delay
=
100
;
int
max_delay
=
0
;
list_for_each_entry
(
pdev
,
&
bus
->
devices
,
bus_list
)
{
if
(
!
pdev
->
imm_ready
)
min_delay
=
0
;
else
if
(
pdev
->
d3cold_delay
<
min_delay
)
min_delay
=
pdev
->
d3cold_delay
;
if
(
pdev
->
d3cold_delay
>
max_delay
)
max_delay
=
pdev
->
d3cold_delay
;
}
return
max
(
min_delay
,
max_delay
);
}
/*
* wait_for_downstream_link - Wait for downstream link to establish
* @pdev: PCIe port whose downstream link is waited
*
* Handle delays according to PCIe 4.0 section 6.6.1 before configuration
* access to the downstream component is permitted.
*
* This blocks PCI core resume of the hierarchy below this port until the
* link is trained. Should be called before resuming port services to
* prevent pciehp from starting to tear-down the hierarchy too soon.
*/
static
void
wait_for_downstream_link
(
struct
pci_dev
*
pdev
)
{
int
delay
;
if
(
pci_pcie_type
(
pdev
)
!=
PCI_EXP_TYPE_ROOT_PORT
&&
pci_pcie_type
(
pdev
)
!=
PCI_EXP_TYPE_DOWNSTREAM
)
return
;
if
(
pci_dev_is_disconnected
(
pdev
))
return
;
if
(
!
pdev
->
subordinate
||
list_empty
(
&
pdev
->
subordinate
->
devices
)
||
!
pdev
->
bridge_d3
)
return
;
delay
=
get_downstream_delay
(
pdev
->
subordinate
);
if
(
!
delay
)
return
;
dev_dbg
(
&
pdev
->
dev
,
"waiting downstream link for %d ms
\n
"
,
delay
);
/*
* If downstream port does not support speeds greater than 5 GT/s
* need to wait 100ms. For higher speeds (gen3) we need to wait
* first for the data link layer to become active.
*/
if
(
pcie_get_speed_cap
(
pdev
)
<=
PCIE_SPEED_5_0GT
)
msleep
(
delay
);
else
pcie_wait_for_link_delay
(
pdev
,
true
,
delay
);
}
/**
* pcie_port_device_suspend - suspend port services associated with a PCIe port
* @dev: PCI Express port to handle
...
...
@@ -391,6 +453,8 @@ int pcie_port_device_suspend(struct device *dev)
int
pcie_port_device_resume_noirq
(
struct
device
*
dev
)
{
size_t
off
=
offsetof
(
struct
pcie_port_service_driver
,
resume_noirq
);
wait_for_downstream_link
(
to_pci_dev
(
dev
));
return
device_for_each_child
(
dev
,
&
off
,
pm_iter
);
}
...
...
@@ -421,6 +485,8 @@ int pcie_port_device_runtime_suspend(struct device *dev)
int
pcie_port_device_runtime_resume
(
struct
device
*
dev
)
{
size_t
off
=
offsetof
(
struct
pcie_port_service_driver
,
runtime_resume
);
wait_for_downstream_link
(
to_pci_dev
(
dev
));
return
device_for_each_child
(
dev
,
&
off
,
pm_iter
);
}
#endif
/* PM */
...
...
include/acpi/acpi_bus.h
浏览文件 @
28ad4b4e
...
...
@@ -513,6 +513,10 @@ int acpi_device_fix_up_power(struct acpi_device *device);
int
acpi_bus_update_power
(
acpi_handle
handle
,
int
*
state_p
);
int
acpi_device_update_power
(
struct
acpi_device
*
device
,
int
*
state_p
);
bool
acpi_bus_power_manageable
(
acpi_handle
handle
);
int
acpi_device_power_add_dependent
(
struct
acpi_device
*
adev
,
struct
device
*
dev
);
void
acpi_device_power_remove_dependent
(
struct
acpi_device
*
adev
,
struct
device
*
dev
);
#ifdef CONFIG_PM
bool
acpi_bus_can_wakeup
(
acpi_handle
handle
);
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录