diff options
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/pcie/aspm.c | 170 |
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 | |||
29 | struct aspm_latency { | 36 | struct 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 | ||
281 | static void pcie_aspm_get_cap_device(struct pci_dev *pdev, u32 *state, | 289 | struct 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 | |||
296 | static 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, ®32); | 304 | pci_read_config_dword(pdev, pos + PCI_EXP_LNKCAP, ®32); |
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, ®16); | 311 | pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, ®16); |
305 | *enabled = reg16 & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); | 312 | info->enabled = reg16 & PCI_EXP_LNKCTL_ASPMC; |
306 | } | 313 | } |
307 | 314 | ||
308 | static void pcie_aspm_check_latency(struct pci_dev *endpoint) | 315 | static 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 | ||
341 | static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) | 354 | static 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 | ||
412 | static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 state) | 439 | static 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, ®16); | 444 | pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, ®16); |
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 | ||
423 | static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state) | 450 | static 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) { |