X-Git-Url: http://git.cascardo.info/?a=blobdiff_plain;f=drivers%2Fntb%2Fntb_hw.c;h=cd29b1038c5e3bf6f4a21659343c65584c44b969;hb=ab760a0c5667519b375ea9c5ab3a23501c4817ef;hp=372e08c4ffefb76852e4d640e6936f3529e8c215;hpb=639b4ac691c6f6e48921dc576379c176f82f3250;p=cascardo%2Flinux.git 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);