PCI-E專欄:PCI Express省電模式剖析 行動平台決戰電池續航力
新電子 2006 年 1 月號 238 期
文.Steve Kolokowsky/Trevor Davis
桌上型與筆記型電腦的演進永無止境,事實上,在討論筆記型電腦或桌上型電腦時,總是會出現 「更快」、「更輕巧」、或 「功能更強」等字眼。我們總是盤算下次何時為系統進行升級,希望在最快的電腦上讓應用程式發揮更大的效能...
桌上型與筆記型電腦的演進永無止境,事實上,在討論筆記型電腦或桌上型電腦時,總是會出現 「更快」、「更輕巧」、或「功能更強」等字眼。我們總是盤算下次何時為系統進行升級,希望在最快的電腦上讓應用程式發揮更大的效能。本文將討論PCI Express新開發的省電模式,此項優勢將協助PCI Express在行動平台方面的普及。
在先前的技術專文中闡述PCI Express存在的重要性,並指出此傳輸技術將全面改變桌上型與筆記型電腦的運作模式,然而有一個尚未討論的問題—那就是PCI Express技術的功耗問題。
行動運算有四大考量因素:記憶體、重量、儲存容量,以及電池續航力。記憶體通常是以Mb或Gb為計量單位,重量則是以盎司計算,儲存容量則以Gb為單位,而電池續航力則是以個人使用的狂熱程度來衡量。這之間的差別在於電池續航力的量測是很主觀的,因為每位使用者不同的使用方式都會影響電持續航力的測量結果。其他項目的量測都能做到相當客觀,例如80Gb硬碟機絕對就只有80Gb的容量;而它的重量是6.5磅,那就是6.5磅,一點也不會少算,但談到電池續航力,就有眾多量測的方法。
事實上,目前有許多不同的「業界標準」方法來測試電池續航力,圖1顯示戴爾電腦在量測搭載Intel Pentium III處理器的Dell Latitude筆記型電腦電池續航力時,其所產生的量測圖表。
圖1顯示戴爾電腦對其產品進行許多標準測試。現今數項主流電池續航力效能量測指標包括BWS BatteryMark、Winstone 2004 Battery-Mark及MobileMark 2002。在日本業界有其電池續航力檢測標準,也就是日本電子與資訊科技產業協會(JEITA)所制定的標準。想像一下在全球眾多廠商致力於延長電池續航力之際,PCI Express方面所產生的變化將會對他們的計畫產生怎樣的影響?
現今普遍致力於如何將筆記型電腦的尺寸變小,再加上大家對於電池續航力的見解與看法大相逕庭的情況之下,導致科技的運用必須著重於系統開發的焦點-電池續航力。設計業者已事先預知此種市場需求,因此當他們在開發PCI Express時就將這些限制納入考量。
PCI Express事先規畫省電效率
標準平行的PCI介面納入許多遵循PCI匯流排電源管理介面規格的省電功能,因此,除了根聯合體(Root Complex)之外,其他所有的PCIe裝置都必須支援這些省電功能。此外,PCIe定義一套名為主動式電源管理(Active State Power Management, ASPM)的機制。主動式電源管理則是進一步擴充PCI電源的管理規格。ASPM與標準PCI電源管理規格之間的主要差別在於ASPM是自動運作的機制。
以往的省電設計中,主機軟體必須與裝置控制器進行互動,相互協調出適合的省電設定。USB就是很好的例子,主機驅動程式必須告訴主機控制器停止傳送開始欄位(Start of Frame, SOF)封包,USB裝置才能切換至低耗電模式。當主機一旦停止傳送SOF封包,USB裝置的耗電量就會大幅降低,從500mA降低至500μA。但由於研判何時關閉這些連結埠的程序相當複雜,Windows主機軟體只有在整部個人電腦進入閒置狀態時,才會讓USB匯流排暫停運作。
另一方面,想像一下,主機在這些裝置進入閒置狀態時,才能關閉這些裝置的運作會是怎樣的狀況?例如除非移動滑鼠,否則滑鼠不會消耗任何電力;另一個例子是試圖存取外接式硬碟時,外接硬碟機才會被啟動。因此,桌上型電腦會變得比較安靜,筆記型電腦會變得更輕巧,而筆記型電腦的電池續航力也會延長一點。
PCIe的ASPM則是能做到接近這種的理想作法,ASPM不需要主機、裝置或者驅動程式的互動,就能自動控制連結狀態。因此,這種設計讓連結能在不論多短暫閒置時間也可立即切換至低耗電狀態。自動電源控制機制亦能縮短連結回覆至全面運作模式延遲時間。
PCIe定義五種電源模式,其中包括正常運作到完全關機模式:
.L0 — 正常運作
.L0s —連結待機
.L1 — 連結閒置
.L2 — 僅啟用輔助電源
.L3 — 關閉電源,裝置關機
PCI Express REV1.1基礎規格
.一個「Yes」的指令即表示須要支援(除非另有說明)、「On」及「Off」的指令表示需要時脈及電源輸送、「On / Off」表示一個設計選擇項目。
PCI Express連結電源管理狀況
L0—正常運作:在L0模式下,連結處在全線運作狀態,所有時脈都啟用,任何傳輸作業都在正常延遲速度下進行,所有裝置都必須支援這種模式。
L0s—連結待機:所有PCIe系統都必須支援L0s模式,在L0s模式中,時脈都維持運作,電源亦保持開啟狀態,但連結不會主動傳送資料。這也意謂著連結從L0s模式回覆正常模式的時候時,必須要重新啟動,但回覆過程的延遲相當短暫。裝置在從L0s回覆時有不同的延遲時間,但是其變幅相當小。連接功能暫存器中定義的L0s延遲範圍為64ns至4μs。
L0s有許多優點,由於不需要端點對端點的互動就能進入L0s模式,因此切換的速度相當快。由於時脈訊號依然保持運作狀態,因此離開的速度也相當快。此外,它能套用至連結的某一端(傳送或接收端),另一端仍能繼續運作。這意謂著系統從一端傳送資料時,在大多數的時間會自動獲得L0s的協助,但在送出訊框完成(FC)封包以及通知(ACK)封包時,連結就會被喚醒。L0s的缺點是時脈訊號一直呈現運作狀態,因此L0s模式會耗用不少電力。
傳送器僅須傳送Electrical Idle指令集,並將PCIe鏈路置於電力待機狀態,就能將接收端置入L0s模式。Electrical Idle指令集是少數在實體層中進行解譯名為Ordered Set的PCIe訊息。Ordered Set指令集長度有四個字元,因此要將連結置入L0s模式時,需要的時間為4×10bits×400ps= 16ns,因此這是相當短的延遲。
因此,從L0s回覆至正常模式也會相當地快,傳送器只須在連結上傳送幾個Fast Training Sequence(FTS)Ordered Sets的指令集,連結就會回覆至正常模式。而且,接收器可以指定要從範圍在1到255的L0s回覆所需的FTS數量。因此,其連結便可以在16ns至 4ms間從L0s回覆至正常的模式,實際時間則視接收器的功能以及時脈來源而定。
L1—低耗電待機模式:L1則是PCIe的一種選項功能,其省電效率遠超過L0s,但是缺點就是延遲時間較為長久。在L1模式中,PCIe參考時脈訊號維持不變,但PCIe裝置使用的內部相鎖迴路(PLL)則被關閉,這種設計讓省電效率得以超越L0s,但卻衍生出較長的延遲和較高的傳輸佔量 (Overhead)。當下游裝置切換至PCI電源管理模式(D1~D3)或是當裝置已準備好透過上述自動電源控制機制ASPM進入L1模式時(圖2),系統就會進入L1模式。由於連結的兩端都須參與作業,因此進入L1模式所涉及的端點對端點互動步驟比進入L0s模式還要多。圖3顯示進入L1模式所需進行的互動步驟。在這個例子中,下游連結埠被指示進入L1模式,並將負責管理傳輸作業。鏈路的其中一端或兩端都可以發出指令,讓鏈路從L0模式轉換至L1模式。
首先,下游連結埠的電源管理邏輯欄位要求進入L1模式,為了要進入L1模式,該連結埠必須:
.阻擋新的傳輸層封包(TLP)傳輸作業
.確定回覆緩衝區已清空資料
.確定已收到足夠的流量控制權限,以便能在每個虛擬通道(VC)與每種資料流上達到最高容量的傳輸
因此,當所有條件都符合的時候,其中下游的連結埠就會開始持續傳送一個PM Active State Request L1的資料鏈結層封包(DLLP),最後直到收到PM Request Ack DLLP為止。在此同時,在上游部份,當收到PM Active State Request L1 DLLP的時候,就會觸發一連串的事件,該連結埠必須:
.阻斷新的傳輸層封包(TLP)傳輸作業
.等待回傳緩衝區清空資料
.等待收到足夠的流量控制權限,以便能在每個虛擬通道(VC)與每種資料流上達到最高容量的傳輸
當上游元件符合所有條件的時後,就會開始傳送一個PM Request Ack DLLP,這個訊號會觸發下游元件轉移至L1模式,並且透過上述的程序使得連結切換至閒置狀態。當連結的上游端收到切換至電源閒置模式的訊號時,它就會進入電源閒置模式。
這種程序看似複雜,但全部都是由PCIe狀態機器負責執行,如果裝置支援L1 ASPM機制,主機軟體甚至完全不必執行任何作業。這就是PCIe電源管理技術的優點。連結的任何一端皆可以讓連結離開L1狀態(圖3),當裝置決定要離開L1模式時,就會開始傳送TS1指令集到連結的另一端。回覆程序會迅速回覆連結的時序,並確保連結的實際參數跟連結關閉時完全一樣,從L1模式回覆所需的時間不到64ms,這個過程的時間愈短代表連結消耗的電力愈少。
L2—輔助電源模式:除了關閉裝置上所有電源外,L2模式是耗電量最低的狀態。在L2模式中,所有裝置的時脈訊號都處在閒置狀態,只剩下用來偵測網路喚醒功能(WAKE)與信標(Beacon)事件的低頻時脈。系統只能利用VAUX為裝置提供電力。若WAKE被啟動時,VAUX則供應高達375mA的電流,若WAKE關閉時,VAUX則只供應20mA的電流,進入L2模式與進入L1模式非常類似,兩者間只有以下差異:
.L2無法透過ASPM來觸發,只能由主機來觸發
.L2模式進入協定會將連結切換至「L2_ready」 狀態。當主機看到下游連結處於L2模式時,就會移除VMAIN。
.裝置須要運用更多的電源關閉步驟來進入L2模式
等候狀態、連結層傳輸、通訊協定電源規畫等,這些都是為了延長電池續航力所需要的關鍵要素。可攜式電子產品節省電力的關鍵在於縮短產品處理作業上的時間,以及縮短元件之間傳輸作業的時間,如此,電子裝置在標準運作模式下的耗電量就愈低。更確切地說,可攜式裝置進出各種作業的速度愈快,省電效率就愈高。為達到這項目標,在開發裝置必須考量PCI Express的能力。
就目前而言,許多業者等候PCI Express大舉進軍桌上型電腦與伺服器產品,根據上述特別的考量因素,PCI Express一直在開發電源敏感度協定。因此,當看到設計業者及工程師將PCI Express視為注重電池續航力的高速資料連結理想介面時,也毋須覺得訝異。考慮到人們對於「更快」、「更輕巧」,以及「功能更強勁」的看法永遠無法一致,業者在開發未來的筆記型電腦與可攜式運算裝置之際,PCI Express必然成為資料連結的最佳選擇。
(本文作者任職於Cypress Semiconductor)
(詳細圖表請見新電子238期1月號)
http://patchwork.kernel.org/patch/42481/
The L0s state can be managed separately for each direction (upstream
direction and downstream direction) of the link. But in the current
implementation, those are mixed up. With this patch, L0s for each
direction are managed separately.
To maintain three states (upstream direction L0s, downstream L0s and
L1), 'aspm_support', 'aspm_enabled', 'aspm_capable', 'aspm_disable'
and 'aspm_default' fields in struct pcie_link_state are changed to
3-bit from 2-bit. The 'latency' field is separated to two 'latency_up'
and 'latency_dw' fields to maintain exit latencies for each direction
of the link. For L0, 'latency_up.l0' and 'latency_dw.l0' are used to
configure upstream direction L0s and downstream direction L0s
respectively. For L1, larger value of 'latency_up.l1' and
'latency_dw.l1' is considered as L1 exit latency.
Signed-off-by: Kenji Kaneshige
---
drivers/pci/pcie/aspm.c | 170 ++++++++++++++++++++++++++++++------------------
1 file changed, 107 insertions(+), 63 deletions(-)
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Index: 20090818/drivers/pci/pcie/aspm.c
===================================================================
--- 20090818.orig/drivers/pci/pcie/aspm.c
+++ 20090818/drivers/pci/pcie/aspm.c
@@ -26,6 +26,13 @@
#endif
#define MODULE_PARAM_PREFIX "pcie_aspm."
+/* Note: those are not register definitions */
+#define ASPM_STATE_L0S_UP (1) /* Upstream direction L0s state */
+#define ASPM_STATE_L0S_DW (2) /* Downstream direction L0s state */
+#define ASPM_STATE_L1 (4) /* L1 state */
+#define ASPM_STATE_L0S (ASPM_STATE_L0S_UP | ASPM_STATE_L0S_DW)
+#define ASPM_STATE_ALL (ASPM_STATE_L0S | ASPM_STATE_L1)
+
struct aspm_latency {
u32 l0s; /* L0s latency (nsec) */
u32 l1; /* L1 latency (nsec) */
@@ -40,19 +47,20 @@ struct pcie_link_state {
struct list_head link; /* node in parent's children list */
/* ASPM state */
- u32 aspm_support:2; /* Supported ASPM state */
- u32 aspm_enabled:2; /* Enabled ASPM state */
- u32 aspm_capable:2; /* Capable ASPM state with latency */
- u32 aspm_default:2; /* Default ASPM state by BIOS */
- u32 aspm_disable:2; /* Disabled ASPM state */
+ u32 aspm_support:3; /* Supported ASPM state */
+ u32 aspm_enabled:3; /* Enabled ASPM state */
+ u32 aspm_capable:3; /* Capable ASPM state with latency */
+ u32 aspm_default:3; /* Default ASPM state by BIOS */
+ u32 aspm_disable:3; /* Disabled ASPM state */
/* Clock PM state */
u32 clkpm_capable:1; /* Clock PM capable? */
u32 clkpm_enabled:1; /* Current Clock PM state */
u32 clkpm_default:1; /* Default Clock PM state by BIOS */
- /* Latencies */
- struct aspm_latency latency; /* Exit latency */
+ /* Exit latencies */
+ struct aspm_latency latency_up; /* Upstream direction exit latency */
+ struct aspm_latency latency_dw; /* Downstream direction exit latency */
/*
* Endpoint acceptable latencies. A pcie downstream port only
* has one slot under it, so at most there are 8 functions.
@@ -84,7 +92,7 @@ static int policy_to_aspm_state(struct p
return 0;
case POLICY_POWERSAVE:
/* Enable ASPM L0s/L1 */
- return PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1;
+ return ASPM_STATE_ALL;
case POLICY_DEFAULT:
return link->aspm_default;
}
@@ -278,36 +286,35 @@ static u32 calc_l1_acceptable(u32 encodi
return (1000 << encoding);
}
-static void pcie_aspm_get_cap_device(struct pci_dev *pdev, u32 *state,
- u32 *l0s, u32 *l1, u32 *enabled)
+struct aspm_register_info {
+ u32 support:2;
+ u32 enabled:2;
+ u32 latency_encoding_l0s;
+ u32 latency_encoding_l1;
+};
+
+static void pcie_get_aspm_reg(struct pci_dev *pdev,
+ struct aspm_register_info *info)
{
int pos;
u16 reg16;
- u32 reg32, encoding;
+ u32 reg32;
- *l0s = *l1 = *enabled = 0;
pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
pci_read_config_dword(pdev, pos + PCI_EXP_LNKCAP, ®32);
- *state = (reg32 & PCI_EXP_LNKCAP_ASPMS) >> 10;
- if (*state != PCIE_LINK_STATE_L0S &&
- *state != (PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_L0S))
- *state = 0;
- if (*state == 0)
- return;
-
- encoding = (reg32 & PCI_EXP_LNKCAP_L0SEL) >> 12;
- *l0s = calc_l0s_latency(encoding);
- if (*state & PCIE_LINK_STATE_L1) {
- encoding = (reg32 & PCI_EXP_LNKCAP_L1EL) >> 15;
- *l1 = calc_l1_latency(encoding);
- }
+ info->support = (reg32 & PCI_EXP_LNKCAP_ASPMS) >> 10;
+ /* 00b and 10b are defined as "Reserved". */
+ if (info->support == PCIE_LINK_STATE_L1)
+ info->support = 0;
+ info->latency_encoding_l0s = (reg32 & PCI_EXP_LNKCAP_L0SEL) >> 12;
+ info->latency_encoding_l1 = (reg32 & PCI_EXP_LNKCAP_L1EL) >> 15;
pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, ®16);
- *enabled = reg16 & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
+ info->enabled = reg16 & PCI_EXP_LNKCTL_ASPMC;
}
static void pcie_aspm_check_latency(struct pci_dev *endpoint)
{
- u32 l1_switch_latency = 0;
+ u32 latency, l1_switch_latency = 0;
struct aspm_latency *acceptable;
struct pcie_link_state *link;
@@ -320,18 +327,24 @@ static void pcie_aspm_check_latency(stru
acceptable = &link->acceptable[PCI_FUNC(endpoint->devfn)];
while (link) {
- /* Check L0s latency */
- if ((link->aspm_capable & PCIE_LINK_STATE_L0S) &&
- (link->latency.l0s > acceptable->l0s))
- link->aspm_capable &= ~PCIE_LINK_STATE_L0S;
+ /* Check upstream direction L0s latency */
+ if ((link->aspm_capable & ASPM_STATE_L0S_UP) &&
+ (link->latency_up.l0s > acceptable->l0s))
+ link->aspm_capable &= ~ASPM_STATE_L0S_UP;
+
+ /* Check downstream direction L0s latency */
+ if ((link->aspm_capable & ASPM_STATE_L0S_DW) &&
+ (link->latency_dw.l0s > acceptable->l0s))
+ link->aspm_capable &= ~ASPM_STATE_L0S_DW;
/*
* Check L1 latency.
* Every switch on the path to root complex need 1
* more microsecond for L1. Spec doesn't mention L0s.
*/
- if ((link->aspm_capable & PCIE_LINK_STATE_L1) &&
- (link->latency.l1 + l1_switch_latency > acceptable->l1))
- link->aspm_capable &= ~PCIE_LINK_STATE_L1;
+ latency = max_t(u32, link->latency_up.l1, link->latency_dw.l1);
+ if ((link->aspm_capable & ASPM_STATE_L1) &&
+ (latency + l1_switch_latency > acceptable->l1))
+ link->aspm_capable &= ~ASPM_STATE_L1;
l1_switch_latency += 1000;
link = link->parent;
@@ -340,33 +353,48 @@ static void pcie_aspm_check_latency(stru
static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
{
- u32 support, l0s, l1, enabled;
struct pci_dev *child, *parent = link->pdev;
struct pci_bus *linkbus = parent->subordinate;
+ struct aspm_register_info upreg, dwreg;
if (blacklist) {
/* Set enabled/disable so that we will disable ASPM later */
- link->aspm_enabled = PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1;
- link->aspm_disable = PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1;
+ link->aspm_enabled = ASPM_STATE_ALL;
+ link->aspm_disable = ASPM_STATE_ALL;
return;
}
/* Configure common clock before checking latencies */
pcie_aspm_configure_common_clock(link);
- /* upstream component states */
- pcie_aspm_get_cap_device(parent, &support, &l0s, &l1, &enabled);
- link->aspm_support = support;
- link->latency.l0s = l0s;
- link->latency.l1 = l1;
- link->aspm_enabled = enabled;
-
- /* downstream component states, all functions have the same setting */
+ /* Get upstream/downstream components' register state */
+ pcie_get_aspm_reg(parent, &upreg);
child = list_entry(linkbus->devices.next, struct pci_dev, bus_list);
- pcie_aspm_get_cap_device(child, &support, &l0s, &l1, &enabled);
- link->aspm_support &= support;
- link->latency.l0s = max_t(u32, link->latency.l0s, l0s);
- link->latency.l1 = max_t(u32, link->latency.l1, l1);
+ pcie_get_aspm_reg(child, &dwreg);
+
+ /*
+ * Setup L0s state
+ *
+ * Note that we must not enable L0s in either direction on a
+ * given link unless components on both sides of the link each
+ * support L0s.
+ */
+ if (dwreg.support & upreg.support & PCIE_LINK_STATE_L0S)
+ link->aspm_support |= ASPM_STATE_L0S;
+ if (dwreg.enabled & PCIE_LINK_STATE_L0S)
+ link->aspm_enabled |= ASPM_STATE_L0S_UP;
+ if (upreg.enabled & PCIE_LINK_STATE_L0S)
+ link->aspm_enabled |= ASPM_STATE_L0S_DW;
+ link->latency_up.l0s = calc_l0s_latency(upreg.latency_encoding_l0s);
+ link->latency_dw.l0s = calc_l0s_latency(dwreg.latency_encoding_l0s);
+
+ /* Setup L1 state */
+ if (upreg.support & dwreg.support & PCIE_LINK_STATE_L1)
+ link->aspm_support |= ASPM_STATE_L1;
+ if (upreg.enabled & dwreg.enabled & PCIE_LINK_STATE_L1)
+ link->aspm_enabled |= ASPM_STATE_L1;
+ link->latency_up.l1 = calc_l1_latency(upreg.latency_encoding_l1);
+ link->latency_dw.l1 = calc_l1_latency(dwreg.latency_encoding_l1);
/* Save default state */
link->aspm_default = link->aspm_enabled;
@@ -379,8 +407,7 @@ static void pcie_aspm_cap_init(struct pc
*/
list_for_each_entry(child, &linkbus->devices, bus_list) {
if (child->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) {
- link->aspm_disable =
- PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1;
+ link->aspm_disable = ASPM_STATE_ALL;
break;
}
}
@@ -409,19 +436,20 @@ static void pcie_aspm_cap_init(struct pc
}
}
-static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 state)
+static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val)
{
u16 reg16;
int pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, ®16);
reg16 &= ~0x3;
- reg16 |= state;
+ reg16 |= val;
pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16);
}
static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state)
{
+ u32 upstream = 0, dwstream = 0;
struct pci_dev *child, *parent = link->pdev;
struct pci_bus *linkbus = parent->subordinate;
@@ -429,20 +457,27 @@ static void pcie_config_aspm_link(struct
state &= (link->aspm_capable & ~link->aspm_disable);
if (link->aspm_enabled == state)
return;
+ /* Convert ASPM state to upstream/downstream ASPM register state */
+ if (state & ASPM_STATE_L0S_UP)
+ dwstream |= PCIE_LINK_STATE_L0S;
+ if (state & ASPM_STATE_L0S_DW)
+ upstream |= PCIE_LINK_STATE_L0S;
+ if (state & ASPM_STATE_L1) {
+ upstream |= PCIE_LINK_STATE_L1;
+ dwstream |= PCIE_LINK_STATE_L1;
+ }
/*
* Spec 2.0 suggests all functions should be configured the
* same setting for ASPM. Enabling ASPM L1 should be done in
* upstream component first and then downstream, and vice
* versa for disabling ASPM L1. Spec doesn't mention L0S.
*/
- if (state & PCIE_LINK_STATE_L1)
- pcie_config_aspm_dev(parent, state);
-
+ if (state & ASPM_STATE_L1)
+ pcie_config_aspm_dev(parent, upstream);
list_for_each_entry(child, &linkbus->devices, bus_list)
- pcie_config_aspm_dev(child, state);
-
- if (!(state & PCIE_LINK_STATE_L1))
- pcie_config_aspm_dev(parent, state);
+ pcie_config_aspm_dev(child, dwstream);
+ if (!(state & ASPM_STATE_L1))
+ pcie_config_aspm_dev(parent, upstream);
link->aspm_enabled = state;
}
@@ -673,7 +708,10 @@ void pci_disable_link_state(struct pci_d
down_read(&pci_bus_sem);
mutex_lock(&aspm_lock);
link = parent->link_state;
- link->aspm_disable |= state;
+ if (state & PCIE_LINK_STATE_L0S)
+ link->aspm_disable |= ASPM_STATE_L0S;
+ if (state & PCIE_LINK_STATE_L1)
+ link->aspm_disable |= ASPM_STATE_L1;
pcie_config_aspm_link(link, policy_to_aspm_state(link));
if (state & PCIE_LINK_STATE_CLKPM) {
@@ -742,11 +780,17 @@ static ssize_t link_state_store(struct d
{
struct pci_dev *pdev = to_pci_dev(dev);
struct pcie_link_state *link, *root = pdev->link_state->root;
- u32 state = buf[0] - '0';
+ u32 val = buf[0] - '0', state = 0;
- if (n < 1 || state > 3)
+ if (n < 1 || val > 3)
return -EINVAL;
+ /* Convert requested state to ASPM state */
+ if (val & PCIE_LINK_STATE_L0S)
+ state |= ASPM_STATE_L0S;
+ if (val & PCIE_LINK_STATE_L1)
+ state |= ASPM_STATE_L1;
+
down_read(&pci_bus_sem);
mutex_lock(&aspm_lock);
list_for_each_entry(link, &link_list, sibling) {