diff options
| author | Jon Mason <jon.mason@intel.com> | 2013-07-15 19:43:54 -0400 |
|---|---|---|
| committer | Jon Mason <jon.mason@intel.com> | 2013-09-05 14:07:58 -0400 |
| commit | ed6c24eda97b6bdcd013dbd91cc5c8b02de507e9 (patch) | |
| tree | 992940462fcdf730a3c93af95dd051b8a543f6ff /drivers/ntb | |
| parent | 497938890a0121d040683aa1db42cad8daf7ae97 (diff) | |
NTB: NTB-RP support
Add support for Non-Transparent Bridge connected to a PCI-E Root Port on
the remote system (also known as NTB-RP mode). This allows for a NTB
enabled system to be connected to a non-NTB enabled system/slot.
Modifications to the registers and BARs/MWs on the Secondary side by the
remote system are reflected into registers on the Primary side for the
local system. Similarly, modifications of registers and BARs/MWs on
Primary side by the local system are reflected into registers on the
Secondary side for the Remote System. This allows communication between
the 2 sides via these registers and BARs/MWs.
Note: there is not a fix for the Xeon Errata (that was already worked
around in NTB-B2B mode) for NTB-RP mode. Due to this limitation, NTB-RP
will not work on the Secondary side with the Xeon Errata workaround
enabled. To get around this, disable the workaround via the
xeon_errata_workaround=0 modparm. However, this can cause the hang
described in the errata.
Signed-off-by: Jon Mason <jon.mason@intel.com>
Diffstat (limited to 'drivers/ntb')
| -rw-r--r-- | drivers/ntb/ntb_hw.c | 262 | ||||
| -rw-r--r-- | drivers/ntb/ntb_regs.h | 5 |
2 files changed, 163 insertions, 104 deletions
diff --git a/drivers/ntb/ntb_hw.c b/drivers/ntb/ntb_hw.c index 97b18eec3d39..2f3ff73b1d1b 100644 --- a/drivers/ntb/ntb_hw.c +++ b/drivers/ntb/ntb_hw.c | |||
| @@ -69,7 +69,7 @@ module_param(xeon_errata_workaround, bool, 0644); | |||
| 69 | MODULE_PARM_DESC(xeon_errata_workaround, "Workaround for the Xeon Errata"); | 69 | MODULE_PARM_DESC(xeon_errata_workaround, "Workaround for the Xeon Errata"); |
| 70 | 70 | ||
| 71 | enum { | 71 | enum { |
| 72 | NTB_CONN_CLASSIC = 0, | 72 | NTB_CONN_TRANSPARENT = 0, |
| 73 | NTB_CONN_B2B, | 73 | NTB_CONN_B2B, |
| 74 | NTB_CONN_RP, | 74 | NTB_CONN_RP, |
| 75 | }; | 75 | }; |
| @@ -509,7 +509,8 @@ static void ntb_link_event(struct ntb_device *ndev, int link_state) | |||
| 509 | ndev->link_status = NTB_LINK_UP; | 509 | ndev->link_status = NTB_LINK_UP; |
| 510 | event = NTB_EVENT_HW_LINK_UP; | 510 | event = NTB_EVENT_HW_LINK_UP; |
| 511 | 511 | ||
| 512 | if (ndev->hw_type == BWD_HW) | 512 | if (ndev->hw_type == BWD_HW || |
| 513 | ndev->conn_type == NTB_CONN_TRANSPARENT) | ||
| 513 | status = readw(ndev->reg_ofs.lnk_stat); | 514 | status = readw(ndev->reg_ofs.lnk_stat); |
| 514 | else { | 515 | else { |
| 515 | int rc = pci_read_config_word(ndev->pdev, | 516 | int rc = pci_read_config_word(ndev->pdev, |
| @@ -649,119 +650,174 @@ static int ntb_xeon_setup(struct ntb_device *ndev) | |||
| 649 | if (rc) | 650 | if (rc) |
| 650 | return rc; | 651 | return rc; |
| 651 | 652 | ||
| 652 | switch (val & SNB_PPD_CONN_TYPE) { | ||
| 653 | case NTB_CONN_B2B: | ||
| 654 | ndev->conn_type = NTB_CONN_B2B; | ||
| 655 | break; | ||
| 656 | case NTB_CONN_CLASSIC: | ||
| 657 | case NTB_CONN_RP: | ||
| 658 | default: | ||
| 659 | dev_err(&ndev->pdev->dev, "Only B2B supported at this time\n"); | ||
| 660 | return -EINVAL; | ||
| 661 | } | ||
| 662 | |||
| 663 | if (val & SNB_PPD_DEV_TYPE) | 653 | if (val & SNB_PPD_DEV_TYPE) |
| 664 | ndev->dev_type = NTB_DEV_USD; | 654 | ndev->dev_type = NTB_DEV_USD; |
| 665 | else | 655 | else |
| 666 | ndev->dev_type = NTB_DEV_DSD; | 656 | ndev->dev_type = NTB_DEV_DSD; |
| 667 | 657 | ||
| 668 | ndev->reg_ofs.ldb = ndev->reg_base + SNB_PDOORBELL_OFFSET; | 658 | switch (val & SNB_PPD_CONN_TYPE) { |
| 669 | ndev->reg_ofs.ldb_mask = ndev->reg_base + SNB_PDBMSK_OFFSET; | 659 | case NTB_CONN_B2B: |
| 670 | ndev->reg_ofs.bar2_xlat = ndev->reg_base + SNB_SBAR2XLAT_OFFSET; | 660 | dev_info(&ndev->pdev->dev, "Conn Type = B2B\n"); |
| 671 | ndev->reg_ofs.bar4_xlat = ndev->reg_base + SNB_SBAR4XLAT_OFFSET; | 661 | ndev->conn_type = NTB_CONN_B2B; |
| 672 | ndev->reg_ofs.lnk_cntl = ndev->reg_base + SNB_NTBCNTL_OFFSET; | 662 | ndev->reg_ofs.ldb = ndev->reg_base + SNB_PDOORBELL_OFFSET; |
| 673 | ndev->reg_ofs.lnk_stat = ndev->reg_base + SNB_LINK_STATUS_OFFSET; | 663 | ndev->reg_ofs.ldb_mask = ndev->reg_base + SNB_PDBMSK_OFFSET; |
| 674 | ndev->reg_ofs.spad_read = ndev->reg_base + SNB_SPAD_OFFSET; | 664 | ndev->reg_ofs.spad_read = ndev->reg_base + SNB_SPAD_OFFSET; |
| 675 | ndev->reg_ofs.spci_cmd = ndev->reg_base + SNB_PCICMD_OFFSET; | 665 | ndev->reg_ofs.bar2_xlat = ndev->reg_base + SNB_SBAR2XLAT_OFFSET; |
| 676 | 666 | ndev->reg_ofs.bar4_xlat = ndev->reg_base + SNB_SBAR4XLAT_OFFSET; | |
| 677 | /* There is a Xeon hardware errata related to writes to | 667 | ndev->limits.max_spads = SNB_MAX_B2B_SPADS; |
| 678 | * SDOORBELL or B2BDOORBELL in conjunction with inbound access | 668 | |
| 679 | * to NTB MMIO Space, which may hang the system. To workaround | 669 | /* There is a Xeon hardware errata related to writes to |
| 680 | * this use the second memory window to access the interrupt and | 670 | * SDOORBELL or B2BDOORBELL in conjunction with inbound access |
| 681 | * scratch pad registers on the remote system. | 671 | * to NTB MMIO Space, which may hang the system. To workaround |
| 682 | */ | 672 | * this use the second memory window to access the interrupt and |
| 683 | if (xeon_errata_workaround) { | 673 | * scratch pad registers on the remote system. |
| 684 | if (!ndev->mw[1].bar_sz) | ||
| 685 | return -EINVAL; | ||
| 686 | |||
| 687 | ndev->limits.max_mw = SNB_ERRATA_MAX_MW; | ||
| 688 | ndev->reg_ofs.spad_write = ndev->mw[1].vbase + | ||
| 689 | SNB_SPAD_OFFSET; | ||
| 690 | ndev->reg_ofs.rdb = ndev->mw[1].vbase + | ||
| 691 | SNB_PDOORBELL_OFFSET; | ||
| 692 | |||
| 693 | /* Set the Limit register to 4k, the minimum size, to | ||
| 694 | * prevent an illegal access | ||
| 695 | */ | 674 | */ |
| 696 | writeq(ndev->mw[1].bar_sz + 0x1000, ndev->reg_base + | 675 | if (xeon_errata_workaround) { |
| 697 | SNB_PBAR4LMT_OFFSET); | 676 | if (!ndev->mw[1].bar_sz) |
| 698 | } else { | 677 | return -EINVAL; |
| 699 | ndev->limits.max_mw = SNB_MAX_MW; | 678 | |
| 700 | ndev->reg_ofs.spad_write = ndev->reg_base + | 679 | ndev->limits.max_mw = SNB_ERRATA_MAX_MW; |
| 701 | SNB_B2B_SPAD_OFFSET; | 680 | ndev->reg_ofs.spad_write = ndev->mw[1].vbase + |
| 702 | ndev->reg_ofs.rdb = ndev->reg_base + | 681 | SNB_SPAD_OFFSET; |
| 703 | SNB_B2B_DOORBELL_OFFSET; | 682 | ndev->reg_ofs.rdb = ndev->mw[1].vbase + |
| 683 | SNB_PDOORBELL_OFFSET; | ||
| 684 | |||
| 685 | /* Set the Limit register to 4k, the minimum size, to | ||
| 686 | * prevent an illegal access | ||
| 687 | */ | ||
| 688 | writeq(ndev->mw[1].bar_sz + 0x1000, ndev->reg_base + | ||
| 689 | SNB_PBAR4LMT_OFFSET); | ||
| 690 | } else { | ||
| 691 | ndev->limits.max_mw = SNB_MAX_MW; | ||
| 692 | ndev->reg_ofs.spad_write = ndev->reg_base + | ||
| 693 | SNB_B2B_SPAD_OFFSET; | ||
| 694 | ndev->reg_ofs.rdb = ndev->reg_base + | ||
| 695 | SNB_B2B_DOORBELL_OFFSET; | ||
| 696 | |||
| 697 | /* Disable the Limit register, just incase it is set to | ||
| 698 | * something silly | ||
| 699 | */ | ||
| 700 | writeq(0, ndev->reg_base + SNB_PBAR4LMT_OFFSET); | ||
| 701 | } | ||
| 704 | 702 | ||
| 705 | /* Disable the Limit register, just incase it is set to | 703 | /* The Xeon errata workaround requires setting SBAR Base |
| 706 | * something silly | 704 | * addresses to known values, so that the PBAR XLAT can be |
| 705 | * pointed at SBAR0 of the remote system. | ||
| 707 | */ | 706 | */ |
| 708 | writeq(0, ndev->reg_base + SNB_PBAR4LMT_OFFSET); | 707 | if (ndev->dev_type == NTB_DEV_USD) { |
| 709 | } | 708 | writeq(SNB_MBAR23_DSD_ADDR, ndev->reg_base + |
| 709 | SNB_PBAR2XLAT_OFFSET); | ||
| 710 | if (xeon_errata_workaround) | ||
| 711 | writeq(SNB_MBAR01_DSD_ADDR, ndev->reg_base + | ||
| 712 | SNB_PBAR4XLAT_OFFSET); | ||
| 713 | else { | ||
| 714 | writeq(SNB_MBAR45_DSD_ADDR, ndev->reg_base + | ||
| 715 | SNB_PBAR4XLAT_OFFSET); | ||
| 716 | /* B2B_XLAT_OFFSET is a 64bit register, but can | ||
| 717 | * only take 32bit writes | ||
| 718 | */ | ||
| 719 | writel(SNB_MBAR01_DSD_ADDR & 0xffffffff, | ||
| 720 | ndev->reg_base + SNB_B2B_XLAT_OFFSETL); | ||
| 721 | writel(SNB_MBAR01_DSD_ADDR >> 32, | ||
| 722 | ndev->reg_base + SNB_B2B_XLAT_OFFSETU); | ||
| 723 | } | ||
| 710 | 724 | ||
| 711 | /* The Xeon errata workaround requires setting SBAR Base | 725 | writeq(SNB_MBAR01_USD_ADDR, ndev->reg_base + |
| 712 | * addresses to known values, so that the PBAR XLAT can be | 726 | SNB_SBAR0BASE_OFFSET); |
| 713 | * pointed at SBAR0 of the remote system. | 727 | writeq(SNB_MBAR23_USD_ADDR, ndev->reg_base + |
| 714 | */ | 728 | SNB_SBAR2BASE_OFFSET); |
| 715 | if (ndev->dev_type == NTB_DEV_USD) { | 729 | writeq(SNB_MBAR45_USD_ADDR, ndev->reg_base + |
| 716 | writeq(SNB_MBAR23_DSD_ADDR, ndev->reg_base + | 730 | SNB_SBAR4BASE_OFFSET); |
| 717 | SNB_PBAR2XLAT_OFFSET); | 731 | } else { |
| 718 | if (xeon_errata_workaround) | 732 | writeq(SNB_MBAR23_USD_ADDR, ndev->reg_base + |
| 733 | SNB_PBAR2XLAT_OFFSET); | ||
| 734 | if (xeon_errata_workaround) | ||
| 735 | writeq(SNB_MBAR01_USD_ADDR, ndev->reg_base + | ||
| 736 | SNB_PBAR4XLAT_OFFSET); | ||
| 737 | else { | ||
| 738 | writeq(SNB_MBAR45_USD_ADDR, ndev->reg_base + | ||
| 739 | SNB_PBAR4XLAT_OFFSET); | ||
| 740 | /* B2B_XLAT_OFFSET is a 64bit register, but can | ||
| 741 | * only take 32bit writes | ||
| 742 | */ | ||
| 743 | writel(SNB_MBAR01_DSD_ADDR & 0xffffffff, | ||
| 744 | ndev->reg_base + SNB_B2B_XLAT_OFFSETL); | ||
| 745 | writel(SNB_MBAR01_USD_ADDR >> 32, | ||
| 746 | ndev->reg_base + SNB_B2B_XLAT_OFFSETU); | ||
| 747 | } | ||
| 719 | writeq(SNB_MBAR01_DSD_ADDR, ndev->reg_base + | 748 | writeq(SNB_MBAR01_DSD_ADDR, ndev->reg_base + |
| 720 | SNB_PBAR4XLAT_OFFSET); | 749 | SNB_SBAR0BASE_OFFSET); |
| 721 | else { | 750 | writeq(SNB_MBAR23_DSD_ADDR, ndev->reg_base + |
| 751 | SNB_SBAR2BASE_OFFSET); | ||
| 722 | writeq(SNB_MBAR45_DSD_ADDR, ndev->reg_base + | 752 | writeq(SNB_MBAR45_DSD_ADDR, ndev->reg_base + |
| 723 | SNB_PBAR4XLAT_OFFSET); | ||
