aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorKenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>2009-08-18 22:02:13 -0400
committerJesse Barnes <jbarnes@virtuousgeek.org>2009-09-09 16:29:49 -0400
commitac18018a414a90d841ea81d38fecb913c0ec1880 (patch)
tree21eeedf2a160f50ae6f6bcba1ef7d5ee5d6cbfb4 /drivers
parentb7206cbf024dd43c42f9585e2017db1c1facd566 (diff)
PCI ASPM: support per direction l0s management
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. Acked-by: Shaohua Li <shaohua.li@intel.com> Signed-off-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/pci/pcie/aspm.c170
1 files changed, 107 insertions, 63 deletions
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 08d293f60fe8..f289ca9bf18d 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -26,6 +26,13 @@
26#endif 26#endif
27#define MODULE_PARAM_PREFIX "pcie_aspm." 27#define MODULE_PARAM_PREFIX "pcie_aspm."
28 28
29/* Note: those are not register definitions */
30#define ASPM_STATE_L0S_UP (1) /* Upstream direction L0s state */
31#define ASPM_STATE_L0S_DW (2) /* Downstream direction L0s state */
32#define ASPM_STATE_L1 (4) /* L1 state */
33#define ASPM_STATE_L0S (ASPM_STATE_L0S_UP | ASPM_STATE_L0S_DW)
34#define ASPM_STATE_ALL (ASPM_STATE_L0S | ASPM_STATE_L1)
35
29struct aspm_latency { 36struct aspm_latency {
30 u32 l0s; /* L0s latency (nsec) */ 37 u32 l0s; /* L0s latency (nsec) */
31 u32 l1; /* L1 latency (nsec) */ 38 u32 l1; /* L1 latency (nsec) */
@@ -40,19 +47,20 @@ struct pcie_link_state {
40 struct list_head link; /* node in parent's children list */ 47 struct list_head link; /* node in parent's children list */
41 48
42 /* ASPM state */ 49 /* ASPM state */
43 u32 aspm_support:2; /* Supported ASPM state */ 50 u32 aspm_support:3; /* Supported ASPM state */
44 u32 aspm_enabled:2; /* Enabled ASPM state */ 51 u32 aspm_enabled:3; /* Enabled ASPM state */
45 u32 aspm_capable:2; /* Capable ASPM state with latency */ 52 u32 aspm_capable:3; /* Capable ASPM state with latency */
46 u32 aspm_default:2; /* Default ASPM state by BIOS */ 53 u32 aspm_default:3; /* Default ASPM state by BIOS */
47 u32 aspm_disable:2; /* Disabled ASPM state */ 54 u32 aspm_disable:3; /* Disabled ASPM state */
48 55
49 /* Clock PM state */ 56 /* Clock PM state */
50 u32 clkpm_capable:1; /* Clock PM capable? */ 57 u32 clkpm_capable:1; /* Clock PM capable? */
51 u32 clkpm_enabled:1; /* Current Clock PM state */ 58 u32 clkpm_enabled:1; /* Current Clock PM state */
52 u32 clkpm_default:1; /* Default Clock PM state by BIOS */ 59 u32 clkpm_default:1; /* Default Clock PM state by BIOS */
53 60
54 /* Latencies */ 61 /* Exit latencies */
55 struct aspm_latency latency; /* Exit latency */ 62 struct aspm_latency latency_up; /* Upstream direction exit latency */
63 struct aspm_latency latency_dw; /* Downstream direction exit latency */
56 /* 64 /*
57 * Endpoint acceptable latencies. A pcie downstream port only 65 * Endpoint acceptable latencies. A pcie downstream port only
58 * has one slot under it, so at most there are 8 functions. 66 * has one slot under it, so at most there are 8 functions.
@@ -84,7 +92,7 @@ static int policy_to_aspm_state(struct pcie_link_state *link)
84 return 0; 92 return 0;
85 case POLICY_POWERSAVE: 93 case POLICY_POWERSAVE:
86 /* Enable ASPM L0s/L1 */ 94 /* Enable ASPM L0s/L1 */
87 return PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1; 95 return ASPM_STATE_ALL;
88 case POLICY_DEFAULT: 96 case POLICY_DEFAULT:
89 return link->aspm_default; 97 return link->aspm_default;
90 } 98 }
@@ -278,36 +286,35 @@ static u32 calc_l1_acceptable(u32 encoding)
278 return (1000 << encoding); 286 return (1000 << encoding);
279} 287}
280 288
281static void pcie_aspm_get_cap_device(struct pci_dev *pdev, u32 *state, 289struct aspm_register_info {
282 u32 *l0s, u32 *l1, u32 *enabled) 290 u32 support:2;
291 u32 enabled:2;
292 u32 latency_encoding_l0s;
293 u32 latency_encoding_l1;
294};
295
296static void pcie_get_aspm_reg(struct pci_dev *pdev,
297 struct aspm_register_info *info)
283{ 298{
284 int pos; 299 int pos;
285 u16 reg16; 300 u16 reg16;
286 u32 reg32, encoding; 301 u32 reg32;
287 302
288 *l0s = *l1 = *enabled = 0;
289 pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); 303 pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
290 pci_read_config_dword(pdev, pos + PCI_EXP_LNKCAP, &reg32); 304 pci_read_config_dword(pdev, pos + PCI_EXP_LNKCAP, &reg32);
291 *state = (reg32 & PCI_EXP_LNKCAP_ASPMS) >> 10; 305 info->support = (reg32 & PCI_EXP_LNKCAP_ASPMS) >> 10;
292 if (*state != PCIE_LINK_STATE_L0S && 306 /* 00b and 10b are defined as "Reserved". */
293 *state != (PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_L0S)) 307 if (info->support == PCIE_LINK_STATE_L1)
294 *state = 0; 308 info->support = 0;
295 if (*state == 0) 309 info->latency_encoding_l0s = (reg32 & PCI_EXP_LNKCAP_L0SEL) >> 12;
296 return; 310 info->latency_encoding_l1 = (reg32 & PCI_EXP_LNKCAP_L1EL) >> 15;
297
298 encoding = (reg32 & PCI_EXP_LNKCAP_L0SEL) >> 12;
299 *l0s = calc_l0s_latency(encoding);
300 if (*state & PCIE_LINK_STATE_L1) {
301 encoding = (reg32 & PCI_EXP_LNKCAP_L1EL) >> 15;
302 *l1 = calc_l1_latency(encoding);
303 }
304 pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, &reg16); 311 pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, &reg16);
305 *enabled = reg16 & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); 312 info->enabled = reg16 & PCI_EXP_LNKCTL_ASPMC;
306} 313}
307 314
308static void pcie_aspm_check_latency(struct pci_dev *endpoint) 315static void pcie_aspm_check_latency(struct pci_dev *endpoint)
309{ 316{
310 u32 l1_switch_latency = 0; 317 u32 latency, l1_switch_latency = 0;
311 struct aspm_latency *acceptable; 318 struct aspm_latency *acceptable;
312 struct pcie_link_state *link; 319 struct pcie_link_state *link;
313 320
@@ -320,18 +327,24 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint)
320 acceptable = &link->acceptable[PCI_FUNC(endpoint->devfn)]; 327 acceptable = &link->acceptable[PCI_FUNC(endpoint->devfn)];
321 328
322 while (link) { 329 while (link) {
323 /* Check L0s latency */ 330 /* Check upstream direction L0s latency */
324 if ((link->aspm_capable & PCIE_LINK_STATE_L0S) && 331 if ((link->aspm_capable & ASPM_STATE_L0S_UP) &&
325 (link->latency.l0s > acceptable->l0s)) 332 (link->latency_up.l0s > acceptable->l0s))
326 link->aspm_capable &= ~PCIE_LINK_STATE_L0S; 333 link->aspm_capable &= ~ASPM_STATE_L0S_UP;
334
335 /* Check downstream direction L0s latency */
336 if ((link->aspm_capable & ASPM_STATE_L0S_DW) &&
337 (link->latency_dw.l0s > acceptable->l0s))
338 link->aspm_capable &= ~ASPM_STATE_L0S_DW;
327 /* 339 /*
328 * Check L1 latency. 340 * Check L1 latency.
329 * Every switch on the path to root complex need 1 341 * Every switch on the path to root complex need 1
330 * more microsecond for L1. Spec doesn't mention L0s. 342 * more microsecond for L1. Spec doesn't mention L0s.
331 */ 343 */
332 if ((link->aspm_capable & PCIE_LINK_STATE_L1) && 344 latency = max_t(u32, link->latency_up.l1, link->latency_dw.l1);
333 (link->latency.l1 + l1_switch_latency > acceptable->l1)) 345 if ((link->aspm_capable & ASPM_STATE_L1) &&
334 link->aspm_capable &= ~PCIE_LINK_STATE_L1; 346 (latency + l1_switch_latency > acceptable->l1))
347 link->aspm_capable &= ~ASPM_STATE_L1;
335 l1_switch_latency += 1000; 348 l1_switch_latency += 1000;
336 349
337 link = link->parent; 350 link = link->parent;
@@ -340,33 +353,48 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint)
340 353
341static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) 354static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
342{ 355{
343 u32 support, l0s, l1, enabled;
344 struct pci_dev *child, *parent = link->pdev; 356 struct pci_dev *child, *parent = link->pdev;
345 struct pci_bus *linkbus = parent->subordinate; 357 struct pci_bus *linkbus = parent->subordinate;
358 struct aspm_register_info upreg, dwreg;
346 359
347 if (blacklist) { 360 if (blacklist) {
348 /* Set enabled/disable so that we will disable ASPM later */ 361 /* Set enabled/disable so that we will disable ASPM later */
349 link->aspm_enabled = PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1; 362 link->aspm_enabled = ASPM_STATE_ALL;
350 link->aspm_disable = PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1; 363 link->aspm_disable = ASPM_STATE_ALL;
351 return; 364 return;
352 } 365 }
353 366
354 /* Configure common clock before checking latencies */ 367 /* Configure common clock before checking latencies */
355 pcie_aspm_configure_common_clock(link); 368 pcie_aspm_configure_common_clock(link);
356 369
357 /* upstream component states */ 370 /* Get upstream/downstream components' register state */
358 pcie_aspm_get_cap_device(parent, &support, &l0s, &l1, &enabled); 371 pcie_get_aspm_reg(parent, &upreg);
359 link->aspm_support = support;
360 link->latency.l0s = l0s;
361 link->latency.l1 = l1;
362 link->aspm_enabled = enabled;
363
364 /* downstream component states, all functions have the same setting */
365 child = list_entry(linkbus->devices.next, struct pci_dev, bus_list); 372 child = list_entry(linkbus->devices.next, struct pci_dev, bus_list);
366 pcie_aspm_get_cap_device(child, &support, &l0s, &l1, &enabled); 373 pcie_get_aspm_reg(child, &dwreg);
367 link->aspm_support &= support; 374
368 link->latency.l0s = max_t(u32, link->latency.l0s, l0s); 375 /*
369 link->latency.l1 = max_t(u32, link->latency.l1, l1); 376 * Setup L0s state
377 *
378 * Note that we must not enable L0s in either direction on a
379 * given link unless components on both sides of the link each
380 * support L0s.
381 */
382 if (dwreg.support & upreg.support & PCIE_LINK_STATE_L0S)
383 link->aspm_support |= ASPM_STATE_L0S;
384 if (dwreg.enabled & PCIE_LINK_STATE_L0S)
385 link->aspm_enabled |= ASPM_STATE_L0S_UP;
386 if (upreg.enabled & PCIE_LINK_STATE_L0S)
387 link->aspm_enabled |= ASPM_STATE_L0S_DW;
388 link->latency_up.l0s = calc_l0s_latency(upreg.latency_encoding_l0s);
389 link->latency_dw.l0s = calc_l0s_latency(dwreg.latency_encoding_l0s);
390
391 /* Setup L1 state */
392 if (upreg.support & dwreg.support & PCIE_LINK_STATE_L1)
393 link->aspm_support |= ASPM_STATE_L1;
394 if (upreg.enabled & dwreg.enabled & PCIE_LINK_STATE_L1)
395 link->aspm_enabled |= ASPM_STATE_L1;
396 link->latency_up.l1 = calc_l1_latency(upreg.latency_encoding_l1);
397 link->latency_dw.l1 = calc_l1_latency(dwreg.latency_encoding_l1);
370 398
371 /* Save default state */ 399 /* Save default state */
372 link->aspm_default = link->aspm_enabled; 400 link->aspm_default = link->aspm_enabled;
@@ -379,8 +407,7 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
379 */ 407 */
380 list_for_each_entry(child, &linkbus->devices, bus_list) { 408 list_for_each_entry(child, &linkbus->devices, bus_list) {
381 if (child->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) { 409 if (child->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) {
382 link->aspm_disable = 410 link->aspm_disable = ASPM_STATE_ALL;
383 PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1;
384 break; 411 break;
385 } 412 }
386 } 413 }
@@ -409,19 +436,20 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
409 } 436 }
410} 437}
411 438
412static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 state) 439static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val)
413{ 440{
414 u16 reg16; 441 u16 reg16;
415 int pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); 442 int pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
416 443
417 pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, &reg16); 444 pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, &reg16);
418 reg16 &= ~0x3; 445 reg16 &= ~0x3;
419 reg16 |= state; 446 reg16 |= val;
420 pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16); 447 pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16);
421} 448}
422 449
423static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state) 450static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state)
424{ 451{
452 u32 upstream = 0, dwstream = 0;
425 struct pci_dev *child, *parent = link->pdev; 453 struct pci_dev *child, *parent = link->pdev;
426 struct pci_bus *linkbus = parent->subordinate; 454 struct pci_bus *linkbus = parent->subordinate;
427 455
@@ -429,20 +457,27 @@ static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state)
429 state &= (link->aspm_capable & ~link->aspm_disable); 457 state &= (link->aspm_capable & ~link->aspm_disable);
430 if (link->aspm_enabled == state) 458 if (link->aspm_enabled == state)
431 return; 459 return;
460 /* Convert ASPM state to upstream/downstream ASPM register state */
461 if (state & ASPM_STATE_L0S_UP)
462 dwstream |= PCIE_LINK_STATE_L0S;
463 if (state & ASPM_STATE_L0S_DW)
464 upstream |= PCIE_LINK_STATE_L0S;
465 if (state & ASPM_STATE_L1) {
466 upstream |= PCIE_LINK_STATE_L1;
467 dwstream |= PCIE_LINK_STATE_L1;
468 }
432 /* 469 /*
433 * Spec 2.0 suggests all functions should be configured the 470 * Spec 2.0 suggests all functions should be configured the
434 * same setting for ASPM. Enabling ASPM L1 should be done in 471 * same setting for ASPM. Enabling ASPM L1 should be done in
435 * upstream component first and then downstream, and vice 472 * upstream component first and then downstream, and vice
436 * versa for disabling ASPM L1. Spec doesn't mention L0S. 473 * versa for disabling ASPM L1. Spec doesn't mention L0S.
437 */ 474 */
438 if (state & PCIE_LINK_STATE_L1) 475 if (state & ASPM_STATE_L1)
439 pcie_config_aspm_dev(parent, state); 476 pcie_config_aspm_dev(parent, upstream);
440
441 list_for_each_entry(child, &linkbus->devices, bus_list) 477 list_for_each_entry(child, &linkbus->devices, bus_list)
442 pcie_config_aspm_dev(child, state); 478 pcie_config_aspm_dev(child, dwstream);
443 479 if (!(state & ASPM_STATE_L1))
444 if (!(state & PCIE_LINK_STATE_L1)) 480 pcie_config_aspm_dev(parent, upstream);
445 pcie_config_aspm_dev(parent, state);
446 481
447 link->aspm_enabled = state; 482 link->aspm_enabled = state;
448} 483}
@@ -673,7 +708,10 @@ void pci_disable_link_state(struct pci_dev *pdev, int state)
673 down_read(&pci_bus_sem); 708 down_read(&pci_bus_sem);
674 mutex_lock(&aspm_lock); 709 mutex_lock(&aspm_lock);
675 link = parent->link_state; 710 link = parent->link_state;
676 link->aspm_disable |= state; 711 if (state & PCIE_LINK_STATE_L0S)
712 link->aspm_disable |= ASPM_STATE_L0S;
713 if (state & PCIE_LINK_STATE_L1)
714 link->aspm_disable |= ASPM_STATE_L1;
677 pcie_config_aspm_link(link, policy_to_aspm_state(link)); 715 pcie_config_aspm_link(link, policy_to_aspm_state(link));
678 716
679 if (state & PCIE_LINK_STATE_CLKPM) { 717 if (state & PCIE_LINK_STATE_CLKPM) {
@@ -742,11 +780,17 @@ static ssize_t link_state_store(struct device *dev,
742{ 780{
743 struct pci_dev *pdev = to_pci_dev(dev); 781 struct pci_dev *pdev = to_pci_dev(dev);
744 struct pcie_link_state *link, *root = pdev->link_state->root; 782 struct pcie_link_state *link, *root = pdev->link_state->root;
745 u32 state = buf[0] - '0'; 783 u32 val = buf[0] - '0', state = 0;
746 784
747 if (n < 1 || state > 3) 785 if (n < 1 || val > 3)
748 return -EINVAL; 786 return -EINVAL;
749 787
788 /* Convert requested state to ASPM state */
789 if (val & PCIE_LINK_STATE_L0S)
790 state |= ASPM_STATE_L0S;
791 if (val & PCIE_LINK_STATE_L1)
792 state |= ASPM_STATE_L1;
793
750 down_read(&pci_bus_sem); 794 down_read(&pci_bus_sem);
751 mutex_lock(&aspm_lock); 795 mutex_lock(&aspm_lock);
752 list_for_each_entry(link, &link_list, sibling) { 796 list_for_each_entry(link, &link_list, sibling) {