diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-19 23:58:22 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-19 23:58:22 +0400 |
commit | 61ed53deb1c6a4386d8710dbbfcee8779c381931 (patch) | |
tree | e39a4f156366cfbd66e64d9a2094da1d3920d2b3 /drivers | |
parent | 278f1d0730f4d0acdfc64256ad9b1066d0f3ab57 (diff) | |
parent | ab760a0c5667519b375ea9c5ab3a23501c4817ef (diff) | |
download | linux-61ed53deb1c6a4386d8710dbbfcee8779c381931.tar.xz |
Merge tag 'ntb-3.18' of git://github.com/jonmason/ntb
Pull ntb (non-transparent bridge) updates from Jon Mason:
"Add support for Haswell NTB split BARs, a debugfs entry for basic
debugging info, and some code clean-ups"
* tag 'ntb-3.18' of git://github.com/jonmason/ntb:
ntb: Adding split BAR support for Haswell platforms
ntb: use errata flag set via DID to implement workaround
ntb: conslidate reading of PPD to move platform detection earlier
ntb: move platform detection to separate function
NTB: debugfs device entry
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/ntb/ntb_hw.c | 567 | ||||
-rw-r--r-- | drivers/ntb/ntb_hw.h | 19 | ||||
-rw-r--r-- | drivers/ntb/ntb_regs.h | 38 |
3 files changed, 501 insertions, 123 deletions
diff --git a/drivers/ntb/ntb_hw.c b/drivers/ntb/ntb_hw.c index 372e08c4ffef..cd29b1038c5e 100644 --- a/drivers/ntb/ntb_hw.c +++ b/drivers/ntb/ntb_hw.c @@ -64,10 +64,6 @@ MODULE_VERSION(NTB_VER); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Intel Corporation"); -static bool xeon_errata_workaround = true; -module_param(xeon_errata_workaround, bool, 0644); -MODULE_PARM_DESC(xeon_errata_workaround, "Workaround for the Xeon Errata"); - enum { NTB_CONN_TRANSPARENT = 0, NTB_CONN_B2B, @@ -88,8 +84,8 @@ static struct dentry *debugfs_dir; #define BWD_LINK_RECOVERY_TIME 500 -/* Translate memory window 0,1 to BAR 2,4 */ -#define MW_TO_BAR(mw) (mw * NTB_MAX_NUM_MW + 2) +/* Translate memory window 0,1,2 to BAR 2,4,5 */ +#define MW_TO_BAR(mw) (mw == 0 ? 2 : (mw == 1 ? 4 : 5)) static const struct pci_device_id ntb_pci_tbl[] = { {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_BWD)}, @@ -109,6 +105,65 @@ static const struct pci_device_id ntb_pci_tbl[] = { }; MODULE_DEVICE_TABLE(pci, ntb_pci_tbl); +static int is_ntb_xeon(struct ntb_device *ndev) +{ + switch (ndev->pdev->device) { + case PCI_DEVICE_ID_INTEL_NTB_SS_JSF: + case PCI_DEVICE_ID_INTEL_NTB_SS_SNB: + case PCI_DEVICE_ID_INTEL_NTB_SS_IVT: + case PCI_DEVICE_ID_INTEL_NTB_SS_HSX: + case PCI_DEVICE_ID_INTEL_NTB_PS_JSF: + case PCI_DEVICE_ID_INTEL_NTB_PS_SNB: + case PCI_DEVICE_ID_INTEL_NTB_PS_IVT: + case PCI_DEVICE_ID_INTEL_NTB_PS_HSX: + case PCI_DEVICE_ID_INTEL_NTB_B2B_JSF: + case PCI_DEVICE_ID_INTEL_NTB_B2B_SNB: + case PCI_DEVICE_ID_INTEL_NTB_B2B_IVT: + case PCI_DEVICE_ID_INTEL_NTB_B2B_HSX: + return 1; + default: + return 0; + } + + return 0; +} + +static int is_ntb_atom(struct ntb_device *ndev) +{ + switch (ndev->pdev->device) { + case PCI_DEVICE_ID_INTEL_NTB_B2B_BWD: + return 1; + default: + return 0; + } + + return 0; +} + +static void ntb_set_errata_flags(struct ntb_device *ndev) +{ + switch (ndev->pdev->device) { + /* + * this workaround applies to all platform up to IvyBridge + * Haswell has splitbar support and use a different workaround + */ + case PCI_DEVICE_ID_INTEL_NTB_SS_JSF: + case PCI_DEVICE_ID_INTEL_NTB_SS_SNB: + case PCI_DEVICE_ID_INTEL_NTB_SS_IVT: + case PCI_DEVICE_ID_INTEL_NTB_SS_HSX: + case PCI_DEVICE_ID_INTEL_NTB_PS_JSF: + case PCI_DEVICE_ID_INTEL_NTB_PS_SNB: + case PCI_DEVICE_ID_INTEL_NTB_PS_IVT: + case PCI_DEVICE_ID_INTEL_NTB_PS_HSX: + case PCI_DEVICE_ID_INTEL_NTB_B2B_JSF: + case PCI_DEVICE_ID_INTEL_NTB_B2B_SNB: + case PCI_DEVICE_ID_INTEL_NTB_B2B_IVT: + case PCI_DEVICE_ID_INTEL_NTB_B2B_HSX: + ndev->wa_flags |= WA_SNB_ERR; + break; + } +} + /** * ntb_register_event_callback() - register event callback * @ndev: pointer to ntb_device instance @@ -451,8 +506,14 @@ void ntb_set_mw_addr(struct ntb_device *ndev, unsigned int mw, u64 addr) case NTB_BAR_23: writeq(addr, ndev->reg_ofs.bar2_xlat); break; - case NTB_BAR_45: - writeq(addr, ndev->reg_ofs.bar4_xlat); + case NTB_BAR_4: + if (ndev->split_bar) + writel(addr, ndev->reg_ofs.bar4_xlat); + else + writeq(addr, ndev->reg_ofs.bar4_xlat); + break; + case NTB_BAR_5: + writel(addr, ndev->reg_ofs.bar5_xlat); break; } } @@ -535,7 +596,7 @@ static void ntb_link_event(struct ntb_device *ndev, int link_state) ndev->link_status = NTB_LINK_UP; event = NTB_EVENT_HW_LINK_UP; - if (ndev->hw_type == BWD_HW || + if (is_ntb_atom(ndev) || ndev->conn_type == NTB_CONN_TRANSPARENT) status = readw(ndev->reg_ofs.lnk_stat); else { @@ -566,7 +627,7 @@ static int ntb_link_status(struct ntb_device *ndev) { int link_state; - if (ndev->hw_type == BWD_HW) { + if (is_ntb_atom(ndev)) { u32 ntb_cntl; ntb_cntl = readl(ndev->reg_ofs.lnk_cntl); @@ -667,29 +728,16 @@ static void bwd_link_poll(struct work_struct *work) static int ntb_xeon_setup(struct ntb_device *ndev) { - int rc; - u8 val; - - ndev->hw_type = SNB_HW; - - rc = pci_read_config_byte(ndev->pdev, NTB_PPD_OFFSET, &val); - if (rc) - return rc; - - if (val & SNB_PPD_DEV_TYPE) - ndev->dev_type = NTB_DEV_USD; - else - ndev->dev_type = NTB_DEV_DSD; - - switch (val & SNB_PPD_CONN_TYPE) { + switch (ndev->conn_type) { case NTB_CONN_B2B: - dev_info(&ndev->pdev->dev, "Conn Type = B2B\n"); - ndev->conn_type = NTB_CONN_B2B; ndev->reg_ofs.ldb = ndev->reg_base + SNB_PDOORBELL_OFFSET; ndev->reg_ofs.ldb_mask = ndev->reg_base + SNB_PDBMSK_OFFSET; ndev->reg_ofs.spad_read = ndev->reg_base + SNB_SPAD_OFFSET; ndev->reg_ofs.bar2_xlat = ndev->reg_base + SNB_SBAR2XLAT_OFFSET; ndev->reg_ofs.bar4_xlat = ndev->reg_base + SNB_SBAR4XLAT_OFFSET; + if (ndev->split_bar) + ndev->reg_ofs.bar5_xlat = + ndev->reg_base + SNB_SBAR5XLAT_OFFSET; ndev->limits.max_spads = SNB_MAX_B2B_SPADS; /* There is a Xeon hardware errata related to writes to @@ -698,16 +746,17 @@ static int ntb_xeon_setup(struct ntb_device *ndev) * this use the second memory window to access the interrupt and * scratch pad registers on the remote system. */ - if (xeon_errata_workaround) { - if (!ndev->mw[1].bar_sz) + if (ndev->wa_flags & WA_SNB_ERR) { + if (!ndev->mw[ndev->limits.max_mw - 1].bar_sz) return -EINVAL; - ndev->limits.max_mw = SNB_ERRATA_MAX_MW; ndev->limits.max_db_bits = SNB_MAX_DB_BITS; - ndev->reg_ofs.spad_write = ndev->mw[1].vbase + - SNB_SPAD_OFFSET; - ndev->reg_ofs.rdb = ndev->mw[1].vbase + - SNB_PDOORBELL_OFFSET; + ndev->reg_ofs.spad_write = + ndev->mw[ndev->limits.max_mw - 1].vbase + + SNB_SPAD_OFFSET; + ndev->reg_ofs.rdb = + ndev->mw[ndev->limits.max_mw - 1].vbase + + SNB_PDOORBELL_OFFSET; /* Set the Limit register to 4k, the minimum size, to * prevent an illegal access @@ -720,9 +769,9 @@ static int ntb_xeon_setup(struct ntb_device *ndev) * the driver defaults, but write the Limit registers * first just in case. */ - } else { - ndev->limits.max_mw = SNB_MAX_MW; + ndev->limits.max_mw = SNB_ERRATA_MAX_MW; + } else { /* HW Errata on bit 14 of b2bdoorbell register. Writes * will not be mirrored to the remote system. Shrink * the number of bits by one, since bit 14 is the last @@ -735,7 +784,8 @@ static int ntb_xeon_setup(struct ntb_device *ndev) SNB_B2B_DOORBELL_OFFSET; /* Disable the Limit register, just incase it is set to - * something silly + * something silly. A 64bit write should handle it + * regardless of whether it has a split BAR or not. */ writeq(0, ndev->reg_base + SNB_PBAR4LMT_OFFSET); /* HW errata on the Limit registers. They can only be @@ -744,6 +794,10 @@ static int ntb_xeon_setup(struct ntb_device *ndev) * the driver defaults, but write the Limit registers * first just in case. */ + if (ndev->split_bar) + ndev->limits.max_mw = HSX_SPLITBAR_MAX_MW; + else + ndev->limits.max_mw = SNB_MAX_MW; } /* The Xeon errata workaround requires setting SBAR Base @@ -753,12 +807,22 @@ static int ntb_xeon_setup(struct ntb_device *ndev) if (ndev->dev_type == NTB_DEV_USD) { writeq(SNB_MBAR23_DSD_ADDR, ndev->reg_base + SNB_PBAR2XLAT_OFFSET); - if (xeon_errata_workaround) + if (ndev->wa_flags & WA_SNB_ERR) writeq(SNB_MBAR01_DSD_ADDR, ndev->reg_base + SNB_PBAR4XLAT_OFFSET); else { - writeq(SNB_MBAR45_DSD_ADDR, ndev->reg_base + - SNB_PBAR4XLAT_OFFSET); + if (ndev->split_bar) { + writel(SNB_MBAR4_DSD_ADDR, + ndev->reg_base + + SNB_PBAR4XLAT_OFFSET); + writel(SNB_MBAR5_DSD_ADDR, + ndev->reg_base + + SNB_PBAR5XLAT_OFFSET); + } else + writeq(SNB_MBAR4_DSD_ADDR, + ndev->reg_base + + SNB_PBAR4XLAT_OFFSET); + /* B2B_XLAT_OFFSET is a 64bit register, but can * only take 32bit writes */ @@ -772,18 +836,35 @@ static int ntb_xeon_setup(struct ntb_device *ndev) SNB_SBAR0BASE_OFFSET); writeq(SNB_MBAR23_USD_ADDR, ndev->reg_base + SNB_SBAR2BASE_OFFSET); - writeq(SNB_MBAR45_USD_ADDR, ndev->reg_base + - SNB_SBAR4BASE_OFFSET); + if (ndev->split_bar) { + writel(SNB_MBAR4_USD_ADDR, ndev->reg_base + + SNB_SBAR4BASE_OFFSET); + writel(SNB_MBAR5_USD_ADDR, ndev->reg_base + + SNB_SBAR5BASE_OFFSET); + } else + writeq(SNB_MBAR4_USD_ADDR, ndev->reg_base + + SNB_SBAR4BASE_OFFSET); } else { writeq(SNB_MBAR23_USD_ADDR, ndev->reg_base + SNB_PBAR2XLAT_OFFSET); - if (xeon_errata_workaround) + if (ndev->wa_flags & WA_SNB_ERR) writeq(SNB_MBAR01_USD_ADDR, ndev->reg_base + SNB_PBAR4XLAT_OFFSET); else { - writeq(SNB_MBAR45_USD_ADDR, ndev->reg_base + - SNB_PBAR4XLAT_OFFSET); - /* B2B_XLAT_OFFSET is a 64bit register, but can + if (ndev->split_bar) { + writel(SNB_MBAR4_USD_ADDR, + ndev->reg_base + + SNB_PBAR4XLAT_OFFSET); + writel(SNB_MBAR5_USD_ADDR, + ndev->reg_base + + SNB_PBAR5XLAT_OFFSET); + } else + writeq(SNB_MBAR4_USD_ADDR, + ndev->reg_base + + SNB_PBAR4XLAT_OFFSET); + + /* + * B2B_XLAT_OFFSET is a 64bit register, but can * only take 32bit writes */ writel(SNB_MBAR01_USD_ADDR & 0xffffffff, @@ -795,17 +876,21 @@ static int ntb_xeon_setup(struct ntb_device *ndev) SNB_SBAR0BASE_OFFSET); writeq(SNB_MBAR23_DSD_ADDR, ndev->reg_base + SNB_SBAR2BASE_OFFSET); - writeq(SNB_MBAR45_DSD_ADDR, ndev->reg_base + - SNB_SBAR4BASE_OFFSET); + if (ndev->split_bar) { + writel(SNB_MBAR4_DSD_ADDR, ndev->reg_base + + SNB_SBAR4BASE_OFFSET); + writel(SNB_MBAR5_DSD_ADDR, ndev->reg_base + + SNB_SBAR5BASE_OFFSET); + } else + writeq(SNB_MBAR4_DSD_ADDR, ndev->reg_base + + SNB_SBAR4BASE_OFFSET); + } break; case NTB_CONN_RP: - dev_info(&ndev->pdev->dev, "Conn Type = RP\n"); - ndev->conn_type = NTB_CONN_RP; - - if (xeon_errata_workaround) { + if (ndev->wa_flags & WA_SNB_ERR) { dev_err(&ndev->pdev->dev, - "NTB-RP disabled due to hardware errata. To disregard this warning and potentially lock-up the system, add the parameter 'xeon_errata_workaround=0'.\n"); + "NTB-RP disabled due to hardware errata.\n"); return -EINVAL; } @@ -829,11 +914,20 @@ static int ntb_xeon_setup(struct ntb_device *ndev) ndev->reg_ofs.spad_read = ndev->reg_base + SNB_SPAD_OFFSET; ndev->reg_ofs.bar2_xlat = ndev->reg_base + SNB_SBAR2XLAT_OFFSET; ndev->reg_ofs.bar4_xlat = ndev->reg_base + SNB_SBAR4XLAT_OFFSET; - ndev->limits.max_mw = SNB_MAX_MW; + if (ndev->split_bar) { + ndev->reg_ofs.bar5_xlat = + ndev->reg_base + SNB_SBAR5XLAT_OFFSET; + ndev->limits.max_mw = HSX_SPLITBAR_MAX_MW; + } else + ndev->limits.max_mw = SNB_MAX_MW; break; case NTB_CONN_TRANSPARENT: - dev_info(&ndev->pdev->dev, "Conn Type = TRANSPARENT\n"); - ndev->conn_type = NTB_CONN_TRANSPARENT; + if (ndev->wa_flags & WA_SNB_ERR) { + dev_err(&ndev->pdev->dev, + "NTB-TRANSPARENT disabled due to hardware errata.\n"); + return -EINVAL; + } + /* Scratch pads need to have exclusive access from the primary * or secondary side. Halve the num spads so that each side can * have an equal amount. @@ -852,13 +946,18 @@ static int ntb_xeon_setup(struct ntb_device *ndev) ndev->reg_ofs.bar2_xlat = ndev->reg_base + SNB_PBAR2XLAT_OFFSET; ndev->reg_ofs.bar4_xlat = ndev->reg_base + SNB_PBAR4XLAT_OFFSET; - ndev->limits.max_mw = SNB_MAX_MW; + if (ndev->split_bar) { + ndev->reg_ofs.bar5_xlat = + ndev->reg_base + SNB_PBAR5XLAT_OFFSET; + ndev->limits.max_mw = HSX_SPLITBAR_MAX_MW; + } else + ndev->limits.max_mw = SNB_MAX_MW; break; default: - /* Most likely caused by the remote NTB-RP device not being - * configured + /* + * we should never hit this. the detect function should've + * take cared of everything. */ - dev_err(&ndev->pdev->dev, "Unknown PPD %x\n", val); return -EINVAL; } @@ -932,34 +1031,16 @@ static int ntb_device_setup(struct ntb_device *ndev) { int rc; - switch (ndev->pdev->device) { - case PCI_DEVICE_ID_INTEL_NTB_SS_JSF: - case PCI_DEVICE_ID_INTEL_NTB_SS_SNB: - case PCI_DEVICE_ID_INTEL_NTB_SS_IVT: - case PCI_DEVICE_ID_INTEL_NTB_SS_HSX: - case PCI_DEVICE_ID_INTEL_NTB_PS_JSF: - case PCI_DEVICE_ID_INTEL_NTB_PS_SNB: - case PCI_DEVICE_ID_INTEL_NTB_PS_IVT: - case PCI_DEVICE_ID_INTEL_NTB_PS_HSX: - case PCI_DEVICE_ID_INTEL_NTB_B2B_JSF: - case PCI_DEVICE_ID_INTEL_NTB_B2B_SNB: - case PCI_DEVICE_ID_INTEL_NTB_B2B_IVT: - case PCI_DEVICE_ID_INTEL_NTB_B2B_HSX: + if (is_ntb_xeon(ndev)) rc = ntb_xeon_setup(ndev); - break; - case PCI_DEVICE_ID_INTEL_NTB_B2B_BWD: + else if (is_ntb_atom(ndev)) rc = ntb_bwd_setup(ndev); - break; - default: + else rc = -ENODEV; - } if (rc) return rc; - dev_info(&ndev->pdev->dev, "Device Type = %s\n", - ndev->dev_type == NTB_DEV_USD ? "USD/DSP" : "DSD/USP"); - if (ndev->conn_type == NTB_CONN_B2B) /* Enable Bus Master and Memory Space on the secondary side */ writew(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER, @@ -970,7 +1051,7 @@ static int ntb_device_setup(struct ntb_device *ndev) static void ntb_device_free(struct ntb_device *ndev) { - if (ndev->hw_type == BWD_HW) { + if (is_ntb_atom(ndev)) { cancel_delayed_work_sync(&ndev->hb_timer); cancel_delayed_work_sync(&ndev->lr_timer); } @@ -1050,7 +1131,7 @@ static irqreturn_t ntb_interrupt(int irq, void *dev) struct ntb_device *ndev = dev; unsigned int i = 0; - if (ndev->hw_type == BWD_HW) { + if (is_ntb_atom(ndev)) { u64 ldb = readq(ndev->reg_ofs.ldb); dev_dbg(&ndev->pdev->dev, "irq %d - ldb = %Lx\n", irq, ldb); @@ -1192,7 +1273,7 @@ static int ntb_setup_msix(struct ntb_device *ndev) for (i = 0; i < msix_entries; i++) ndev->msix_entries[i].entry = i; - if (ndev->hw_type == BWD_HW) + if (is_ntb_atom(ndev)) rc = ntb_setup_bwd_msix(ndev, msix_entries); else rc = ntb_setup_snb_msix(ndev, msix_entries); @@ -1252,7 +1333,7 @@ static int ntb_setup_interrupts(struct ntb_device *ndev) /* On BWD, disable all interrupts. On SNB, disable all but Link * Interrupt. The rest will be unmasked as callbacks are registered. */ - if (ndev->hw_type == BWD_HW) + if (is_ntb_atom(ndev)) writeq(~0, ndev->reg_ofs.ldb_mask); else { u16 var = 1 << SNB_LINK_DB; @@ -1285,7 +1366,7 @@ static void ntb_free_interrupts(struct ntb_device *ndev) struct pci_dev *pdev = ndev->pdev; /* mask interrupts */ - if (ndev->hw_type == BWD_HW) + if (is_ntb_atom(ndev)) writeq(~0, ndev->reg_ofs.ldb_mask); else writew(~0, ndev->reg_ofs.ldb_mask); @@ -1296,7 +1377,7 @@ static void ntb_free_interrupts(struct ntb_device *ndev) for (i = 0; i < ndev->num_msix; i++) { msix = &ndev->msix_entries[i]; - if (ndev->hw_type != BWD_HW && i == ndev->num_msix - 1) + if (is_ntb_xeon(ndev) && i == ndev->num_msix - 1) free_irq(msix->vector, ndev); else free_irq(msix->vector, &ndev->db_cb[i]); @@ -1344,6 +1425,101 @@ static void ntb_free_callbacks(struct ntb_device *ndev) kfree(ndev->db_cb); } +static ssize_t ntb_debugfs_read(struct file *filp, char __user *ubuf, + size_t count, loff_t *offp) +{ + struct ntb_device *ndev; + char *buf; + ssize_t ret, offset, out_count; + + out_count = 500; + + buf = kmalloc(out_count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ndev = filp->private_data; + offset = 0; + offset += snprintf(buf + offset, out_count - offset, + "NTB Device Information:\n"); + offset += snprintf(buf + offset, out_count - offset, + "Connection Type - \t\t%s\n", + ndev->conn_type == NTB_CONN_TRANSPARENT ? + "Transparent" : (ndev->conn_type == NTB_CONN_B2B) ? + "Back to back" : "Root Port"); + offset += snprintf(buf + offset, out_count - offset, + "Device Type - \t\t\t%s\n", + ndev->dev_type == NTB_DEV_USD ? + "DSD/USP" : "USD/DSP"); + offset += snprintf(buf + offset, out_count - offset, + "Max Number of Callbacks - \t%u\n", + ntb_max_cbs(ndev)); + offset += snprintf(buf + offset, out_count - offset, + "Link Status - \t\t\t%s\n", + ntb_hw_link_status(ndev) ? "Up" : "Down"); + if (ntb_hw_link_status(ndev)) { + offset += snprintf(buf + offset, out_count - offset, + "Link Speed - \t\t\tPCI-E Gen %u\n", + ndev->link_speed); + offset += snprintf(buf + offset, out_count - offset, + "Link Width - \t\t\tx%u\n", + ndev->link_width); + } + + if (is_ntb_xeon(ndev)) { + u32 status32; + u16 status16; + int rc; + + offset += snprintf(buf + offset, out_count - offset, + "\nNTB Device Statistics:\n"); + offset += snprintf(buf + offset, out_count - offset, + "Upstream Memory Miss - \t%u\n", + readw(ndev->reg_base + + SNB_USMEMMISS_OFFSET)); + + offset += snprintf(buf + offset, out_count - offset, + "\nNTB Hardware Errors:\n"); + + rc = pci_read_config_word(ndev->pdev, SNB_DEVSTS_OFFSET, + &status16); + if (!rc) + offset += snprintf(buf + offset, out_count - offset, + "DEVSTS - \t%#06x\n", status16); + + rc = pci_read_config_word(ndev->pdev, SNB_LINK_STATUS_OFFSET, + &status16); + if (!rc) + offset += snprintf(buf + offset, out_count - offset, + "LNKSTS - \t%#06x\n", status16); + + rc = pci_read_config_dword(ndev->pdev, SNB_UNCERRSTS_OFFSET, + &status32); + if (!rc) + offset += snprintf(buf + offset, out_count - offset, + "UNCERRSTS - \t%#010x\n", status32); + + rc = pci_read_config_dword(ndev->pdev, SNB_CORERRSTS_OFFSET, + &status32); + if (!rc) + offset += snprintf(buf + offset, out_count - offset, + "CORERRSTS - \t%#010x\n", status32); + } + + if (offset > out_count) + offset = out_count; + + ret = simple_read_from_buffer(ubuf, count, offp, buf, offset); + kfree(buf); + return ret; +} + +static const struct file_operations ntb_debugfs_info = { + .owner = THIS_MODULE, + .open = simple_open, + .read = ntb_debugfs_read, +}; + static void ntb_setup_debugfs(struct ntb_device *ndev) { if (!debugfs_initialized()) @@ -1354,6 +1530,11 @@ static void ntb_setup_debugfs(struct ntb_device *ndev) ndev->debugfs_dir = debugfs_create_dir(pci_name(ndev->pdev), debugfs_dir); + if (ndev->debugfs_dir) + ndev->debugfs_info = debugfs_create_file("info", S_IRUSR, + ndev->debugfs_dir, + ndev, + &ntb_debugfs_info); } static void ntb_free_debugfs(struct ntb_device *ndev) @@ -1377,7 +1558,11 @@ static void ntb_hw_link_up(struct ntb_device *ndev) ntb_cntl = readl(ndev->reg_ofs.lnk_cntl); ntb_cntl &= ~(NTB_CNTL_LINK_DISABLE | NTB_CNTL_CFG_LOCK); ntb_cntl |= NTB_CNTL_P2S_BAR23_SNOOP | NTB_CNTL_S2P_BAR23_SNOOP; - ntb_cntl |= NTB_CNTL_P2S_BAR45_SNOOP | NTB_CNTL_S2P_BAR45_SNOOP; + ntb_cntl |= NTB_CNTL_P2S_BAR4_SNOOP | NTB_CNTL_S2P_BAR4_SNOOP; + if (ndev->split_bar) + ntb_cntl |= NTB_CNTL_P2S_BAR5_SNOOP | + NTB_CNTL_S2P_BAR5_SNOOP; + writel(ntb_cntl, ndev->reg_ofs.lnk_cntl); } } @@ -1394,11 +1579,128 @@ static void ntb_hw_link_down(struct ntb_device *ndev) /* Bring NTB link down */ ntb_cntl = readl(ndev->reg_ofs.lnk_cntl); ntb_cntl &= ~(NTB_CNTL_P2S_BAR23_SNOOP | NTB_CNTL_S2P_BAR23_SNOOP); - ntb_cntl &= ~(NTB_CNTL_P2S_BAR45_SNOOP | NTB_CNTL_S2P_BAR45_SNOOP); + ntb_cntl &= ~(NTB_CNTL_P2S_BAR4_SNOOP | NTB_CNTL_S2P_BAR4_SNOOP); + if (ndev->split_bar) + ntb_cntl &= ~(NTB_CNTL_P2S_BAR5_SNOOP | + NTB_CNTL_S2P_BAR5_SNOOP); ntb_cntl |= NTB_CNTL_LINK_DISABLE | NTB_CNTL_CFG_LOCK; writel(ntb_cntl, ndev->reg_ofs.lnk_cntl); } +static void ntb_max_mw_detect(struct ntb_device *ndev) +{ + if (ndev->split_bar) + ndev->limits.max_mw = HSX_SPLITBAR_MAX_MW; + else + ndev->limits.max_mw = SNB_MAX_MW; +} + +static int ntb_xeon_detect(struct ntb_device *ndev) +{ + int rc, bars_mask; + u32 bars; + u8 ppd; + + ndev->hw_type = SNB_HW; + + rc = pci_read_config_byte(ndev->pdev, NTB_PPD_OFFSET, &ppd); + if (rc) + return -EIO; + + if (ppd & SNB_PPD_DEV_TYPE) + ndev->dev_type = NTB_DEV_USD; + else + ndev->dev_type = NTB_DEV_DSD; + + ndev->split_bar = (ppd & SNB_PPD_SPLIT_BAR) ? 1 : 0; + + switch (ppd & SNB_PPD_CONN_TYPE) { + case NTB_CONN_B2B: + dev_info(&ndev->pdev->dev, "Conn Type = B2B\n"); + ndev->conn_type = NTB_CONN_B2B; + break; + case NTB_CONN_RP: + dev_info(&ndev->pdev->dev, "Conn Type = RP\n"); + ndev->conn_type = NTB_CONN_RP; + break; + case NTB_CONN_TRANSPARENT: + dev_info(&ndev->pdev->dev, "Conn Type = TRANSPARENT\n"); + ndev->conn_type = NTB_CONN_TRANSPARENT; + /* + * This mode is default to USD/DSP. HW does not report + * properly in transparent mode as it has no knowledge of + * NTB. We will just force correct here. + */ + ndev->dev_type = NTB_DEV_USD; + + /* + * This is a way for transparent BAR to figure out if we + * are doing split BAR or not. There is no way for the hw + * on the transparent side to know and set the PPD. + */ + bars_mask = pci_select_bars(ndev->pdev, IORESOURCE_MEM); + bars = hweight32(bars_mask); + if (bars == (HSX_SPLITBAR_MAX_MW + 1)) + ndev->split_bar = 1; + + break; + default: + dev_err(&ndev->pdev->dev, "Unknown PPD %x\n", ppd); + return -ENODEV; + } + + ntb_max_mw_detect(ndev); + + return 0; +} + +static int ntb_atom_detect(struct ntb_device *ndev) +{ + int rc; + u32 ppd; + + ndev->hw_type = BWD_HW; + + rc = pci_read_config_dword(ndev->pdev, NTB_PPD_OFFSET, &ppd); + if (rc) + return rc; + + switch ((ppd & BWD_PPD_CONN_TYPE) >> 8) { + case NTB_CONN_B2B: + dev_info(&ndev->pdev->dev, "Conn Type = B2B\n"); + ndev->conn_type = NTB_CONN_B2B; + break; + case NTB_CONN_RP: + default: + dev_err(&ndev->pdev->dev, "Unsupported NTB configuration\n"); + return -EINVAL; + } + + if (ppd & BWD_PPD_DEV_TYPE) + ndev->dev_type = NTB_DEV_DSD; + else + ndev->dev_type = NTB_DEV_USD; + + return 0; +} + +static int ntb_device_detect(struct ntb_device *ndev) +{ + int rc; + + if (is_ntb_xeon(ndev)) + rc = ntb_xeon_detect(ndev); + else if (is_ntb_atom(ndev)) + rc = ntb_atom_detect(ndev); + else + rc = -ENODEV; + + dev_info(&ndev->pdev->dev, "Device Type = %s\n", + ndev->dev_type == NTB_DEV_USD ? "USD/DSP" : "DSD/USP"); + + return 0; +} + static int ntb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct ntb_device *ndev; @@ -1409,6 +1711,9 @@ static int ntb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) return -ENOMEM; ndev->pdev = pdev; + + ntb_set_errata_flags(ndev); + ndev->link_status = NTB_LINK_DOWN; pci_set_drvdata(pdev, ndev); ntb_setup_debugfs(ndev); @@ -1419,22 +1724,54 @@ static int ntb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_master(ndev->pdev); - rc = pci_request_selected_regions(pdev, NTB_BAR_MASK, KBUILD_MODNAME); + rc = ntb_device_detect(ndev); if (rc) + goto err; + + ndev->mw = kcalloc(ndev->limits.max_mw, sizeof(struct ntb_mw), + GFP_KERNEL); + if (!ndev->mw) { + rc = -ENOMEM; goto err1; + } + + if (ndev->split_bar) + rc = pci_request_selected_regions(pdev, NTB_SPLITBAR_MASK, + KBUILD_MODNAME); + else + rc = pci_request_selected_regions(pdev, NTB_BAR_MASK, + KBUILD_MODNAME); + + if (rc) + goto err2; ndev->reg_base = pci_ioremap_bar(pdev, NTB_BAR_MMIO); if (!ndev->reg_base) { dev_warn(&pdev->dev, "Cannot remap BAR 0\n"); rc = -EIO; - goto err2; + goto err3; } - for (i = 0; i < NTB_MAX_NUM_MW; i++) { + for (i = 0; i < ndev->limits.max_mw; i++) { ndev->mw[i].bar_sz = pci_resource_len(pdev, MW_TO_BAR(i)); - ndev->mw[i].vbase = - ioremap_wc(pci_resource_start(pdev, MW_TO_BAR(i)), - ndev->mw[i].bar_sz); + + /* + * with the errata we need to steal last of the memory + * windows for workarounds and they point to MMIO registers. + */ + if ((ndev->wa_flags & WA_SNB_ERR) && + (i == (ndev->limits.max_mw - 1))) { + ndev->mw[i].vbase = + ioremap_nocache(pci_resource_start(pdev, + MW_TO_BAR(i)), + ndev->mw[i].bar_sz); + } else { + ndev->mw[i].vbase = + ioremap_wc(pci_resource_start(pdev, + MW_TO_BAR(i)), + ndev->mw[i].bar_sz); + } + dev_info(&pdev->dev, "MW %d size %llu\n", i, (unsigned long long) ndev->mw[i].bar_sz); if (!ndev->mw[i].vbase) { @@ -1449,7 +1786,7 @@ static int ntb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (rc) { rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (rc) - goto err3; + goto err4; dev_warn(&pdev->dev, "Cannot DMA highmem\n"); } @@ -1458,22 +1795,22 @@ static int ntb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (rc) { rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); if (rc) - goto err3; + goto err4; dev_warn(&pdev->dev, "Cannot DMA consistent highmem\n"); } rc = ntb_device_setup(ndev); if (rc) - goto err3; + goto err4; rc = ntb_create_callbacks(ndev); if (rc) - goto err4; + goto err5; rc = ntb_setup_interrupts(ndev); if (rc) - goto err5; + goto err6; /* The scratchpad registers keep the values between rmmod/insmod, * blast them now @@ -1485,24 +1822,29 @@ static int ntb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) rc = ntb_transport_init(pdev); if (rc) - goto err6; + goto err7; ntb_hw_link_up(ndev); return 0; -err6: +err7: ntb_free_interrupts(ndev); -err5: +err6: ntb_free_callbacks(ndev); -err4: +err5: ntb_device_free(ndev); -err3: +err4: for (i--; i >= 0; i--) iounmap(ndev->mw[i].vbase); iounmap(ndev->reg_base); +err3: + if (ndev->split_bar) + pci_release_selected_regions(pdev, NTB_SPLITBAR_MASK); + else + pci_release_selected_regions(pdev, NTB_BAR_MASK); err2: - pci_release_selected_regions(pdev, NTB_BAR_MASK); + kfree(ndev->mw); err1: pci_disable_device(pdev); err: @@ -1526,11 +1868,19 @@ static void ntb_pci_remove(struct pci_dev *pdev) ntb_free_callbacks(ndev); ntb_device_free(ndev); - for (i = 0; i < NTB_MAX_NUM_MW; i++) + /* need to reset max_mw limits so we can unmap properly */ + if (ndev->hw_type == SNB_HW) + ntb_max_mw_detect(ndev); + + for (i = 0; i < ndev->limits.max_mw; i++) iounmap(ndev->mw[i].vbase); + kfree(ndev->mw); iounmap(ndev->reg_base); - pci_release_selected_regions(pdev, NTB_BAR_MASK); + if (ndev->split_bar) + pci_release_selected_regions(pdev, NTB_SPLITBAR_MASK); + else + pci_release_selected_regions(pdev, NTB_BAR_MASK); pci_disable_device(pdev); ntb_free_debugfs(ndev); kfree(ndev); @@ -1542,4 +1892,5 @@ static struct pci_driver ntb_pci_driver = { .probe = ntb_pci_probe, .remove = ntb_pci_remove, }; + module_pci_driver(ntb_pci_driver); diff --git a/drivers/ntb/ntb_hw.h b/drivers/ntb/ntb_hw.h index 465517b7393e..96de5fc95f90 100644 --- a/drivers/ntb/ntb_hw.h +++ b/drivers/ntb/ntb_hw.h @@ -78,14 +78,16 @@ static inline void writeq(u64 val, void __iomem *addr) #define NTB_BAR_MMIO 0 #define NTB_BAR_23 2 -#define NTB_BAR_45 4 +#define NTB_BAR_4 4 +#define NTB_BAR_5 5 + #define NTB_BAR_MASK ((1 << NTB_BAR_MMIO) | (1 << NTB_BAR_23) |\ - (1 << NTB_BAR_45)) + (1 << NTB_BAR_4)) +#define NTB_SPLITBAR_MASK ((1 << NTB_BAR_MMIO) | (1 << NTB_BAR_23) |\ + (1 << NTB_BAR_4) | (1 << NTB_BAR_5)) #define NTB_HB_TIMEOUT msecs_to_jiffies(1000) -#define NTB_MAX_NUM_MW 2 - enum ntb_hw_event { NTB_EVENT_SW_EVENT0 = 0, NTB_EVENT_SW_EVENT1, @@ -109,11 +111,13 @@ struct ntb_db_cb { struct tasklet_struct irq_work; }; +#define WA_SNB_ERR 0x00000001 + struct ntb_device { struct pci_dev *pdev; struct msix_entry *msix_entries; void __iomem *reg_base; - struct ntb_mw mw[NTB_MAX_NUM_MW]; + struct ntb_mw *mw; struct { unsigned char max_mw; unsigned char max_spads; @@ -126,6 +130,7 @@ struct ntb_device { void __iomem *rdb; void __iomem *bar2_xlat; void __iomem *bar4_xlat; + void __iomem *bar5_xlat; void __iomem *spad_write; void __iomem *spad_read; void __iomem *lnk_cntl; @@ -145,6 +150,7 @@ struct ntb_device { unsigned char link_width; unsigned char link_speed; unsigned char link_status; + unsigned char split_bar; struct delayed_work hb_timer; unsigned long last_ts; @@ -152,6 +158,9 @@ struct ntb_device { struct delayed_work lr_timer; struct dentry *debugfs_dir; + struct dentry *debugfs_info; + + unsigned int wa_flags; }; /** diff --git a/drivers/ntb/ntb_regs.h b/drivers/ntb/ntb_regs.h index 9774506419d7..f028ff81fd77 100644 --- a/drivers/ntb/ntb_regs.h +++ b/drivers/ntb/ntb_regs.h @@ -57,34 +57,43 @@ #define SNB_MAX_DB_BITS 15 #define SNB_LINK_DB 15 #define SNB_DB_BITS_PER_VEC 5 +#define HSX_SPLITBAR_MAX_MW 3 #define SNB_MAX_MW 2 #define SNB_ERRATA_MAX_MW 1 #define SNB_DB_HW_LINK 0x8000 +#define SNB_UNCERRSTS_OFFSET 0x014C +#define SNB_CORERRSTS_OFFSET 0x0158 +#define SNB_LINK_STATUS_OFFSET 0x01A2 #define SNB_PCICMD_OFFSET 0x0504 #define SNB_DEVCTRL_OFFSET 0x0598 +#define SNB_DEVSTS_OFFSET 0x059A #define SNB_SLINK_STATUS_OFFSET 0x05A2 -#define SNB_LINK_STATUS_OFFSET 0x01A2 #define SNB_PBAR2LMT_OFFSET 0x0000 #define SNB_PBAR4LMT_OFFSET 0x0008 +#define SNB_PBAR5LMT_OFFSET 0x000C #define SNB_PBAR2XLAT_OFFSET 0x0010 #define SNB_PBAR4XLAT_OFFSET 0x0018 +#define SNB_PBAR5XLAT_OFFSET 0x001C #define SNB_SBAR2LMT_OFFSET 0x0020 #define SNB_SBAR4LMT_OFFSET 0x0028 +#define SNB_SBAR5LMT_OFFSET 0x002C #define SNB_SBAR2XLAT_OFFSET 0x0030 #define SNB_SBAR4XLAT_OFFSET 0x0038 +#define SNB_SBAR5XLAT_OFFSET 0x003C #define SNB_SBAR0BASE_OFFSET 0x0040 #define SNB_SBAR2BASE_OFFSET 0x0048 #define SNB_SBAR4BASE_OFFSET 0x0050 +#define SNB_SBAR5BASE_OFFSET 0x0054 #define SNB_NTBCNTL_OFFSET 0x0058 #define SNB_SBDF_OFFSET 0x005C #define SNB_PDOORBELL_OFFSET 0x0060 #define SNB_PDBMSK_OFFSET 0x0062 #define SNB_SDOORBELL_OFFSET 0x0064 #define SNB_SDBMSK_OFFSET 0x0066 -#define SNB_USMEMMISS 0x0070 +#define SNB_USMEMMISS_OFFSET 0x0070 #define SNB_SPAD_OFFSET 0x0080 #define SNB_SPADSEMA4_OFFSET 0x00c0 #define SNB_WCCNTRL_OFFSET 0x00e0 @@ -93,12 +102,18 @@ #define SNB_B2B_XLAT_OFFSETL 0x0144 #define SNB_B2B_XLAT_OFFSETU 0x0148 -#define SNB_MBAR01_USD_ADDR 0x000000210000000CULL -#define SNB_MBAR23_USD_ADDR 0x000000410000000CULL -#define SNB_MBAR45_USD_ADDR 0x000000810000000CULL -#define SNB_MBAR01_DSD_ADDR 0x000000200000000CULL -#define SNB_MBAR23_DSD_ADDR 0x000000400000000CULL -#define SNB_MBAR45_DSD_ADDR 0x000000800000000CULL +/* + * The addresses are setup so the 32bit BARs can function. Thus + * the addresses are all in 32bit space + */ +#define SNB_MBAR01_USD_ADDR 0x000000002100000CULL +#define SNB_MBAR23_USD_ADDR 0x000000004100000CULL +#define SNB_MBAR4_USD_ADDR 0x000000008100000CULL +#define SNB_MBAR5_USD_ADDR 0x00000000A100000CULL +#define SNB_MBAR01_DSD_ADDR 0x000000002000000CULL +#define SNB_MBAR23_DSD_ADDR 0x000000004000000CULL +#define SNB_MBAR4_DSD_ADDR 0x000000008000000CULL +#define SNB_MBAR5_DSD_ADDR 0x00000000A000000CULL #define BWD_MSIX_CNT 34 #define BWD_MAX_SPADS 16 @@ -147,13 +162,16 @@ #define NTB_CNTL_LINK_DISABLE (1 << 1) #define NTB_CNTL_S2P_BAR23_SNOOP (1 << 2) #define NTB_CNTL_P2S_BAR23_SNOOP (1 << 4) -#define NTB_CNTL_S2P_BAR45_SNOOP (1 << 6) -#define NTB_CNTL_P2S_BAR45_SNOOP (1 << 8) +#define NTB_CNTL_S2P_BAR4_SNOOP (1 << 6) +#define NTB_CNTL_P2S_BAR4_SNOOP (1 << 8) +#define NTB_CNTL_S2P_BAR5_SNOOP (1 << 12) +#define NTB_CNTL_P2S_BAR5_SNOOP (1 << 14) #define BWD_CNTL_LINK_DOWN (1 << 16) #define NTB_PPD_OFFSET 0x00D4 #define SNB_PPD_CONN_TYPE 0x0003 #define SNB_PPD_DEV_TYPE 0x0010 +#define SNB_PPD_SPLIT_BAR (1 << 6) #define BWD_PPD_INIT_LINK 0x0008 #define BWD_PPD_CONN_TYPE 0x0300 #define BWD_PPD_DEV_TYPE 0x1000 |