diff options
author | Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com> | 2009-08-18 22:00:25 -0400 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2009-09-09 16:29:47 -0400 |
commit | 07d92760d2ee542fe932f4e8b5807dd98481d1fd (patch) | |
tree | 47d94230d73567b16f9c9a307d1c8f08eff47184 /drivers/pci/pcie | |
parent | f1c0ca29ae72bc0c10282eada66c8a792ee98482 (diff) |
PCI ASPM: introduce capable flag
Introduce 'aspm_capable' field to maintain the capable ASPM setting of
the link. By the 'aspm_capable', we don't need to recheck latency
every time ASPM policy is changed.
Each bit in 'aspm_capable' is associated to ASPM state (L0S/L1). The
bit is set if the associated ASPM state is supported by the link and
it satisfies the latency requirement (i.e. exit latency < endpoint
acceptable latency). The 'aspm_capable' is updated when
- an endpoint device is added (boot time or hot-plug time)
- an endpoint device is removed (hot-unplug time)
- PCI power state is changed.
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/pci/pcie')
-rw-r--r-- | drivers/pci/pcie/aspm.c | 117 |
1 files changed, 86 insertions, 31 deletions
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 8f7884a8bc2e..79f6d61798fa 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c | |||
@@ -42,6 +42,7 @@ struct pcie_link_state { | |||
42 | /* ASPM state */ | 42 | /* ASPM state */ |
43 | u32 aspm_support:2; /* Supported ASPM state */ | 43 | u32 aspm_support:2; /* Supported ASPM state */ |
44 | u32 aspm_enabled:2; /* Enabled ASPM state */ | 44 | u32 aspm_enabled:2; /* Enabled ASPM state */ |
45 | u32 aspm_capable:2; /* Capable ASPM state with latency */ | ||
45 | u32 aspm_default:2; /* Default ASPM state by BIOS */ | 46 | u32 aspm_default:2; /* Default ASPM state by BIOS */ |
46 | u32 aspm_disable:2; /* Disabled ASPM state */ | 47 | u32 aspm_disable:2; /* Disabled ASPM state */ |
47 | 48 | ||
@@ -316,6 +317,39 @@ static void pcie_aspm_get_cap_device(struct pci_dev *pdev, u32 *state, | |||
316 | *enabled = reg16 & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); | 317 | *enabled = reg16 & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); |
317 | } | 318 | } |
318 | 319 | ||
320 | static void pcie_aspm_check_latency(struct pci_dev *endpoint) | ||
321 | { | ||
322 | u32 l1_switch_latency = 0; | ||
323 | struct aspm_latency *acceptable; | ||
324 | struct pcie_link_state *link; | ||
325 | |||
326 | /* Device not in D0 doesn't need latency check */ | ||
327 | if ((endpoint->current_state != PCI_D0) && | ||
328 | (endpoint->current_state != PCI_UNKNOWN)) | ||
329 | return; | ||
330 | |||
331 | link = endpoint->bus->self->link_state; | ||
332 | acceptable = &link->acceptable[PCI_FUNC(endpoint->devfn)]; | ||
333 | |||
334 | while (link) { | ||
335 | /* Check L0s latency */ | ||
336 | if ((link->aspm_capable & PCIE_LINK_STATE_L0S) && | ||
337 | (link->latency.l0s > acceptable->l0s)) | ||
338 | link->aspm_capable &= ~PCIE_LINK_STATE_L0S; | ||
339 | /* | ||
340 | * Check L1 latency. | ||
341 | * Every switch on the path to root complex need 1 | ||
342 | * more microsecond for L1. Spec doesn't mention L0s. | ||
343 | */ | ||
344 | if ((link->aspm_capable & PCIE_LINK_STATE_L1) && | ||
345 | (link->latency.l1 + l1_switch_latency > acceptable->l1)) | ||
346 | link->aspm_capable &= ~PCIE_LINK_STATE_L1; | ||
347 | l1_switch_latency += 1000; | ||
348 | |||
349 | link = link->parent; | ||
350 | } | ||
351 | } | ||
352 | |||
319 | static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) | 353 | static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) |
320 | { | 354 | { |
321 | u32 support, l0s, l1, enabled; | 355 | u32 support, l0s, l1, enabled; |
@@ -348,6 +382,9 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) | |||
348 | 382 | ||
349 | /* Save default state */ | 383 | /* Save default state */ |
350 | link->aspm_default = link->aspm_enabled; | 384 | link->aspm_default = link->aspm_enabled; |
385 | |||
386 | /* Setup initial capable state. Will be updated later */ | ||
387 | link->aspm_capable = link->aspm_support; | ||
351 | /* | 388 | /* |
352 | * If the downstream component has pci bridge function, don't | 389 | * If the downstream component has pci bridge function, don't |
353 | * do ASPM for now. | 390 | * do ASPM for now. |
@@ -376,12 +413,14 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) | |||
376 | 413 | ||
377 | pos = pci_find_capability(child, PCI_CAP_ID_EXP); | 414 | pos = pci_find_capability(child, PCI_CAP_ID_EXP); |
378 | pci_read_config_dword(child, pos + PCI_EXP_DEVCAP, ®32); | 415 | pci_read_config_dword(child, pos + PCI_EXP_DEVCAP, ®32); |
416 | /* Calculate endpoint L0s acceptable latency */ | ||
379 | encoding = (reg32 & PCI_EXP_DEVCAP_L0S) >> 6; | 417 | encoding = (reg32 & PCI_EXP_DEVCAP_L0S) >> 6; |
380 | acceptable->l0s = calc_l0s_acceptable(encoding); | 418 | acceptable->l0s = calc_l0s_acceptable(encoding); |
381 | if (link->aspm_support & PCIE_LINK_STATE_L1) { | 419 | /* Calculate endpoint L1 acceptable latency */ |
382 | encoding = (reg32 & PCI_EXP_DEVCAP_L1) >> 9; | 420 | encoding = (reg32 & PCI_EXP_DEVCAP_L1) >> 9; |
383 | acceptable->l1 = calc_l1_acceptable(encoding); | 421 | acceptable->l1 = calc_l1_acceptable(encoding); |
384 | } | 422 | |
423 | pcie_aspm_check_latency(child); | ||
385 | } | 424 | } |
386 | } | 425 | } |
387 | 426 | ||
@@ -397,27 +436,10 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) | |||
397 | */ | 436 | */ |
398 | static u32 __pcie_aspm_check_state_one(struct pci_dev *endpoint, u32 state) | 437 | static u32 __pcie_aspm_check_state_one(struct pci_dev *endpoint, u32 state) |
399 | { | 438 | { |
400 | u32 l1_switch_latency = 0; | 439 | struct pcie_link_state *link = endpoint->bus->self->link_state; |
401 | struct aspm_latency *acceptable; | ||
402 | struct pcie_link_state *link; | ||
403 | |||
404 | link = endpoint->bus->self->link_state; | ||
405 | state &= link->aspm_support; | ||
406 | acceptable = &link->acceptable[PCI_FUNC(endpoint->devfn)]; | ||
407 | |||
408 | while (link && state) { | 440 | while (link && state) { |
409 | if ((state & PCIE_LINK_STATE_L0S) && | 441 | state &= link->aspm_capable; |
410 | (link->latency.l0s > acceptable->l0s)) | ||
411 | state &= ~PCIE_LINK_STATE_L0S; | ||
412 | if ((state & PCIE_LINK_STATE_L1) && | ||
413 | (link->latency.l1 + l1_switch_latency > acceptable->l1)) | ||
414 | state &= ~PCIE_LINK_STATE_L1; | ||
415 | link = link->parent; | 442 | link = link->parent; |
416 | /* | ||
417 | * Every switch on the path to root complex need 1 | ||
418 | * more microsecond for L1. Spec doesn't mention L0s. | ||
419 | */ | ||
420 | l1_switch_latency += 1000; | ||
421 | } | 443 | } |
422 | return state; | 444 | return state; |
423 | } | 445 | } |
@@ -668,11 +690,35 @@ out: | |||
668 | up_read(&pci_bus_sem); | 690 | up_read(&pci_bus_sem); |
669 | } | 691 | } |
670 | 692 | ||
693 | /* Recheck latencies and update aspm_capable for links under the root */ | ||
694 | static void pcie_update_aspm_capable(struct pcie_link_state *root) | ||
695 | { | ||
696 | struct pcie_link_state *link; | ||
697 | BUG_ON(root->parent); | ||
698 | list_for_each_entry(link, &link_list, sibling) { | ||
699 | if (link->root != root) | ||
700 | continue; | ||
701 | link->aspm_capable = link->aspm_support; | ||
702 | } | ||
703 | list_for_each_entry(link, &link_list, sibling) { | ||
704 | struct pci_dev *child; | ||
705 | struct pci_bus *linkbus = link->pdev->subordinate; | ||
706 | if (link->root != root) | ||
707 | continue; | ||
708 | list_for_each_entry(child, &linkbus->devices, bus_list) { | ||
709 | if ((child->pcie_type != PCI_EXP_TYPE_ENDPOINT) && | ||
710 | (child->pcie_type != PCI_EXP_TYPE_LEG_END)) | ||
711 | continue; | ||
712 | pcie_aspm_check_latency(child); | ||
713 | } | ||
714 | } | ||
715 | } | ||
716 | |||
671 | /* @pdev: the endpoint device */ | 717 | /* @pdev: the endpoint device */ |
672 | void pcie_aspm_exit_link_state(struct pci_dev *pdev) | 718 | void pcie_aspm_exit_link_state(struct pci_dev *pdev) |
673 | { | 719 | { |
674 | struct pci_dev *parent = pdev->bus->self; | 720 | struct pci_dev *parent = pdev->bus->self; |
675 | struct pcie_link_state *link; | 721 | struct pcie_link_state *link, *root; |
676 | 722 | ||
677 | if (aspm_disabled || !pdev->is_pcie || !parent || !parent->link_state) | 723 | if (aspm_disabled || !pdev->is_pcie || !parent || !parent->link_state) |
678 | return; | 724 | return; |
@@ -690,6 +736,7 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) | |||
690 | goto out; | 736 | goto out; |
691 | 737 | ||
692 | link = parent->link_state; | 738 | link = parent->link_state; |
739 | root = link->root; | ||
693 | 740 | ||
694 | /* All functions are removed, so just disable ASPM for the link */ | 741 | /* All functions are removed, so just disable ASPM for the link */ |
695 | __pcie_aspm_config_one_dev(parent, 0); | 742 | __pcie_aspm_config_one_dev(parent, 0); |
@@ -697,6 +744,9 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) | |||
697 | list_del(&link->link); | 744 | list_del(&link->link); |
698 | /* Clock PM is for endpoint device */ | 745 | /* Clock PM is for endpoint device */ |
699 | free_link_state(link); | 746 | free_link_state(link); |
747 | |||
748 | /* Recheck latencies and configure upstream links */ | ||
749 | pcie_update_aspm_capable(root); | ||
700 | out: | 750 | out: |
701 | mutex_unlock(&aspm_lock); | 751 | mutex_unlock(&aspm_lock); |
702 | up_read(&pci_bus_sem); | 752 | up_read(&pci_bus_sem); |
@@ -705,18 +755,23 @@ out: | |||
705 | /* @pdev: the root port or switch downstream port */ | 755 | /* @pdev: the root port or switch downstream port */ |
706 | void pcie_aspm_pm_state_change(struct pci_dev *pdev) | 756 | void pcie_aspm_pm_state_change(struct pci_dev *pdev) |
707 | { | 757 | { |
708 | struct pcie_link_state *link_state = pdev->link_state; | 758 | struct pcie_link_state *link = pdev->link_state; |
709 | 759 | ||
710 | if (aspm_disabled || !pdev->is_pcie || !pdev->link_state) | 760 | if (aspm_disabled || !pdev->is_pcie || !link) |
711 | return; | 761 | return; |
712 | if (pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT && | 762 | if ((pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT) && |
713 | pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) | 763 | (pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM)) |
714 | return; | 764 | return; |
715 | /* | 765 | /* |
716 | * devices changed PM state, we should recheck if latency meets all | 766 | * Devices changed PM state, we should recheck if latency |
717 | * functions' requirement | 767 | * meets all functions' requirement |
718 | */ | 768 | */ |
719 | pcie_aspm_configure_link_state(link_state, link_state->aspm_enabled); | 769 | down_read(&pci_bus_sem); |
770 | mutex_lock(&aspm_lock); | ||
771 | pcie_update_aspm_capable(link->root); | ||
772 | __pcie_aspm_configure_link_state(link, link->aspm_enabled); | ||
773 | mutex_unlock(&aspm_lock); | ||
774 | up_read(&pci_bus_sem); | ||
720 | } | 775 | } |
721 | 776 | ||
722 | /* | 777 | /* |