Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
source "drivers/ntb/hw/amd/Kconfig"
source "drivers/ntb/hw/idt/Kconfig"
source "drivers/ntb/hw/intel/Kconfig"
source "drivers/ntb/hw/epf/Kconfig"
source "drivers/ntb/hw/mscc/Kconfig"
+6
View File
@@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_NTB_AMD) += amd/
obj-$(CONFIG_NTB_IDT) += idt/
obj-$(CONFIG_NTB_INTEL) += intel/
obj-$(CONFIG_NTB_EPF) += epf/
obj-$(CONFIG_NTB_SWITCHTEC) += mscc/
+8
View File
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-only
config NTB_AMD
tristate "AMD Non-Transparent Bridge support"
depends on X86_64
help
This driver supports AMD NTB on capable Zeppelin hardware.
If unsure, say N.
+2
View File
@@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_NTB_AMD) += ntb_hw_amd.o
File diff suppressed because it is too large Load Diff
+224
View File
@@ -0,0 +1,224 @@
/*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright (C) 2016 Advanced Micro Devices, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* BSD LICENSE
*
* Copyright (C) 2016 Advanced Micro Devices, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copy
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of AMD Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* AMD PCIe NTB Linux driver
*
* Contact Information:
* Xiangliang Yu <Xiangliang.Yu@amd.com>
*/
#ifndef NTB_HW_AMD_H
#define NTB_HW_AMD_H
#include <linux/ntb.h>
#include <linux/pci.h>
#define AMD_LINK_HB_TIMEOUT msecs_to_jiffies(1000)
#define NTB_LNK_STA_SPEED_MASK 0x000F0000
#define NTB_LNK_STA_WIDTH_MASK 0x03F00000
#define NTB_LNK_STA_SPEED(x) (((x) & NTB_LNK_STA_SPEED_MASK) >> 16)
#define NTB_LNK_STA_WIDTH(x) (((x) & NTB_LNK_STA_WIDTH_MASK) >> 20)
#ifndef read64
#ifdef readq
#define read64 readq
#else
#define read64 _read64
static inline u64 _read64(void __iomem *mmio)
{
u64 low, high;
low = readl(mmio);
high = readl(mmio + sizeof(u32));
return low | (high << 32);
}
#endif
#endif
#ifndef write64
#ifdef writeq
#define write64 writeq
#else
#define write64 _write64
static inline void _write64(u64 val, void __iomem *mmio)
{
writel(val, mmio);
writel(val >> 32, mmio + sizeof(u32));
}
#endif
#endif
enum {
/* AMD NTB Capability */
AMD_DB_CNT = 16,
AMD_MSIX_VECTOR_CNT = 24,
AMD_SPADS_CNT = 16,
/* AMD NTB register offset */
AMD_CNTL_OFFSET = 0x200,
/* NTB control register bits */
PMM_REG_CTL = BIT(21),
SMM_REG_CTL = BIT(20),
SMM_REG_ACC_PATH = BIT(18),
PMM_REG_ACC_PATH = BIT(17),
NTB_CLK_EN = BIT(16),
AMD_STA_OFFSET = 0x204,
AMD_PGSLV_OFFSET = 0x208,
AMD_SPAD_MUX_OFFSET = 0x20C,
AMD_SPAD_OFFSET = 0x210,
AMD_RSMU_HCID = 0x250,
AMD_RSMU_SIID = 0x254,
AMD_PSION_OFFSET = 0x300,
AMD_SSION_OFFSET = 0x330,
AMD_MMINDEX_OFFSET = 0x400,
AMD_MMDATA_OFFSET = 0x404,
AMD_SIDEINFO_OFFSET = 0x408,
AMD_SIDE_MASK = BIT(0),
AMD_SIDE_READY = BIT(1),
/* limit register */
AMD_ROMBARLMT_OFFSET = 0x410,
AMD_BAR1LMT_OFFSET = 0x414,
AMD_BAR23LMT_OFFSET = 0x418,
AMD_BAR45LMT_OFFSET = 0x420,
/* xlat address */
AMD_POMBARXLAT_OFFSET = 0x428,
AMD_BAR1XLAT_OFFSET = 0x430,
AMD_BAR23XLAT_OFFSET = 0x438,
AMD_BAR45XLAT_OFFSET = 0x440,
/* doorbell and interrupt */
AMD_DBFM_OFFSET = 0x450,
AMD_DBREQ_OFFSET = 0x454,
AMD_MIRRDBSTAT_OFFSET = 0x458,
AMD_DBMASK_OFFSET = 0x45C,
AMD_DBSTAT_OFFSET = 0x460,
AMD_INTMASK_OFFSET = 0x470,
AMD_INTSTAT_OFFSET = 0x474,
/* event type */
AMD_PEER_FLUSH_EVENT = BIT(0),
AMD_PEER_RESET_EVENT = BIT(1),
AMD_PEER_D3_EVENT = BIT(2),
AMD_PEER_PMETO_EVENT = BIT(3),
AMD_PEER_D0_EVENT = BIT(4),
AMD_LINK_UP_EVENT = BIT(5),
AMD_LINK_DOWN_EVENT = BIT(6),
AMD_EVENT_INTMASK = (AMD_PEER_FLUSH_EVENT |
AMD_PEER_RESET_EVENT | AMD_PEER_D3_EVENT |
AMD_PEER_PMETO_EVENT | AMD_PEER_D0_EVENT |
AMD_LINK_UP_EVENT | AMD_LINK_DOWN_EVENT),
AMD_PMESTAT_OFFSET = 0x480,
AMD_PMSGTRIG_OFFSET = 0x490,
AMD_LTRLATENCY_OFFSET = 0x494,
AMD_FLUSHTRIG_OFFSET = 0x498,
/* SMU register*/
AMD_SMUACK_OFFSET = 0x4A0,
AMD_SINRST_OFFSET = 0x4A4,
AMD_RSPNUM_OFFSET = 0x4A8,
AMD_SMU_SPADMUTEX = 0x4B0,
AMD_SMU_SPADOFFSET = 0x4B4,
AMD_PEER_OFFSET = 0x400,
};
struct ntb_dev_data {
const unsigned char mw_count;
const unsigned int mw_idx;
const bool is_endpoint;
};
struct amd_ntb_dev;
struct amd_ntb_vec {
struct amd_ntb_dev *ndev;
int num;
};
struct amd_ntb_dev {
struct ntb_dev ntb;
u32 ntb_side;
u32 lnk_sta;
u32 cntl_sta;
u32 peer_sta;
struct ntb_dev_data *dev_data;
unsigned char mw_count;
unsigned char spad_count;
unsigned char db_count;
unsigned char msix_vec_count;
u64 db_valid_mask;
u64 db_mask;
u64 db_last_bit;
u32 int_mask;
struct msix_entry *msix;
struct amd_ntb_vec *vec;
/* synchronize rmw access of db_mask and hw reg */
spinlock_t db_mask_lock;
void __iomem *self_mmio;
void __iomem *peer_mmio;
unsigned int self_spad;
unsigned int peer_spad;
struct delayed_work hb_timer;
struct dentry *debugfs_dir;
struct dentry *debugfs_info;
};
#define ntb_ndev(__ntb) container_of(__ntb, struct amd_ntb_dev, ntb)
#define hb_ndev(__work) container_of(__work, struct amd_ntb_dev, hb_timer.work)
static void amd_set_side_info_reg(struct amd_ntb_dev *ndev, bool peer);
static void amd_clear_side_info_reg(struct amd_ntb_dev *ndev, bool peer);
static int amd_poll_link(struct amd_ntb_dev *ndev);
#endif
+5
View File
@@ -0,0 +1,5 @@
config NTB_EPF
tristate "Generic EPF Non-Transparent Bridge support"
help
This driver supports EPF NTB on configurable endpoint.
If unsure, say N.
+1
View File
@@ -0,0 +1 @@
obj-$(CONFIG_NTB_EPF) += ntb_hw_epf.o
+873
View File
@@ -0,0 +1,873 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Host side endpoint driver to implement Non-Transparent Bridge functionality
*
* Copyright (C) 2020 Texas Instruments
* Author: Kishon Vijay Abraham I <kishon@ti.com>
*/
#include <linux/atomic.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/ntb.h>
#define NTB_EPF_COMMAND 0x0
#define CMD_CONFIGURE_DOORBELL 1
#define CMD_TEARDOWN_DOORBELL 2
#define CMD_CONFIGURE_MW 3
#define CMD_TEARDOWN_MW 4
#define CMD_LINK_UP 5
#define CMD_LINK_DOWN 6
#define NTB_EPF_ARGUMENT 0x4
#define MSIX_ENABLE BIT(16)
#define NTB_EPF_CMD_STATUS 0x8
#define COMMAND_STATUS_OK 1
#define COMMAND_STATUS_ERROR 2
#define NTB_EPF_LINK_STATUS 0x0A
#define LINK_STATUS_UP BIT(0)
#define NTB_EPF_TOPOLOGY 0x0C
#define NTB_EPF_LOWER_ADDR 0x10
#define NTB_EPF_UPPER_ADDR 0x14
#define NTB_EPF_LOWER_SIZE 0x18
#define NTB_EPF_UPPER_SIZE 0x1C
#define NTB_EPF_MW_COUNT 0x20
#define NTB_EPF_MW1_OFFSET 0x24
#define NTB_EPF_SPAD_OFFSET 0x28
#define NTB_EPF_SPAD_COUNT 0x2C
#define NTB_EPF_DB_ENTRY_SIZE 0x30
#define NTB_EPF_DB_DATA(n) (0x34 + (n) * 4)
#define NTB_EPF_DB_OFFSET(n) (0xB4 + (n) * 4)
/*
* Legacy doorbell slot layout when paired with pci-epf-*ntb:
*
* slot 0 : reserved for link events
* slot 1 : unused (historical extra offset)
* slot 2 : DB#0
* slot 3 : DB#1
* ...
*
* Thus, NTB_EPF_MIN_DB_COUNT=3 means that we at least create vectors for
* doorbells DB#0 and DB#1.
*/
#define NTB_EPF_MIN_DB_COUNT 3
#define NTB_EPF_MAX_DB_COUNT 31
#define NTB_EPF_COMMAND_TIMEOUT 1000 /* 1 Sec */
enum pci_barno {
NO_BAR = -1,
BAR_0,
BAR_1,
BAR_2,
BAR_3,
BAR_4,
BAR_5,
};
enum epf_ntb_bar {
BAR_CONFIG,
BAR_PEER_SPAD,
BAR_DB,
BAR_MW1,
BAR_MW2,
BAR_MW3,
BAR_MW4,
NTB_BAR_NUM,
};
enum epf_irq_slot {
EPF_IRQ_LINK = 0,
EPF_IRQ_RESERVED_DB, /* Historically skipped slot */
EPF_IRQ_DB_START,
};
#define NTB_EPF_MAX_MW_COUNT (NTB_BAR_NUM - BAR_MW1)
struct ntb_epf_dev;
struct ntb_epf_irq_ctx {
struct ntb_epf_dev *ndev;
unsigned int irq_no;
};
struct ntb_epf_dev {
struct ntb_dev ntb;
struct device *dev;
/* Mutex to protect providing commands to NTB EPF */
struct mutex cmd_lock;
const enum pci_barno *barno_map;
unsigned int mw_count;
unsigned int spad_count;
unsigned int db_count;
void __iomem *ctrl_reg;
void __iomem *db_reg;
void __iomem *peer_spad_reg;
unsigned int self_spad;
unsigned int peer_spad;
atomic64_t db_val;
u64 db_valid_mask;
struct ntb_epf_irq_ctx irq_ctx[NTB_EPF_MAX_DB_COUNT + 1];
};
#define ntb_ndev(__ntb) container_of(__ntb, struct ntb_epf_dev, ntb)
static int ntb_epf_send_command(struct ntb_epf_dev *ndev, u32 command,
u32 argument)
{
ktime_t timeout;
bool timedout;
int ret = 0;
u32 status;
mutex_lock(&ndev->cmd_lock);
writel(argument, ndev->ctrl_reg + NTB_EPF_ARGUMENT);
writel(command, ndev->ctrl_reg + NTB_EPF_COMMAND);
timeout = ktime_add_ms(ktime_get(), NTB_EPF_COMMAND_TIMEOUT);
while (1) {
timedout = ktime_after(ktime_get(), timeout);
status = readw(ndev->ctrl_reg + NTB_EPF_CMD_STATUS);
if (status == COMMAND_STATUS_ERROR) {
ret = -EINVAL;
break;
}
if (status == COMMAND_STATUS_OK)
break;
if (WARN_ON(timedout)) {
ret = -ETIMEDOUT;
break;
}
usleep_range(5, 10);
}
writew(0, ndev->ctrl_reg + NTB_EPF_CMD_STATUS);
mutex_unlock(&ndev->cmd_lock);
return ret;
}
static int ntb_epf_mw_to_bar(struct ntb_epf_dev *ndev, int idx)
{
struct device *dev = ndev->dev;
if (idx < 0 || idx > ndev->mw_count) {
dev_err(dev, "Unsupported Memory Window index %d\n", idx);
return -EINVAL;
}
return ndev->barno_map[BAR_MW1 + idx];
}
static int ntb_epf_mw_count(struct ntb_dev *ntb, int pidx)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
struct device *dev = ndev->dev;
if (pidx != NTB_DEF_PEER_IDX) {
dev_err(dev, "Unsupported Peer ID %d\n", pidx);
return -EINVAL;
}
return ndev->mw_count;
}
static int ntb_epf_mw_get_align(struct ntb_dev *ntb, int pidx, int idx,
resource_size_t *addr_align,
resource_size_t *size_align,
resource_size_t *size_max)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
struct device *dev = ndev->dev;
int bar;
if (pidx != NTB_DEF_PEER_IDX) {
dev_err(dev, "Unsupported Peer ID %d\n", pidx);
return -EINVAL;
}
bar = ntb_epf_mw_to_bar(ndev, idx);
if (bar < 0)
return bar;
if (addr_align)
*addr_align = SZ_4K;
if (size_align)
*size_align = 1;
if (size_max)
*size_max = pci_resource_len(ndev->ntb.pdev, bar);
return 0;
}
static u64 ntb_epf_link_is_up(struct ntb_dev *ntb,
enum ntb_speed *speed,
enum ntb_width *width)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
u32 status;
status = readw(ndev->ctrl_reg + NTB_EPF_LINK_STATUS);
return status & LINK_STATUS_UP;
}
static u32 ntb_epf_spad_read(struct ntb_dev *ntb, int idx)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
struct device *dev = ndev->dev;
u32 offset;
if (idx < 0 || idx >= ndev->spad_count) {
dev_err(dev, "READ: Invalid ScratchPad Index %d\n", idx);
return 0;
}
offset = readl(ndev->ctrl_reg + NTB_EPF_SPAD_OFFSET);
offset += (idx << 2);
return readl(ndev->ctrl_reg + offset);
}
static int ntb_epf_spad_write(struct ntb_dev *ntb,
int idx, u32 val)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
struct device *dev = ndev->dev;
u32 offset;
if (idx < 0 || idx >= ndev->spad_count) {
dev_err(dev, "WRITE: Invalid ScratchPad Index %d\n", idx);
return -EINVAL;
}
offset = readl(ndev->ctrl_reg + NTB_EPF_SPAD_OFFSET);
offset += (idx << 2);
writel(val, ndev->ctrl_reg + offset);
return 0;
}
static u32 ntb_epf_peer_spad_read(struct ntb_dev *ntb, int pidx, int idx)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
struct device *dev = ndev->dev;
u32 offset;
if (pidx != NTB_DEF_PEER_IDX) {
dev_err(dev, "Unsupported Peer ID %d\n", pidx);
return -EINVAL;
}
if (idx < 0 || idx >= ndev->spad_count) {
dev_err(dev, "WRITE: Invalid Peer ScratchPad Index %d\n", idx);
return -EINVAL;
}
offset = (idx << 2);
return readl(ndev->peer_spad_reg + offset);
}
static int ntb_epf_peer_spad_write(struct ntb_dev *ntb, int pidx,
int idx, u32 val)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
struct device *dev = ndev->dev;
u32 offset;
if (pidx != NTB_DEF_PEER_IDX) {
dev_err(dev, "Unsupported Peer ID %d\n", pidx);
return -EINVAL;
}
if (idx < 0 || idx >= ndev->spad_count) {
dev_err(dev, "WRITE: Invalid Peer ScratchPad Index %d\n", idx);
return -EINVAL;
}
offset = (idx << 2);
writel(val, ndev->peer_spad_reg + offset);
return 0;
}
static int ntb_epf_link_enable(struct ntb_dev *ntb,
enum ntb_speed max_speed,
enum ntb_width max_width)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
struct device *dev = ndev->dev;
int ret;
ret = ntb_epf_send_command(ndev, CMD_LINK_UP, 0);
if (ret) {
dev_err(dev, "Fail to enable link\n");
return ret;
}
return 0;
}
static int ntb_epf_link_disable(struct ntb_dev *ntb)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
struct device *dev = ndev->dev;
int ret;
ret = ntb_epf_send_command(ndev, CMD_LINK_DOWN, 0);
if (ret) {
dev_err(dev, "Fail to disable link\n");
return ret;
}
return 0;
}
static irqreturn_t ntb_epf_vec_isr(int irq, void *dev)
{
struct ntb_epf_irq_ctx *ctx = dev;
struct ntb_epf_dev *ndev = ctx->ndev;
unsigned int db_vector;
unsigned int irq_no = ctx->irq_no;
if (irq_no == EPF_IRQ_LINK) {
ntb_link_event(&ndev->ntb);
} else if (irq_no == EPF_IRQ_RESERVED_DB) {
dev_warn_ratelimited(ndev->dev,
"Unexpected reserved doorbell slot IRQ received\n");
} else {
db_vector = irq_no - EPF_IRQ_DB_START;
if (ndev->db_count < NTB_EPF_MIN_DB_COUNT ||
db_vector >= ndev->db_count - 1) {
dev_warn_ratelimited(ndev->dev,
"Unexpected doorbell vector %u (db_count %u)\n",
db_vector, ndev->db_count);
return IRQ_HANDLED;
}
atomic64_or(BIT_ULL(db_vector), &ndev->db_val);
ntb_db_event(&ndev->ntb, db_vector);
}
return IRQ_HANDLED;
}
static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max)
{
struct pci_dev *pdev = ndev->ntb.pdev;
struct device *dev = ndev->dev;
u32 argument = MSIX_ENABLE;
int irq;
int ret;
int i;
irq = pci_alloc_irq_vectors(pdev, msi_min, msi_max, PCI_IRQ_MSIX);
if (irq < 0) {
dev_dbg(dev, "Failed to get MSIX interrupts\n");
irq = pci_alloc_irq_vectors(pdev, msi_min, msi_max,
PCI_IRQ_MSI);
if (irq < 0) {
dev_err(dev, "Failed to get MSI interrupts\n");
return irq;
}
argument &= ~MSIX_ENABLE;
}
ndev->db_count = irq - 1;
for (i = 0; i < irq; i++) {
ndev->irq_ctx[i].ndev = ndev;
ndev->irq_ctx[i].irq_no = i;
ret = request_irq(pci_irq_vector(pdev, i), ntb_epf_vec_isr,
0, "ntb_epf", &ndev->irq_ctx[i]);
if (ret) {
dev_err(dev, "Failed to request irq\n");
goto err_free_irq;
}
}
ret = ntb_epf_send_command(ndev, CMD_CONFIGURE_DOORBELL,
argument | irq);
if (ret) {
dev_err(dev, "Failed to configure doorbell\n");
goto err_free_irq;
}
return 0;
err_free_irq:
while (i--)
free_irq(pci_irq_vector(pdev, i), &ndev->irq_ctx[i]);
pci_free_irq_vectors(pdev);
return ret;
}
static int ntb_epf_peer_mw_count(struct ntb_dev *ntb)
{
return ntb_ndev(ntb)->mw_count;
}
static int ntb_epf_spad_count(struct ntb_dev *ntb)
{
return ntb_ndev(ntb)->spad_count;
}
static u64 ntb_epf_db_valid_mask(struct ntb_dev *ntb)
{
return ntb_ndev(ntb)->db_valid_mask;
}
static int ntb_epf_db_vector_count(struct ntb_dev *ntb)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
unsigned int db_count = ndev->db_count;
/*
* db_count includes an extra skipped slot due to the legacy
* doorbell layout. Expose only the real doorbell vectors.
*/
if (db_count < NTB_EPF_MIN_DB_COUNT)
return 0;
return db_count - 1;
}
static u64 ntb_epf_db_vector_mask(struct ntb_dev *ntb, int db_vector)
{
int nr_vec;
/*
* db_count includes one skipped slot in the legacy layout. Valid
* doorbell vectors are therefore [0 .. (db_count - 2)].
*/
nr_vec = ntb_epf_db_vector_count(ntb);
if (db_vector < 0 || db_vector >= nr_vec)
return 0;
return BIT_ULL(db_vector);
}
static int ntb_epf_db_set_mask(struct ntb_dev *ntb, u64 db_bits)
{
return 0;
}
static int ntb_epf_mw_set_trans(struct ntb_dev *ntb, int pidx, int idx,
dma_addr_t addr, resource_size_t size)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
struct device *dev = ndev->dev;
resource_size_t mw_size;
int bar;
if (pidx != NTB_DEF_PEER_IDX) {
dev_err(dev, "Unsupported Peer ID %d\n", pidx);
return -EINVAL;
}
bar = ntb_epf_mw_to_bar(ndev, idx);
if (bar < 0)
return bar;
mw_size = pci_resource_len(ntb->pdev, bar);
if (size > mw_size) {
dev_err(dev, "Size:%pa is greater than the MW size %pa\n",
&size, &mw_size);
return -EINVAL;
}
writel(lower_32_bits(addr), ndev->ctrl_reg + NTB_EPF_LOWER_ADDR);
writel(upper_32_bits(addr), ndev->ctrl_reg + NTB_EPF_UPPER_ADDR);
writel(lower_32_bits(size), ndev->ctrl_reg + NTB_EPF_LOWER_SIZE);
writel(upper_32_bits(size), ndev->ctrl_reg + NTB_EPF_UPPER_SIZE);
ntb_epf_send_command(ndev, CMD_CONFIGURE_MW, idx);
return 0;
}
static int ntb_epf_mw_clear_trans(struct ntb_dev *ntb, int pidx, int idx)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
struct device *dev = ndev->dev;
int ret = 0;
ntb_epf_send_command(ndev, CMD_TEARDOWN_MW, idx);
if (ret)
dev_err(dev, "Failed to teardown memory window\n");
return ret;
}
static int ntb_epf_peer_mw_get_addr(struct ntb_dev *ntb, int idx,
phys_addr_t *base, resource_size_t *size)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
u32 offset = 0;
int bar;
if (idx == 0)
offset = readl(ndev->ctrl_reg + NTB_EPF_MW1_OFFSET);
bar = ntb_epf_mw_to_bar(ndev, idx);
if (bar < 0)
return bar;
if (base)
*base = pci_resource_start(ndev->ntb.pdev, bar) + offset;
if (size)
*size = pci_resource_len(ndev->ntb.pdev, bar) - offset;
return 0;
}
static int ntb_epf_peer_db_set(struct ntb_dev *ntb, u64 db_bits)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
/*
* ffs() returns a 1-based bit index (bit 0 -> 1).
*
* With slot 0 reserved for link events, DB#0 would naturally map to
* slot 1. Historically an extra +1 offset was added, so DB#0 maps to
* slot 2 and slot 1 remains unused. Keep this mapping for
* backward-compatibility.
*/
u32 interrupt_num = ffs(db_bits) + 1;
struct device *dev = ndev->dev;
u32 db_entry_size;
u32 db_offset;
u32 db_data;
if (interrupt_num > ndev->db_count) {
dev_err(dev, "DB interrupt %d greater than Max Supported %d\n",
interrupt_num, ndev->db_count);
return -EINVAL;
}
db_entry_size = readl(ndev->ctrl_reg + NTB_EPF_DB_ENTRY_SIZE);
db_data = readl(ndev->ctrl_reg + NTB_EPF_DB_DATA(interrupt_num));
db_offset = readl(ndev->ctrl_reg + NTB_EPF_DB_OFFSET(interrupt_num));
writel(db_data, ndev->db_reg + (db_entry_size * interrupt_num) +
db_offset);
return 0;
}
static u64 ntb_epf_db_read(struct ntb_dev *ntb)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
return atomic64_read(&ndev->db_val);
}
static int ntb_epf_db_clear_mask(struct ntb_dev *ntb, u64 db_bits)
{
return 0;
}
static int ntb_epf_db_clear(struct ntb_dev *ntb, u64 db_bits)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
atomic64_and(~db_bits, &ndev->db_val);
return 0;
}
static const struct ntb_dev_ops ntb_epf_ops = {
.mw_count = ntb_epf_mw_count,
.spad_count = ntb_epf_spad_count,
.peer_mw_count = ntb_epf_peer_mw_count,
.db_valid_mask = ntb_epf_db_valid_mask,
.db_vector_count = ntb_epf_db_vector_count,
.db_vector_mask = ntb_epf_db_vector_mask,
.db_set_mask = ntb_epf_db_set_mask,
.mw_set_trans = ntb_epf_mw_set_trans,
.mw_clear_trans = ntb_epf_mw_clear_trans,
.peer_mw_get_addr = ntb_epf_peer_mw_get_addr,
.link_enable = ntb_epf_link_enable,
.spad_read = ntb_epf_spad_read,
.spad_write = ntb_epf_spad_write,
.peer_spad_read = ntb_epf_peer_spad_read,
.peer_spad_write = ntb_epf_peer_spad_write,
.peer_db_set = ntb_epf_peer_db_set,
.db_read = ntb_epf_db_read,
.mw_get_align = ntb_epf_mw_get_align,
.link_is_up = ntb_epf_link_is_up,
.db_clear_mask = ntb_epf_db_clear_mask,
.db_clear = ntb_epf_db_clear,
.link_disable = ntb_epf_link_disable,
};
static inline void ntb_epf_init_struct(struct ntb_epf_dev *ndev,
struct pci_dev *pdev)
{
ndev->ntb.pdev = pdev;
ndev->ntb.topo = NTB_TOPO_NONE;
ndev->ntb.ops = &ntb_epf_ops;
}
static int ntb_epf_init_dev(struct ntb_epf_dev *ndev)
{
struct device *dev = ndev->dev;
int ret;
ndev->mw_count = readl(ndev->ctrl_reg + NTB_EPF_MW_COUNT);
if (ndev->mw_count > NTB_EPF_MAX_MW_COUNT) {
dev_err(dev, "Unsupported MW count: %u\n", ndev->mw_count);
return -EINVAL;
}
/* One Link interrupt and rest doorbell interrupt */
ret = ntb_epf_init_isr(ndev, NTB_EPF_MIN_DB_COUNT + 1,
NTB_EPF_MAX_DB_COUNT + 1);
if (ret) {
dev_err(dev, "Failed to init ISR\n");
return ret;
}
/*
* ndev->db_count includes an extra skipped slot due to the legacy
* doorbell layout, hence -1.
*/
ndev->db_valid_mask = BIT_ULL(ndev->db_count - 1) - 1;
ndev->spad_count = readl(ndev->ctrl_reg + NTB_EPF_SPAD_COUNT);
return 0;
}
static int ntb_epf_init_pci(struct ntb_epf_dev *ndev,
struct pci_dev *pdev)
{
struct device *dev = ndev->dev;
size_t spad_sz, spad_off;
int ret;
pci_set_drvdata(pdev, ndev);
ret = pci_enable_device(pdev);
if (ret) {
dev_err(dev, "Cannot enable PCI device\n");
goto err_pci_enable;
}
ret = pci_request_regions(pdev, "ntb");
if (ret) {
dev_err(dev, "Cannot obtain PCI resources\n");
goto err_pci_regions;
}
pci_set_master(pdev);
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
if (ret) {
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(dev, "Cannot set DMA mask\n");
goto err_pci_regions;
}
dev_warn(&pdev->dev, "Cannot DMA highmem\n");
}
ndev->ctrl_reg = pci_iomap(pdev, ndev->barno_map[BAR_CONFIG], 0);
if (!ndev->ctrl_reg) {
ret = -EIO;
goto err_pci_regions;
}
if (ndev->barno_map[BAR_PEER_SPAD] != ndev->barno_map[BAR_CONFIG]) {
ndev->peer_spad_reg = pci_iomap(pdev,
ndev->barno_map[BAR_PEER_SPAD], 0);
if (!ndev->peer_spad_reg) {
ret = -EIO;
goto err_pci_regions;
}
} else {
spad_sz = 4 * readl(ndev->ctrl_reg + NTB_EPF_SPAD_COUNT);
spad_off = readl(ndev->ctrl_reg + NTB_EPF_SPAD_OFFSET);
ndev->peer_spad_reg = ndev->ctrl_reg + spad_off + spad_sz;
}
ndev->db_reg = pci_iomap(pdev, ndev->barno_map[BAR_DB], 0);
if (!ndev->db_reg) {
ret = -EIO;
goto err_pci_regions;
}
return 0;
err_pci_regions:
pci_disable_device(pdev);
err_pci_enable:
pci_set_drvdata(pdev, NULL);
return ret;
}
static void ntb_epf_deinit_pci(struct ntb_epf_dev *ndev)
{
struct pci_dev *pdev = ndev->ntb.pdev;
pci_iounmap(pdev, ndev->ctrl_reg);
if (ndev->barno_map[BAR_PEER_SPAD] != ndev->barno_map[BAR_CONFIG])
pci_iounmap(pdev, ndev->peer_spad_reg);
pci_iounmap(pdev, ndev->db_reg);
pci_release_regions(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
}
static void ntb_epf_cleanup_isr(struct ntb_epf_dev *ndev)
{
struct pci_dev *pdev = ndev->ntb.pdev;
int i;
ntb_epf_send_command(ndev, CMD_TEARDOWN_DOORBELL, ndev->db_count + 1);
for (i = 0; i < ndev->db_count + 1; i++)
free_irq(pci_irq_vector(pdev, i), &ndev->irq_ctx[i]);
pci_free_irq_vectors(pdev);
}
static int ntb_epf_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct device *dev = &pdev->dev;
struct ntb_epf_dev *ndev;
int ret;
if (pci_is_bridge(pdev))
return -ENODEV;
ndev = devm_kzalloc(dev, sizeof(*ndev), GFP_KERNEL);
if (!ndev)
return -ENOMEM;
ndev->barno_map = (const enum pci_barno *)id->driver_data;
if (!ndev->barno_map)
return -EINVAL;
ndev->dev = dev;
ntb_epf_init_struct(ndev, pdev);
mutex_init(&ndev->cmd_lock);
ret = ntb_epf_init_pci(ndev, pdev);
if (ret) {
dev_err(dev, "Failed to init PCI\n");
return ret;
}
ret = ntb_epf_init_dev(ndev);
if (ret) {
dev_err(dev, "Failed to init device\n");
goto err_init_dev;
}
ret = ntb_register_device(&ndev->ntb);
if (ret) {
dev_err(dev, "Failed to register NTB device\n");
goto err_register_dev;
}
return 0;
err_register_dev:
ntb_epf_cleanup_isr(ndev);
err_init_dev:
ntb_epf_deinit_pci(ndev);
return ret;
}
static void ntb_epf_pci_remove(struct pci_dev *pdev)
{
struct ntb_epf_dev *ndev = pci_get_drvdata(pdev);
ntb_unregister_device(&ndev->ntb);
ntb_epf_cleanup_isr(ndev);
ntb_epf_deinit_pci(ndev);
}
static const enum pci_barno j721e_map[NTB_BAR_NUM] = {
[BAR_CONFIG] = BAR_0,
[BAR_PEER_SPAD] = BAR_1,
[BAR_DB] = BAR_2,
[BAR_MW1] = BAR_2,
[BAR_MW2] = BAR_3,
[BAR_MW3] = BAR_4,
[BAR_MW4] = BAR_5
};
static const enum pci_barno mx8_map[NTB_BAR_NUM] = {
[BAR_CONFIG] = BAR_0,
[BAR_PEER_SPAD] = BAR_0,
[BAR_DB] = BAR_2,
[BAR_MW1] = BAR_4,
[BAR_MW2] = BAR_5,
[BAR_MW3] = NO_BAR,
[BAR_MW4] = NO_BAR
};
static const enum pci_barno rcar_barno[NTB_BAR_NUM] = {
[BAR_CONFIG] = BAR_0,
[BAR_PEER_SPAD] = BAR_0,
[BAR_DB] = BAR_4,
[BAR_MW1] = BAR_2,
[BAR_MW2] = NO_BAR,
[BAR_MW3] = NO_BAR,
[BAR_MW4] = NO_BAR,
};
static const struct pci_device_id ntb_epf_pci_tbl[] = {
{
PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_J721E),
.class = PCI_CLASS_MEMORY_RAM << 8, .class_mask = 0xffff00,
.driver_data = (kernel_ulong_t)j721e_map,
},
{
PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, 0x0809),
.class = PCI_CLASS_MEMORY_RAM << 8, .class_mask = 0xffff00,
.driver_data = (kernel_ulong_t)mx8_map,
},
{
PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0030),
.class = PCI_CLASS_MEMORY_RAM << 8, .class_mask = 0xffff00,
.driver_data = (kernel_ulong_t)rcar_barno,
},
{ },
};
static struct pci_driver ntb_epf_pci_driver = {
.name = KBUILD_MODNAME,
.id_table = ntb_epf_pci_tbl,
.probe = ntb_epf_pci_probe,
.remove = ntb_epf_pci_remove,
};
module_pci_driver(ntb_epf_pci_driver);
MODULE_DESCRIPTION("PCI ENDPOINT NTB HOST DRIVER");
MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
MODULE_LICENSE("GPL v2");
+31
View File
@@ -0,0 +1,31 @@
# SPDX-License-Identifier: GPL-2.0-only
config NTB_IDT
tristate "IDT PCIe-switch Non-Transparent Bridge support"
depends on PCI
select HWMON
help
This driver supports NTB of capable IDT PCIe-switches.
Some of the pre-initializations must be made before IDT PCIe-switch
exposes its NT-functions correctly. It should be done by either proper
initialization of EEPROM connected to master SMbus of the switch or
by BIOS using slave-SMBus interface changing corresponding registers
value. Evidently it must be done before PCI bus enumeration is
finished in Linux kernel.
First of all partitions must be activated and properly assigned to all
the ports with NT-functions intended to be activated (see SWPARTxCTL
and SWPORTxCTL registers). Then all NT-function BARs must be enabled
with chosen valid aperture. For memory windows related BARs the
aperture settings shall determine the maximum size of memory windows
accepted by a BAR. Note that BAR0 must map PCI configuration space
registers.
It's worth to note, that since a part of this driver relies on the
BAR settings of peer NT-functions, the BAR setups can't be done over
kernel PCI fixups. That's why the alternative pre-initialization
techniques like BIOS using SMBus interface or EEPROM should be
utilized.
If unsure, say N.
+2
View File
@@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_NTB_IDT) += ntb_hw_idt.o
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+8
View File
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-only
config NTB_INTEL
tristate "Intel Non-Transparent Bridge support"
depends on X86_64
help
This driver supports Intel NTB on capable Xeon and Atom hardware.
If unsure, say N.
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_NTB_INTEL) += ntb_hw_intel.o
ntb_hw_intel-y := ntb_hw_gen1.o ntb_hw_gen3.o ntb_hw_gen4.o
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,185 @@
/*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012-2017 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* BSD LICENSE
*
* Copyright(c) 2012-2017 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copy
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _NTB_INTEL_GEN1_H_
#define _NTB_INTEL_GEN1_H_
#include "ntb_hw_intel.h"
/* Intel Gen1 Xeon hardware */
#define XEON_PBAR23LMT_OFFSET 0x0000
#define XEON_PBAR45LMT_OFFSET 0x0008
#define XEON_PBAR4LMT_OFFSET 0x0008
#define XEON_PBAR5LMT_OFFSET 0x000c
#define XEON_PBAR23XLAT_OFFSET 0x0010
#define XEON_PBAR45XLAT_OFFSET 0x0018
#define XEON_PBAR4XLAT_OFFSET 0x0018
#define XEON_PBAR5XLAT_OFFSET 0x001c
#define XEON_SBAR23LMT_OFFSET 0x0020
#define XEON_SBAR45LMT_OFFSET 0x0028
#define XEON_SBAR4LMT_OFFSET 0x0028
#define XEON_SBAR5LMT_OFFSET 0x002c
#define XEON_SBAR23XLAT_OFFSET 0x0030
#define XEON_SBAR45XLAT_OFFSET 0x0038
#define XEON_SBAR4XLAT_OFFSET 0x0038
#define XEON_SBAR5XLAT_OFFSET 0x003c
#define XEON_SBAR0BASE_OFFSET 0x0040
#define XEON_SBAR23BASE_OFFSET 0x0048
#define XEON_SBAR45BASE_OFFSET 0x0050
#define XEON_SBAR4BASE_OFFSET 0x0050
#define XEON_SBAR5BASE_OFFSET 0x0054
#define XEON_SBDF_OFFSET 0x005c
#define XEON_NTBCNTL_OFFSET 0x0058
#define XEON_PDOORBELL_OFFSET 0x0060
#define XEON_PDBMSK_OFFSET 0x0062
#define XEON_SDOORBELL_OFFSET 0x0064
#define XEON_SDBMSK_OFFSET 0x0066
#define XEON_USMEMMISS_OFFSET 0x0070
#define XEON_SPAD_OFFSET 0x0080
#define XEON_PBAR23SZ_OFFSET 0x00d0
#define XEON_PBAR45SZ_OFFSET 0x00d1
#define XEON_PBAR4SZ_OFFSET 0x00d1
#define XEON_SBAR23SZ_OFFSET 0x00d2
#define XEON_SBAR45SZ_OFFSET 0x00d3
#define XEON_SBAR4SZ_OFFSET 0x00d3
#define XEON_PPD_OFFSET 0x00d4
#define XEON_PBAR5SZ_OFFSET 0x00d5
#define XEON_SBAR5SZ_OFFSET 0x00d6
#define XEON_WCCNTRL_OFFSET 0x00e0
#define XEON_UNCERRSTS_OFFSET 0x014c
#define XEON_CORERRSTS_OFFSET 0x0158
#define XEON_LINK_STATUS_OFFSET 0x01a2
#define XEON_SPCICMD_OFFSET 0x0504
#define XEON_DEVCTRL_OFFSET 0x0598
#define XEON_DEVSTS_OFFSET 0x059a
#define XEON_SLINK_STATUS_OFFSET 0x05a2
#define XEON_B2B_SPAD_OFFSET 0x0100
#define XEON_B2B_DOORBELL_OFFSET 0x0140
#define XEON_B2B_XLAT_OFFSETL 0x0144
#define XEON_B2B_XLAT_OFFSETU 0x0148
#define XEON_PPD_CONN_MASK 0x03
#define XEON_PPD_CONN_TRANSPARENT 0x00
#define XEON_PPD_CONN_B2B 0x01
#define XEON_PPD_CONN_RP 0x02
#define XEON_PPD_DEV_MASK 0x10
#define XEON_PPD_DEV_USD 0x00
#define XEON_PPD_DEV_DSD 0x10
#define XEON_PPD_SPLIT_BAR_MASK 0x40
#define XEON_PPD_TOPO_MASK (XEON_PPD_CONN_MASK | XEON_PPD_DEV_MASK)
#define XEON_PPD_TOPO_PRI_USD (XEON_PPD_CONN_RP | XEON_PPD_DEV_USD)
#define XEON_PPD_TOPO_PRI_DSD (XEON_PPD_CONN_RP | XEON_PPD_DEV_DSD)
#define XEON_PPD_TOPO_SEC_USD (XEON_PPD_CONN_TRANSPARENT | XEON_PPD_DEV_USD)
#define XEON_PPD_TOPO_SEC_DSD (XEON_PPD_CONN_TRANSPARENT | XEON_PPD_DEV_DSD)
#define XEON_PPD_TOPO_B2B_USD (XEON_PPD_CONN_B2B | XEON_PPD_DEV_USD)
#define XEON_PPD_TOPO_B2B_DSD (XEON_PPD_CONN_B2B | XEON_PPD_DEV_DSD)
#define XEON_MW_COUNT 2
#define HSX_SPLIT_BAR_MW_COUNT 3
#define XEON_DB_COUNT 15
#define XEON_DB_LINK 15
#define XEON_DB_LINK_BIT BIT_ULL(XEON_DB_LINK)
#define XEON_DB_MSIX_VECTOR_COUNT 4
#define XEON_DB_MSIX_VECTOR_SHIFT 5
#define XEON_DB_TOTAL_SHIFT 16
#define XEON_SPAD_COUNT 16
/* Use the following addresses for translation between b2b ntb devices in case
* the hardware default values are not reliable. */
#define XEON_B2B_BAR0_ADDR 0x1000000000000000ull
#define XEON_B2B_BAR2_ADDR64 0x2000000000000000ull
#define XEON_B2B_BAR4_ADDR64 0x4000000000000000ull
#define XEON_B2B_BAR4_ADDR32 0x20000000u
#define XEON_B2B_BAR5_ADDR32 0x40000000u
/* The peer ntb secondary config space is 32KB fixed size */
#define XEON_B2B_MIN_SIZE 0x8000
/* flags to indicate hardware errata */
#define NTB_HWERR_SDOORBELL_LOCKUP BIT_ULL(0)
#define NTB_HWERR_SB01BASE_LOCKUP BIT_ULL(1)
#define NTB_HWERR_B2BDOORBELL_BIT14 BIT_ULL(2)
#define NTB_HWERR_MSIX_VECTOR32_BAD BIT_ULL(3)
#define NTB_HWERR_BAR_ALIGN BIT_ULL(4)
#define NTB_HWERR_LTR_BAD BIT_ULL(5)
extern struct intel_b2b_addr xeon_b2b_usd_addr;
extern struct intel_b2b_addr xeon_b2b_dsd_addr;
int ndev_init_isr(struct intel_ntb_dev *ndev, int msix_min, int msix_max,
int msix_shift, int total_shift);
enum ntb_topo xeon_ppd_topo(struct intel_ntb_dev *ndev, u8 ppd);
void ndev_db_addr(struct intel_ntb_dev *ndev,
phys_addr_t *db_addr, resource_size_t *db_size,
phys_addr_t reg_addr, unsigned long reg);
u64 ndev_db_read(struct intel_ntb_dev *ndev, void __iomem *mmio);
int ndev_db_write(struct intel_ntb_dev *ndev, u64 db_bits,
void __iomem *mmio);
int ndev_mw_to_bar(struct intel_ntb_dev *ndev, int idx);
int intel_ntb_mw_count(struct ntb_dev *ntb, int pidx);
int intel_ntb_mw_get_align(struct ntb_dev *ntb, int pidx, int idx,
resource_size_t *addr_align, resource_size_t *size_align,
resource_size_t *size_max);
int intel_ntb_peer_mw_count(struct ntb_dev *ntb);
int intel_ntb_peer_mw_get_addr(struct ntb_dev *ntb, int idx,
phys_addr_t *base, resource_size_t *size);
u64 intel_ntb_link_is_up(struct ntb_dev *ntb, enum ntb_speed *speed,
enum ntb_width *width);
int intel_ntb_link_disable(struct ntb_dev *ntb);
u64 intel_ntb_db_valid_mask(struct ntb_dev *ntb);
int intel_ntb_db_vector_count(struct ntb_dev *ntb);
u64 intel_ntb_db_vector_mask(struct ntb_dev *ntb, int db_vector);
int intel_ntb_db_set_mask(struct ntb_dev *ntb, u64 db_bits);
int intel_ntb_db_clear_mask(struct ntb_dev *ntb, u64 db_bits);
int intel_ntb_spad_is_unsafe(struct ntb_dev *ntb);
int intel_ntb_spad_count(struct ntb_dev *ntb);
u32 intel_ntb_spad_read(struct ntb_dev *ntb, int idx);
int intel_ntb_spad_write(struct ntb_dev *ntb, int idx, u32 val);
u32 intel_ntb_peer_spad_read(struct ntb_dev *ntb, int pidx, int sidx);
int intel_ntb_peer_spad_write(struct ntb_dev *ntb, int pidx, int sidx,
u32 val);
int intel_ntb_peer_spad_addr(struct ntb_dev *ntb, int pidx, int sidx,
phys_addr_t *spad_addr);
int xeon_link_is_up(struct intel_ntb_dev *ndev);
#endif
@@ -0,0 +1,630 @@
/*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2017 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* BSD LICENSE
*
* Copyright(c) 2017 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copy
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Intel PCIe GEN3 NTB Linux driver
*
*/
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/random.h>
#include <linux/slab.h>
#include <linux/ntb.h>
#include "ntb_hw_intel.h"
#include "ntb_hw_gen1.h"
#include "ntb_hw_gen3.h"
static int gen3_poll_link(struct intel_ntb_dev *ndev);
static const struct intel_ntb_reg gen3_reg = {
.poll_link = gen3_poll_link,
.link_is_up = xeon_link_is_up,
.db_ioread = gen3_db_ioread,
.db_iowrite = gen3_db_iowrite,
.db_size = sizeof(u32),
.ntb_ctl = GEN3_NTBCNTL_OFFSET,
.mw_bar = {2, 4},
};
static const struct intel_ntb_alt_reg gen3_pri_reg = {
.db_bell = GEN3_EM_DOORBELL_OFFSET,
.db_clear = GEN3_IM_INT_STATUS_OFFSET,
.db_mask = GEN3_IM_INT_DISABLE_OFFSET,
.spad = GEN3_IM_SPAD_OFFSET,
};
static const struct intel_ntb_alt_reg gen3_b2b_reg = {
.db_bell = GEN3_IM_DOORBELL_OFFSET,
.db_clear = GEN3_EM_INT_STATUS_OFFSET,
.db_mask = GEN3_EM_INT_DISABLE_OFFSET,
.spad = GEN3_B2B_SPAD_OFFSET,
};
static const struct intel_ntb_xlat_reg gen3_sec_xlat = {
/* .bar0_base = GEN3_EMBAR0_OFFSET, */
.bar2_limit = GEN3_IMBAR1XLMT_OFFSET,
.bar2_xlat = GEN3_IMBAR1XBASE_OFFSET,
};
static int gen3_poll_link(struct intel_ntb_dev *ndev)
{
u16 reg_val;
int rc;
ndev->reg->db_iowrite(ndev->db_link_mask,
ndev->self_mmio +
ndev->self_reg->db_clear);
rc = pci_read_config_word(ndev->ntb.pdev,
GEN3_LINK_STATUS_OFFSET, &reg_val);
if (rc)
return 0;
if (reg_val == ndev->lnk_sta)
return 0;
ndev->lnk_sta = reg_val;
return 1;
}
static int gen3_init_isr(struct intel_ntb_dev *ndev)
{
int i;
/*
* The MSIX vectors and the interrupt status bits are not lined up
* on Skylake. By default the link status bit is bit 32, however it
* is by default MSIX vector0. We need to fixup to line them up.
* The vectors at reset is 1-32,0. We need to reprogram to 0-32.
*/
for (i = 0; i < GEN3_DB_MSIX_VECTOR_COUNT; i++)
iowrite8(i, ndev->self_mmio + GEN3_INTVEC_OFFSET + i);
/* move link status down one as workaround */
if (ndev->hwerr_flags & NTB_HWERR_MSIX_VECTOR32_BAD) {
iowrite8(GEN3_DB_MSIX_VECTOR_COUNT - 2,
ndev->self_mmio + GEN3_INTVEC_OFFSET +
(GEN3_DB_MSIX_VECTOR_COUNT - 1));
}
return ndev_init_isr(ndev, GEN3_DB_MSIX_VECTOR_COUNT,
GEN3_DB_MSIX_VECTOR_COUNT,
GEN3_DB_MSIX_VECTOR_SHIFT,
GEN3_DB_TOTAL_SHIFT);
}
static int gen3_setup_b2b_mw(struct intel_ntb_dev *ndev,
const struct intel_b2b_addr *addr,
const struct intel_b2b_addr *peer_addr)
{
struct pci_dev *pdev;
void __iomem *mmio;
phys_addr_t bar_addr;
pdev = ndev->ntb.pdev;
mmio = ndev->self_mmio;
/* setup incoming bar limits == base addrs (zero length windows) */
bar_addr = addr->bar2_addr64;
iowrite64(bar_addr, mmio + GEN3_IMBAR1XLMT_OFFSET);
bar_addr = ioread64(mmio + GEN3_IMBAR1XLMT_OFFSET);
dev_dbg(&pdev->dev, "IMBAR1XLMT %#018llx\n", bar_addr);
bar_addr = addr->bar4_addr64;
iowrite64(bar_addr, mmio + GEN3_IMBAR2XLMT_OFFSET);
bar_addr = ioread64(mmio + GEN3_IMBAR2XLMT_OFFSET);
dev_dbg(&pdev->dev, "IMBAR2XLMT %#018llx\n", bar_addr);
/* zero incoming translation addrs */
iowrite64(0, mmio + GEN3_IMBAR1XBASE_OFFSET);
iowrite64(0, mmio + GEN3_IMBAR2XBASE_OFFSET);
ndev->peer_mmio = ndev->self_mmio;
return 0;
}
static int gen3_init_ntb(struct intel_ntb_dev *ndev)
{
int rc;
ndev->mw_count = XEON_MW_COUNT;
ndev->spad_count = GEN3_SPAD_COUNT;
ndev->db_count = GEN3_DB_COUNT;
ndev->db_link_mask = GEN3_DB_LINK_BIT;
/* DB fixup for using 31 right now */
if (ndev->hwerr_flags & NTB_HWERR_MSIX_VECTOR32_BAD)
ndev->db_link_mask |= BIT_ULL(31);
switch (ndev->ntb.topo) {
case NTB_TOPO_B2B_USD:
case NTB_TOPO_B2B_DSD:
ndev->self_reg = &gen3_pri_reg;
ndev->peer_reg = &gen3_b2b_reg;
ndev->xlat_reg = &gen3_sec_xlat;
if (ndev->ntb.topo == NTB_TOPO_B2B_USD) {
rc = gen3_setup_b2b_mw(ndev,
&xeon_b2b_dsd_addr,
&xeon_b2b_usd_addr);
} else {
rc = gen3_setup_b2b_mw(ndev,
&xeon_b2b_usd_addr,
&xeon_b2b_dsd_addr);
}
if (rc)
return rc;
/* Enable Bus Master and Memory Space on the secondary side */
iowrite16(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER,
ndev->self_mmio + GEN3_SPCICMD_OFFSET);
break;
default:
return -EINVAL;
}
ndev->db_valid_mask = BIT_ULL(ndev->db_count) - 1;
/* Make sure we are not using DB's used for link status */
if (ndev->hwerr_flags & NTB_HWERR_MSIX_VECTOR32_BAD)
ndev->db_valid_mask &= ~ndev->db_link_mask;
ndev->reg->db_iowrite(ndev->db_valid_mask,
ndev->self_mmio +
ndev->self_reg->db_mask);
return 0;
}
int gen3_init_dev(struct intel_ntb_dev *ndev)
{
struct pci_dev *pdev;
u8 ppd;
int rc;
pdev = ndev->ntb.pdev;
ndev->reg = &gen3_reg;
rc = pci_read_config_byte(pdev, XEON_PPD_OFFSET, &ppd);
if (rc)
return -EIO;
ndev->ntb.topo = xeon_ppd_topo(ndev, ppd);
dev_dbg(&pdev->dev, "ppd %#x topo %s\n", ppd,
ntb_topo_string(ndev->ntb.topo));
if (ndev->ntb.topo == NTB_TOPO_NONE)
return -EINVAL;
ndev->hwerr_flags |= NTB_HWERR_MSIX_VECTOR32_BAD;
rc = gen3_init_ntb(ndev);
if (rc)
return rc;
return gen3_init_isr(ndev);
}
ssize_t ndev_ntb3_debugfs_read(struct file *filp, char __user *ubuf,
size_t count, loff_t *offp)
{
struct intel_ntb_dev *ndev;
void __iomem *mmio;
char *buf;
size_t buf_size;
ssize_t ret, off;
union { u64 v64; u32 v32; u16 v16; } u;
ndev = filp->private_data;
mmio = ndev->self_mmio;
buf_size = min(count, 0x800ul);
buf = kmalloc(buf_size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
off = 0;
off += scnprintf(buf + off, buf_size - off,
"NTB Device Information:\n");
off += scnprintf(buf + off, buf_size - off,
"Connection Topology -\t%s\n",
ntb_topo_string(ndev->ntb.topo));
off += scnprintf(buf + off, buf_size - off,
"NTB CTL -\t\t%#06x\n", ndev->ntb_ctl);
off += scnprintf(buf + off, buf_size - off,
"LNK STA -\t\t%#06x\n", ndev->lnk_sta);
if (!ndev->reg->link_is_up(ndev))
off += scnprintf(buf + off, buf_size - off,
"Link Status -\t\tDown\n");
else {
off += scnprintf(buf + off, buf_size - off,
"Link Status -\t\tUp\n");
off += scnprintf(buf + off, buf_size - off,
"Link Speed -\t\tPCI-E Gen %u\n",
NTB_LNK_STA_SPEED(ndev->lnk_sta));
off += scnprintf(buf + off, buf_size - off,
"Link Width -\t\tx%u\n",
NTB_LNK_STA_WIDTH(ndev->lnk_sta));
}
off += scnprintf(buf + off, buf_size - off,
"Memory Window Count -\t%u\n", ndev->mw_count);
off += scnprintf(buf + off, buf_size - off,
"Scratchpad Count -\t%u\n", ndev->spad_count);
off += scnprintf(buf + off, buf_size - off,
"Doorbell Count -\t%u\n", ndev->db_count);
off += scnprintf(buf + off, buf_size - off,
"Doorbell Vector Count -\t%u\n", ndev->db_vec_count);
off += scnprintf(buf + off, buf_size - off,
"Doorbell Vector Shift -\t%u\n", ndev->db_vec_shift);
off += scnprintf(buf + off, buf_size - off,
"Doorbell Valid Mask -\t%#llx\n", ndev->db_valid_mask);
off += scnprintf(buf + off, buf_size - off,
"Doorbell Link Mask -\t%#llx\n", ndev->db_link_mask);
off += scnprintf(buf + off, buf_size - off,
"Doorbell Mask Cached -\t%#llx\n", ndev->db_mask);
u.v64 = ndev_db_read(ndev, mmio + ndev->self_reg->db_mask);
off += scnprintf(buf + off, buf_size - off,
"Doorbell Mask -\t\t%#llx\n", u.v64);
u.v64 = ndev_db_read(ndev, mmio + ndev->self_reg->db_bell);
off += scnprintf(buf + off, buf_size - off,
"Doorbell Bell -\t\t%#llx\n", u.v64);
off += scnprintf(buf + off, buf_size - off,
"\nNTB Incoming XLAT:\n");
u.v64 = ioread64(mmio + GEN3_IMBAR1XBASE_OFFSET);
off += scnprintf(buf + off, buf_size - off,
"IMBAR1XBASE -\t\t%#018llx\n", u.v64);
u.v64 = ioread64(mmio + GEN3_IMBAR2XBASE_OFFSET);
off += scnprintf(buf + off, buf_size - off,
"IMBAR2XBASE -\t\t%#018llx\n", u.v64);
u.v64 = ioread64(mmio + GEN3_IMBAR1XLMT_OFFSET);
off += scnprintf(buf + off, buf_size - off,
"IMBAR1XLMT -\t\t\t%#018llx\n", u.v64);
u.v64 = ioread64(mmio + GEN3_IMBAR2XLMT_OFFSET);
off += scnprintf(buf + off, buf_size - off,
"IMBAR2XLMT -\t\t\t%#018llx\n", u.v64);
if (ntb_topo_is_b2b(ndev->ntb.topo)) {
off += scnprintf(buf + off, buf_size - off,
"\nNTB Outgoing B2B XLAT:\n");
u.v64 = ioread64(mmio + GEN3_EMBAR1XBASE_OFFSET);
off += scnprintf(buf + off, buf_size - off,
"EMBAR1XBASE -\t\t%#018llx\n", u.v64);
u.v64 = ioread64(mmio + GEN3_EMBAR2XBASE_OFFSET);
off += scnprintf(buf + off, buf_size - off,
"EMBAR2XBASE -\t\t%#018llx\n", u.v64);
u.v64 = ioread64(mmio + GEN3_EMBAR1XLMT_OFFSET);
off += scnprintf(buf + off, buf_size - off,
"EMBAR1XLMT -\t\t%#018llx\n", u.v64);
u.v64 = ioread64(mmio + GEN3_EMBAR2XLMT_OFFSET);
off += scnprintf(buf + off, buf_size - off,
"EMBAR2XLMT -\t\t%#018llx\n", u.v64);
off += scnprintf(buf + off, buf_size - off,
"\nNTB Secondary BAR:\n");
u.v64 = ioread64(mmio + GEN3_EMBAR0_OFFSET);
off += scnprintf(buf + off, buf_size - off,
"EMBAR0 -\t\t%#018llx\n", u.v64);
u.v64 = ioread64(mmio + GEN3_EMBAR1_OFFSET);
off += scnprintf(buf + off, buf_size - off,
"EMBAR1 -\t\t%#018llx\n", u.v64);
u.v64 = ioread64(mmio + GEN3_EMBAR2_OFFSET);
off += scnprintf(buf + off, buf_size - off,
"EMBAR2 -\t\t%#018llx\n", u.v64);
}
off += scnprintf(buf + off, buf_size - off,
"\nNTB Statistics:\n");
u.v16 = ioread16(mmio + GEN3_USMEMMISS_OFFSET);
off += scnprintf(buf + off, buf_size - off,
"Upstream Memory Miss -\t%u\n", u.v16);
off += scnprintf(buf + off, buf_size - off,
"\nNTB Hardware Errors:\n");
if (!pci_read_config_word(ndev->ntb.pdev,
GEN3_DEVSTS_OFFSET, &u.v16))
off += scnprintf(buf + off, buf_size - off,
"DEVSTS -\t\t%#06x\n", u.v16);
if (!pci_read_config_word(ndev->ntb.pdev,
GEN3_LINK_STATUS_OFFSET, &u.v16))
off += scnprintf(buf + off, buf_size - off,
"LNKSTS -\t\t%#06x\n", u.v16);
if (!pci_read_config_dword(ndev->ntb.pdev,
GEN3_UNCERRSTS_OFFSET, &u.v32))
off += scnprintf(buf + off, buf_size - off,
"UNCERRSTS -\t\t%#06x\n", u.v32);
if (!pci_read_config_dword(ndev->ntb.pdev,
GEN3_CORERRSTS_OFFSET, &u.v32))
off += scnprintf(buf + off, buf_size - off,
"CORERRSTS -\t\t%#06x\n", u.v32);
ret = simple_read_from_buffer(ubuf, count, offp, buf, off);
kfree(buf);
return ret;
}
int intel_ntb3_link_enable(struct ntb_dev *ntb, enum ntb_speed max_speed,
enum ntb_width max_width)
{
struct intel_ntb_dev *ndev;
u32 ntb_ctl;
ndev = container_of(ntb, struct intel_ntb_dev, ntb);
dev_dbg(&ntb->pdev->dev,
"Enabling link with max_speed %d max_width %d\n",
max_speed, max_width);
if (max_speed != NTB_SPEED_AUTO)
dev_dbg(&ntb->pdev->dev, "ignoring max_speed %d\n", max_speed);
if (max_width != NTB_WIDTH_AUTO)
dev_dbg(&ntb->pdev->dev, "ignoring max_width %d\n", max_width);
ntb_ctl = ioread32(ndev->self_mmio + ndev->reg->ntb_ctl);
ntb_ctl &= ~(NTB_CTL_DISABLE | NTB_CTL_CFG_LOCK);
ntb_ctl |= NTB_CTL_P2S_BAR2_SNOOP | NTB_CTL_S2P_BAR2_SNOOP;
ntb_ctl |= NTB_CTL_P2S_BAR4_SNOOP | NTB_CTL_S2P_BAR4_SNOOP;
iowrite32(ntb_ctl, ndev->self_mmio + ndev->reg->ntb_ctl);
return 0;
}
static int intel_ntb3_mw_set_trans(struct ntb_dev *ntb, int pidx, int idx,
dma_addr_t addr, resource_size_t size)
{
struct intel_ntb_dev *ndev = ntb_ndev(ntb);
unsigned long xlat_reg, limit_reg;
resource_size_t bar_size, mw_size;
void __iomem *mmio;
u64 base, limit, reg_val;
int bar;
if (pidx != NTB_DEF_PEER_IDX)
return -EINVAL;
if (idx >= ndev->b2b_idx && !ndev->b2b_off)
idx += 1;
bar = ndev_mw_to_bar(ndev, idx);
if (bar < 0)
return bar;
bar_size = pci_resource_len(ndev->ntb.pdev, bar);
if (idx == ndev->b2b_idx)
mw_size = bar_size - ndev->b2b_off;
else
mw_size = bar_size;
/* hardware requires that addr is aligned to bar size */
if (addr & (bar_size - 1))
return -EINVAL;
/* make sure the range fits in the usable mw size */
if (size > mw_size)
return -EINVAL;
mmio = ndev->self_mmio;
xlat_reg = ndev->xlat_reg->bar2_xlat + (idx * 0x10);
limit_reg = ndev->xlat_reg->bar2_limit + (idx * 0x10);
base = pci_resource_start(ndev->ntb.pdev, bar);
/* Set the limit if supported, if size is not mw_size */
if (limit_reg && size != mw_size)
limit = base + size;
else
limit = base + mw_size;
/* set and verify setting the translation address */
iowrite64(addr, mmio + xlat_reg);
reg_val = ioread64(mmio + xlat_reg);
if (reg_val != addr) {
iowrite64(0, mmio + xlat_reg);
return -EIO;
}
dev_dbg(&ntb->pdev->dev, "BAR %d IMBARXBASE: %#Lx\n", bar, reg_val);
/* set and verify setting the limit */
iowrite64(limit, mmio + limit_reg);
reg_val = ioread64(mmio + limit_reg);
if (reg_val != limit) {
iowrite64(base, mmio + limit_reg);
iowrite64(0, mmio + xlat_reg);
return -EIO;
}
dev_dbg(&ntb->pdev->dev, "BAR %d IMBARXLMT: %#Lx\n", bar, reg_val);
/* setup the EP */
limit_reg = ndev->xlat_reg->bar2_limit + (idx * 0x10) + 0x4000;
base = ioread64(mmio + GEN3_EMBAR1_OFFSET + (8 * idx));
base &= ~0xf;
if (limit_reg && size != mw_size)
limit = base + size;
else
limit = base + mw_size;
/* set and verify setting the limit */
iowrite64(limit, mmio + limit_reg);
reg_val = ioread64(mmio + limit_reg);
if (reg_val != limit) {
iowrite64(base, mmio + limit_reg);
iowrite64(0, mmio + xlat_reg);
return -EIO;
}
dev_dbg(&ntb->pdev->dev, "BAR %d EMBARXLMT: %#Lx\n", bar, reg_val);
return 0;
}
int intel_ntb3_peer_db_addr(struct ntb_dev *ntb, phys_addr_t *db_addr,
resource_size_t *db_size,
u64 *db_data, int db_bit)
{
phys_addr_t db_addr_base;
struct intel_ntb_dev *ndev = ntb_ndev(ntb);
if (unlikely(db_bit >= BITS_PER_LONG_LONG))
return -EINVAL;
if (unlikely(BIT_ULL(db_bit) & ~ntb_ndev(ntb)->db_valid_mask))
return -EINVAL;
ndev_db_addr(ndev, &db_addr_base, db_size, ndev->peer_addr,
ndev->peer_reg->db_bell);
if (db_addr) {
*db_addr = db_addr_base + (db_bit * 4);
dev_dbg(&ndev->ntb.pdev->dev, "Peer db addr %llx db bit %d\n",
*db_addr, db_bit);
}
if (db_data) {
*db_data = 1;
dev_dbg(&ndev->ntb.pdev->dev, "Peer db data %llx db bit %d\n",
*db_data, db_bit);
}
return 0;
}
int intel_ntb3_peer_db_set(struct ntb_dev *ntb, u64 db_bits)
{
struct intel_ntb_dev *ndev = ntb_ndev(ntb);
int bit;
if (db_bits & ~ndev->db_valid_mask)
return -EINVAL;
while (db_bits) {
bit = __ffs(db_bits);
iowrite32(1, ndev->peer_mmio +
ndev->peer_reg->db_bell + (bit * 4));
db_bits &= db_bits - 1;
}
return 0;
}
u64 intel_ntb3_db_read(struct ntb_dev *ntb)
{
struct intel_ntb_dev *ndev = ntb_ndev(ntb);
return ndev_db_read(ndev,
ndev->self_mmio +
ndev->self_reg->db_clear);
}
int intel_ntb3_db_clear(struct ntb_dev *ntb, u64 db_bits)
{
struct intel_ntb_dev *ndev = ntb_ndev(ntb);
return ndev_db_write(ndev, db_bits,
ndev->self_mmio +
ndev->self_reg->db_clear);
}
const struct ntb_dev_ops intel_ntb3_ops = {
.mw_count = intel_ntb_mw_count,
.mw_get_align = intel_ntb_mw_get_align,
.mw_set_trans = intel_ntb3_mw_set_trans,
.peer_mw_count = intel_ntb_peer_mw_count,
.peer_mw_get_addr = intel_ntb_peer_mw_get_addr,
.link_is_up = intel_ntb_link_is_up,
.link_enable = intel_ntb3_link_enable,
.link_disable = intel_ntb_link_disable,
.db_valid_mask = intel_ntb_db_valid_mask,
.db_vector_count = intel_ntb_db_vector_count,
.db_vector_mask = intel_ntb_db_vector_mask,
.db_read = intel_ntb3_db_read,
.db_clear = intel_ntb3_db_clear,
.db_set_mask = intel_ntb_db_set_mask,
.db_clear_mask = intel_ntb_db_clear_mask,
.peer_db_addr = intel_ntb3_peer_db_addr,
.peer_db_set = intel_ntb3_peer_db_set,
.spad_is_unsafe = intel_ntb_spad_is_unsafe,
.spad_count = intel_ntb_spad_count,
.spad_read = intel_ntb_spad_read,
.spad_write = intel_ntb_spad_write,
.peer_spad_addr = intel_ntb_peer_spad_addr,
.peer_spad_read = intel_ntb_peer_spad_read,
.peer_spad_write = intel_ntb_peer_spad_write,
};
@@ -0,0 +1,118 @@
/*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012-2017 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* BSD LICENSE
*
* Copyright(c) 2012-2017 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copy
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _NTB_INTEL_GEN3_H_
#define _NTB_INTEL_GEN3_H_
#include "ntb_hw_intel.h"
/* Intel Skylake Xeon hardware */
#define GEN3_IMBAR1SZ_OFFSET 0x00d0
#define GEN3_IMBAR2SZ_OFFSET 0x00d1
#define GEN3_EMBAR1SZ_OFFSET 0x00d2
#define GEN3_EMBAR2SZ_OFFSET 0x00d3
#define GEN3_DEVCTRL_OFFSET 0x0098
#define GEN3_DEVSTS_OFFSET 0x009a
#define GEN3_UNCERRSTS_OFFSET 0x014c
#define GEN3_CORERRSTS_OFFSET 0x0158
#define GEN3_LINK_STATUS_OFFSET 0x01a2
#define GEN3_NTBCNTL_OFFSET 0x0000
#define GEN3_IMBAR1XBASE_OFFSET 0x0010 /* SBAR2XLAT */
#define GEN3_IMBAR1XLMT_OFFSET 0x0018 /* SBAR2LMT */
#define GEN3_IMBAR2XBASE_OFFSET 0x0020 /* SBAR4XLAT */
#define GEN3_IMBAR2XLMT_OFFSET 0x0028 /* SBAR4LMT */
#define GEN3_IM_INT_STATUS_OFFSET 0x0040
#define GEN3_IM_INT_DISABLE_OFFSET 0x0048
#define GEN3_IM_SPAD_OFFSET 0x0080 /* SPAD */
#define GEN3_USMEMMISS_OFFSET 0x0070
#define GEN3_INTVEC_OFFSET 0x00d0
#define GEN3_IM_DOORBELL_OFFSET 0x0100 /* SDOORBELL0 */
#define GEN3_B2B_SPAD_OFFSET 0x0180 /* B2B SPAD */
#define GEN3_EMBAR0XBASE_OFFSET 0x4008 /* B2B_XLAT */
#define GEN3_EMBAR1XBASE_OFFSET 0x4010 /* PBAR2XLAT */
#define GEN3_EMBAR1XLMT_OFFSET 0x4018 /* PBAR2LMT */
#define GEN3_EMBAR2XBASE_OFFSET 0x4020 /* PBAR4XLAT */
#define GEN3_EMBAR2XLMT_OFFSET 0x4028 /* PBAR4LMT */
#define GEN3_EM_INT_STATUS_OFFSET 0x4040
#define GEN3_EM_INT_DISABLE_OFFSET 0x4048
#define GEN3_EM_SPAD_OFFSET 0x4080 /* remote SPAD */
#define GEN3_EM_DOORBELL_OFFSET 0x4100 /* PDOORBELL0 */
#define GEN3_SPCICMD_OFFSET 0x4504 /* SPCICMD */
#define GEN3_EMBAR0_OFFSET 0x4510 /* SBAR0BASE */
#define GEN3_EMBAR1_OFFSET 0x4518 /* SBAR23BASE */
#define GEN3_EMBAR2_OFFSET 0x4520 /* SBAR45BASE */
#define GEN3_DB_COUNT 32
#define GEN3_DB_LINK 32
#define GEN3_DB_LINK_BIT BIT_ULL(GEN3_DB_LINK)
#define GEN3_DB_MSIX_VECTOR_COUNT 33
#define GEN3_DB_MSIX_VECTOR_SHIFT 1
#define GEN3_DB_TOTAL_SHIFT 33
#define GEN3_SPAD_COUNT 16
static inline u64 gen3_db_ioread(const void __iomem *mmio)
{
return ioread64(mmio);
}
static inline void gen3_db_iowrite(u64 bits, void __iomem *mmio)
{
iowrite64(bits, mmio);
}
ssize_t ndev_ntb3_debugfs_read(struct file *filp, char __user *ubuf,
size_t count, loff_t *offp);
int gen3_init_dev(struct intel_ntb_dev *ndev);
int intel_ntb3_link_enable(struct ntb_dev *ntb, enum ntb_speed max_speed,
enum ntb_width max_width);
u64 intel_ntb3_db_read(struct ntb_dev *ntb);
int intel_ntb3_db_clear(struct ntb_dev *ntb, u64 db_bits);
int intel_ntb3_peer_db_set(struct ntb_dev *ntb, u64 db_bits);
int intel_ntb3_peer_db_addr(struct ntb_dev *ntb, phys_addr_t *db_addr,
resource_size_t *db_size,
u64 *db_data, int db_bit);
extern const struct ntb_dev_ops intel_ntb3_ops;
#endif
@@ -0,0 +1,604 @@
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
/* Copyright(c) 2020 Intel Corporation. All rights reserved. */
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/random.h>
#include <linux/slab.h>
#include <linux/ntb.h>
#include <linux/log2.h>
#include "ntb_hw_intel.h"
#include "ntb_hw_gen1.h"
#include "ntb_hw_gen3.h"
#include "ntb_hw_gen4.h"
static int gen4_poll_link(struct intel_ntb_dev *ndev);
static int gen4_link_is_up(struct intel_ntb_dev *ndev);
static const struct intel_ntb_reg gen4_reg = {
.poll_link = gen4_poll_link,
.link_is_up = gen4_link_is_up,
.db_ioread = gen3_db_ioread,
.db_iowrite = gen3_db_iowrite,
.db_size = sizeof(u32),
.ntb_ctl = GEN4_NTBCNTL_OFFSET,
.mw_bar = {2, 4},
};
static const struct intel_ntb_alt_reg gen4_pri_reg = {
.db_clear = GEN4_IM_INT_STATUS_OFFSET,
.db_mask = GEN4_IM_INT_DISABLE_OFFSET,
.spad = GEN4_IM_SPAD_OFFSET,
};
static const struct intel_ntb_xlat_reg gen4_sec_xlat = {
.bar2_limit = GEN4_IM23XLMT_OFFSET,
.bar2_xlat = GEN4_IM23XBASE_OFFSET,
.bar2_idx = GEN4_IM23XBASEIDX_OFFSET,
};
static const struct intel_ntb_alt_reg gen4_b2b_reg = {
.db_bell = GEN4_IM_DOORBELL_OFFSET,
.spad = GEN4_EM_SPAD_OFFSET,
};
static u64 get_ppd0(struct pci_dev *pdev)
{
if (pdev_is_gen4(pdev) || pdev_is_gen5(pdev))
return GEN4_PPD0_OFFSET;
else if (pdev_is_gen6(pdev))
return GEN6_PPD0_OFFSET;
return ULLONG_MAX;
}
static int gen4_poll_link(struct intel_ntb_dev *ndev)
{
u16 reg_val;
/*
* We need to write to DLLSCS bit in the SLOTSTS before we
* can clear the hardware link interrupt on ICX NTB.
*/
iowrite16(GEN4_SLOTSTS_DLLSCS, ndev->self_mmio + GEN4_SLOTSTS);
ndev->reg->db_iowrite(ndev->db_link_mask,
ndev->self_mmio +
ndev->self_reg->db_clear);
reg_val = ioread16(ndev->self_mmio + GEN4_LINK_STATUS_OFFSET);
if (reg_val == ndev->lnk_sta)
return 0;
ndev->lnk_sta = reg_val;
return 1;
}
static int gen4_link_is_up(struct intel_ntb_dev *ndev)
{
return NTB_LNK_STA_ACTIVE(ndev->lnk_sta);
}
static int gen4_init_isr(struct intel_ntb_dev *ndev)
{
int i;
/*
* The MSIX vectors and the interrupt status bits are not lined up
* on Gen3 (Skylake) and Gen4. By default the link status bit is bit
* 32, however it is by default MSIX vector0. We need to fixup to
* line them up. The vectors at reset is 1-32,0. We need to reprogram
* to 0-32.
*/
for (i = 0; i < GEN4_DB_MSIX_VECTOR_COUNT; i++)
iowrite8(i, ndev->self_mmio + GEN4_INTVEC_OFFSET + i);
return ndev_init_isr(ndev, GEN4_DB_MSIX_VECTOR_COUNT,
GEN4_DB_MSIX_VECTOR_COUNT,
GEN4_DB_MSIX_VECTOR_SHIFT,
GEN4_DB_TOTAL_SHIFT);
}
static int gen4_setup_b2b_mw(struct intel_ntb_dev *ndev,
const struct intel_b2b_addr *addr,
const struct intel_b2b_addr *peer_addr)
{
struct pci_dev *pdev;
void __iomem *mmio;
phys_addr_t bar_addr;
pdev = ndev->ntb.pdev;
mmio = ndev->self_mmio;
/* setup incoming bar limits == base addrs (zero length windows) */
bar_addr = addr->bar2_addr64;
iowrite64(bar_addr, mmio + GEN4_IM23XLMT_OFFSET);
bar_addr = ioread64(mmio + GEN4_IM23XLMT_OFFSET);
dev_dbg(&pdev->dev, "IM23XLMT %#018llx\n", bar_addr);
bar_addr = addr->bar4_addr64;
iowrite64(bar_addr, mmio + GEN4_IM45XLMT_OFFSET);
bar_addr = ioread64(mmio + GEN4_IM45XLMT_OFFSET);
dev_dbg(&pdev->dev, "IM45XLMT %#018llx\n", bar_addr);
/* zero incoming translation addrs */
iowrite64(0, mmio + GEN4_IM23XBASE_OFFSET);
iowrite64(0, mmio + GEN4_IM45XBASE_OFFSET);
ndev->peer_mmio = ndev->self_mmio;
return 0;
}
static int gen4_init_ntb(struct intel_ntb_dev *ndev)
{
int rc;
ndev->mw_count = XEON_MW_COUNT;
ndev->spad_count = GEN4_SPAD_COUNT;
ndev->db_count = GEN4_DB_COUNT;
ndev->db_link_mask = GEN4_DB_LINK_BIT;
ndev->self_reg = &gen4_pri_reg;
ndev->xlat_reg = &gen4_sec_xlat;
ndev->peer_reg = &gen4_b2b_reg;
if (ndev->ntb.topo == NTB_TOPO_B2B_USD)
rc = gen4_setup_b2b_mw(ndev, &xeon_b2b_dsd_addr,
&xeon_b2b_usd_addr);
else
rc = gen4_setup_b2b_mw(ndev, &xeon_b2b_usd_addr,
&xeon_b2b_dsd_addr);
if (rc)
return rc;
ndev->db_valid_mask = BIT_ULL(ndev->db_count) - 1;
ndev->reg->db_iowrite(ndev->db_valid_mask,
ndev->self_mmio +
ndev->self_reg->db_mask);
return 0;
}
static enum ntb_topo gen4_ppd_topo(struct intel_ntb_dev *ndev, u32 ppd)
{
switch (ppd & GEN4_PPD_TOPO_MASK) {
case GEN4_PPD_TOPO_B2B_USD:
return NTB_TOPO_B2B_USD;
case GEN4_PPD_TOPO_B2B_DSD:
return NTB_TOPO_B2B_DSD;
}
return NTB_TOPO_NONE;
}
static enum ntb_topo spr_ppd_topo(struct intel_ntb_dev *ndev, u32 ppd)
{
switch (ppd & SPR_PPD_TOPO_MASK) {
case SPR_PPD_TOPO_B2B_USD:
return NTB_TOPO_B2B_USD;
case SPR_PPD_TOPO_B2B_DSD:
return NTB_TOPO_B2B_DSD;
}
return NTB_TOPO_NONE;
}
int gen4_init_dev(struct intel_ntb_dev *ndev)
{
struct pci_dev *pdev = ndev->ntb.pdev;
u32 ppd1;
u16 lnkctl;
int rc;
ndev->reg = &gen4_reg;
if (pdev_is_ICX(pdev)) {
ndev->hwerr_flags |= NTB_HWERR_BAR_ALIGN;
ndev->hwerr_flags |= NTB_HWERR_LTR_BAD;
}
ppd1 = ioread32(ndev->self_mmio + GEN4_PPD1_OFFSET);
if (pdev_is_ICX(pdev))
ndev->ntb.topo = gen4_ppd_topo(ndev, ppd1);
else if (pdev_is_SPR(pdev) || pdev_is_gen5(pdev) || pdev_is_gen6(pdev))
ndev->ntb.topo = spr_ppd_topo(ndev, ppd1);
dev_dbg(&pdev->dev, "ppd %#x topo %s\n", ppd1,
ntb_topo_string(ndev->ntb.topo));
if (ndev->ntb.topo == NTB_TOPO_NONE)
return -EINVAL;
rc = gen4_init_ntb(ndev);
if (rc)
return rc;
/* init link setup */
lnkctl = ioread16(ndev->self_mmio + GEN4_LINK_CTRL_OFFSET);
lnkctl |= GEN4_LINK_CTRL_LINK_DISABLE;
iowrite16(lnkctl, ndev->self_mmio + GEN4_LINK_CTRL_OFFSET);
return gen4_init_isr(ndev);
}
ssize_t ndev_ntb4_debugfs_read(struct file *filp, char __user *ubuf,
size_t count, loff_t *offp)
{
struct intel_ntb_dev *ndev;
void __iomem *mmio;
char *buf;
size_t buf_size;
ssize_t ret, off;
union { u64 v64; u32 v32; u16 v16; } u;
ndev = filp->private_data;
mmio = ndev->self_mmio;
buf_size = min(count, 0x800ul);
buf = kmalloc(buf_size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
off = 0;
off += scnprintf(buf + off, buf_size - off,
"NTB Device Information:\n");
off += scnprintf(buf + off, buf_size - off,
"Connection Topology -\t%s\n",
ntb_topo_string(ndev->ntb.topo));
off += scnprintf(buf + off, buf_size - off,
"NTB CTL -\t\t%#06x\n", ndev->ntb_ctl);
off += scnprintf(buf + off, buf_size - off,
"LNK STA (cached) -\t\t%#06x\n", ndev->lnk_sta);
if (!ndev->reg->link_is_up(ndev))
off += scnprintf(buf + off, buf_size - off,
"Link Status -\t\tDown\n");
else {
off += scnprintf(buf + off, buf_size - off,
"Link Status -\t\tUp\n");
off += scnprintf(buf + off, buf_size - off,
"Link Speed -\t\tPCI-E Gen %u\n",
NTB_LNK_STA_SPEED(ndev->lnk_sta));
off += scnprintf(buf + off, buf_size - off,
"Link Width -\t\tx%u\n",
NTB_LNK_STA_WIDTH(ndev->lnk_sta));
}
off += scnprintf(buf + off, buf_size - off,
"Memory Window Count -\t%u\n", ndev->mw_count);
off += scnprintf(buf + off, buf_size - off,
"Scratchpad Count -\t%u\n", ndev->spad_count);
off += scnprintf(buf + off, buf_size - off,
"Doorbell Count -\t%u\n", ndev->db_count);
off += scnprintf(buf + off, buf_size - off,
"Doorbell Vector Count -\t%u\n", ndev->db_vec_count);
off += scnprintf(buf + off, buf_size - off,
"Doorbell Vector Shift -\t%u\n", ndev->db_vec_shift);
off += scnprintf(buf + off, buf_size - off,
"Doorbell Valid Mask -\t%#llx\n", ndev->db_valid_mask);
off += scnprintf(buf + off, buf_size - off,
"Doorbell Link Mask -\t%#llx\n", ndev->db_link_mask);
off += scnprintf(buf + off, buf_size - off,
"Doorbell Mask Cached -\t%#llx\n", ndev->db_mask);
u.v64 = ndev_db_read(ndev, mmio + ndev->self_reg->db_mask);
off += scnprintf(buf + off, buf_size - off,
"Doorbell Mask -\t\t%#llx\n", u.v64);
off += scnprintf(buf + off, buf_size - off,
"\nNTB Incoming XLAT:\n");
u.v64 = ioread64(mmio + GEN4_IM23XBASE_OFFSET);
off += scnprintf(buf + off, buf_size - off,
"IM23XBASE -\t\t%#018llx\n", u.v64);
u.v64 = ioread64(mmio + GEN4_IM45XBASE_OFFSET);
off += scnprintf(buf + off, buf_size - off,
"IM45XBASE -\t\t%#018llx\n", u.v64);
u.v64 = ioread64(mmio + GEN4_IM23XLMT_OFFSET);
off += scnprintf(buf + off, buf_size - off,
"IM23XLMT -\t\t\t%#018llx\n", u.v64);
u.v64 = ioread64(mmio + GEN4_IM45XLMT_OFFSET);
off += scnprintf(buf + off, buf_size - off,
"IM45XLMT -\t\t\t%#018llx\n", u.v64);
off += scnprintf(buf + off, buf_size - off,
"\nNTB Statistics:\n");
off += scnprintf(buf + off, buf_size - off,
"\nNTB Hardware Errors:\n");
if (!pci_read_config_word(ndev->ntb.pdev,
GEN4_DEVSTS_OFFSET, &u.v16))
off += scnprintf(buf + off, buf_size - off,
"DEVSTS -\t\t%#06x\n", u.v16);
u.v16 = ioread16(mmio + GEN4_LINK_STATUS_OFFSET);
off += scnprintf(buf + off, buf_size - off,
"LNKSTS -\t\t%#06x\n", u.v16);
if (!pci_read_config_dword(ndev->ntb.pdev,
GEN4_UNCERRSTS_OFFSET, &u.v32))
off += scnprintf(buf + off, buf_size - off,
"UNCERRSTS -\t\t%#06x\n", u.v32);
if (!pci_read_config_dword(ndev->ntb.pdev,
GEN4_CORERRSTS_OFFSET, &u.v32))
off += scnprintf(buf + off, buf_size - off,
"CORERRSTS -\t\t%#06x\n", u.v32);
ret = simple_read_from_buffer(ubuf, count, offp, buf, off);
kfree(buf);
return ret;
}
static int intel_ntb4_mw_set_trans(struct ntb_dev *ntb, int pidx, int idx,
dma_addr_t addr, resource_size_t size)
{
struct intel_ntb_dev *ndev = ntb_ndev(ntb);
unsigned long xlat_reg, limit_reg, idx_reg;
unsigned short base_idx, reg_val16;
resource_size_t bar_size, mw_size;
void __iomem *mmio;
u64 base, limit, reg_val;
int bar;
if (pidx != NTB_DEF_PEER_IDX)
return -EINVAL;
if (idx >= ndev->b2b_idx && !ndev->b2b_off)
idx += 1;
bar = ndev_mw_to_bar(ndev, idx);
if (bar < 0)
return bar;
bar_size = pci_resource_len(ndev->ntb.pdev, bar);
if (idx == ndev->b2b_idx)
mw_size = bar_size - ndev->b2b_off;
else
mw_size = bar_size;
if (ndev->hwerr_flags & NTB_HWERR_BAR_ALIGN) {
/* hardware requires that addr is aligned to bar size */
if (addr & (bar_size - 1))
return -EINVAL;
} else {
if (addr & (PAGE_SIZE - 1))
return -EINVAL;
}
/* make sure the range fits in the usable mw size */
if (size > mw_size)
return -EINVAL;
mmio = ndev->self_mmio;
xlat_reg = ndev->xlat_reg->bar2_xlat + (idx * 0x10);
limit_reg = ndev->xlat_reg->bar2_limit + (idx * 0x10);
base = pci_resource_start(ndev->ntb.pdev, bar);
/* Set the limit if supported, if size is not mw_size */
if (limit_reg && size != mw_size) {
limit = base + size;
base_idx = __ilog2_u64(size);
} else {
limit = base + mw_size;
base_idx = __ilog2_u64(mw_size);
}
/* set and verify setting the translation address */
iowrite64(addr, mmio + xlat_reg);
reg_val = ioread64(mmio + xlat_reg);
if (reg_val != addr) {
iowrite64(0, mmio + xlat_reg);
return -EIO;
}
dev_dbg(&ntb->pdev->dev, "BAR %d IMXBASE: %#Lx\n", bar, reg_val);
/* set and verify setting the limit */
iowrite64(limit, mmio + limit_reg);
reg_val = ioread64(mmio + limit_reg);
if (reg_val != limit) {
iowrite64(base, mmio + limit_reg);
iowrite64(0, mmio + xlat_reg);
return -EIO;
}
dev_dbg(&ntb->pdev->dev, "BAR %d IMXLMT: %#Lx\n", bar, reg_val);
if (ndev->hwerr_flags & NTB_HWERR_BAR_ALIGN) {
idx_reg = ndev->xlat_reg->bar2_idx + (idx * 0x2);
iowrite16(base_idx, mmio + idx_reg);
reg_val16 = ioread16(mmio + idx_reg);
if (reg_val16 != base_idx) {
iowrite64(base, mmio + limit_reg);
iowrite64(0, mmio + xlat_reg);
iowrite16(0, mmio + idx_reg);
return -EIO;
}
dev_dbg(&ntb->pdev->dev, "BAR %d IMBASEIDX: %#x\n", bar, reg_val16);
}
return 0;
}
static int intel_ntb4_link_enable(struct ntb_dev *ntb,
enum ntb_speed max_speed, enum ntb_width max_width)
{
struct intel_ntb_dev *ndev;
struct pci_dev *pdev;
u32 ntb_ctl, ppd0;
u16 lnkctl;
ndev = container_of(ntb, struct intel_ntb_dev, ntb);
pdev = ntb->pdev;
dev_dbg(&ntb->pdev->dev,
"Enabling link with max_speed %d max_width %d\n",
max_speed, max_width);
if (max_speed != NTB_SPEED_AUTO)
dev_dbg(&ntb->pdev->dev,
"ignoring max_speed %d\n", max_speed);
if (max_width != NTB_WIDTH_AUTO)
dev_dbg(&ntb->pdev->dev,
"ignoring max_width %d\n", max_width);
if (!(ndev->hwerr_flags & NTB_HWERR_LTR_BAD)) {
u32 ltr;
/* Setup active snoop LTR values */
ltr = NTB_LTR_ACTIVE_REQMNT | NTB_LTR_ACTIVE_VAL | NTB_LTR_ACTIVE_LATSCALE;
/* Setup active non-snoop values */
ltr = (ltr << NTB_LTR_NS_SHIFT) | ltr;
iowrite32(ltr, ndev->self_mmio + GEN4_LTR_ACTIVE_OFFSET);
/* Setup idle snoop LTR values */
ltr = NTB_LTR_IDLE_VAL | NTB_LTR_IDLE_LATSCALE | NTB_LTR_IDLE_REQMNT;
/* Setup idle non-snoop values */
ltr = (ltr << NTB_LTR_NS_SHIFT) | ltr;
iowrite32(ltr, ndev->self_mmio + GEN4_LTR_IDLE_OFFSET);
/* setup PCIe LTR to active */
iowrite8(NTB_LTR_SWSEL_ACTIVE, ndev->self_mmio + GEN4_LTR_SWSEL_OFFSET);
}
ntb_ctl = NTB_CTL_E2I_BAR23_SNOOP | NTB_CTL_I2E_BAR23_SNOOP;
ntb_ctl |= NTB_CTL_E2I_BAR45_SNOOP | NTB_CTL_I2E_BAR45_SNOOP;
iowrite32(ntb_ctl, ndev->self_mmio + ndev->reg->ntb_ctl);
lnkctl = ioread16(ndev->self_mmio + GEN4_LINK_CTRL_OFFSET);
lnkctl &= ~GEN4_LINK_CTRL_LINK_DISABLE;
iowrite16(lnkctl, ndev->self_mmio + GEN4_LINK_CTRL_OFFSET);
/* start link training in PPD0 */
ppd0 = ioread32(ndev->self_mmio + get_ppd0(pdev));
ppd0 |= GEN4_PPD_LINKTRN;
iowrite32(ppd0, ndev->self_mmio + get_ppd0(pdev));
/* make sure link training has started */
ppd0 = ioread32(ndev->self_mmio + get_ppd0(pdev));
if (!(ppd0 & GEN4_PPD_LINKTRN)) {
dev_warn(&ntb->pdev->dev, "Link is not training\n");
return -ENXIO;
}
ndev->dev_up = 1;
return 0;
}
static int intel_ntb4_link_disable(struct ntb_dev *ntb)
{
struct intel_ntb_dev *ndev;
u32 ntb_cntl;
u16 lnkctl;
ndev = container_of(ntb, struct intel_ntb_dev, ntb);
dev_dbg(&ntb->pdev->dev, "Disabling link\n");
/* clear the snoop bits */
ntb_cntl = ioread32(ndev->self_mmio + ndev->reg->ntb_ctl);
ntb_cntl &= ~(NTB_CTL_E2I_BAR23_SNOOP | NTB_CTL_I2E_BAR23_SNOOP);
ntb_cntl &= ~(NTB_CTL_E2I_BAR45_SNOOP | NTB_CTL_I2E_BAR45_SNOOP);
iowrite32(ntb_cntl, ndev->self_mmio + ndev->reg->ntb_ctl);
lnkctl = ioread16(ndev->self_mmio + GEN4_LINK_CTRL_OFFSET);
lnkctl |= GEN4_LINK_CTRL_LINK_DISABLE;
iowrite16(lnkctl, ndev->self_mmio + GEN4_LINK_CTRL_OFFSET);
/* set LTR to idle */
if (!(ndev->hwerr_flags & NTB_HWERR_LTR_BAD))
iowrite8(NTB_LTR_SWSEL_IDLE, ndev->self_mmio + GEN4_LTR_SWSEL_OFFSET);
ndev->dev_up = 0;
return 0;
}
static int intel_ntb4_mw_get_align(struct ntb_dev *ntb, int pidx, int idx,
resource_size_t *addr_align,
resource_size_t *size_align,
resource_size_t *size_max)
{
struct intel_ntb_dev *ndev = ntb_ndev(ntb);
resource_size_t bar_size, mw_size;
int bar;
if (pidx != NTB_DEF_PEER_IDX)
return -EINVAL;
if (idx >= ndev->b2b_idx && !ndev->b2b_off)
idx += 1;
bar = ndev_mw_to_bar(ndev, idx);
if (bar < 0)
return bar;
bar_size = pci_resource_len(ndev->ntb.pdev, bar);
if (idx == ndev->b2b_idx)
mw_size = bar_size - ndev->b2b_off;
else
mw_size = bar_size;
if (addr_align) {
if (ndev->hwerr_flags & NTB_HWERR_BAR_ALIGN)
*addr_align = pci_resource_len(ndev->ntb.pdev, bar);
else
*addr_align = PAGE_SIZE;
}
if (size_align)
*size_align = 1;
if (size_max)
*size_max = mw_size;
return 0;
}
const struct ntb_dev_ops intel_ntb4_ops = {
.mw_count = intel_ntb_mw_count,
.mw_get_align = intel_ntb4_mw_get_align,
.mw_set_trans = intel_ntb4_mw_set_trans,
.peer_mw_count = intel_ntb_peer_mw_count,
.peer_mw_get_addr = intel_ntb_peer_mw_get_addr,
.link_is_up = intel_ntb_link_is_up,
.link_enable = intel_ntb4_link_enable,
.link_disable = intel_ntb4_link_disable,
.db_valid_mask = intel_ntb_db_valid_mask,
.db_vector_count = intel_ntb_db_vector_count,
.db_vector_mask = intel_ntb_db_vector_mask,
.db_read = intel_ntb3_db_read,
.db_clear = intel_ntb3_db_clear,
.db_set_mask = intel_ntb_db_set_mask,
.db_clear_mask = intel_ntb_db_clear_mask,
.peer_db_addr = intel_ntb3_peer_db_addr,
.peer_db_set = intel_ntb3_peer_db_set,
.spad_is_unsafe = intel_ntb_spad_is_unsafe,
.spad_count = intel_ntb_spad_count,
.spad_read = intel_ntb_spad_read,
.spad_write = intel_ntb_spad_write,
.peer_spad_addr = intel_ntb_peer_spad_addr,
.peer_spad_read = intel_ntb_peer_spad_read,
.peer_spad_write = intel_ntb_peer_spad_write,
};
@@ -0,0 +1,133 @@
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
/* Copyright(c) 2020 Intel Corporation. All rights reserved. */
#ifndef _NTB_INTEL_GEN4_H_
#define _NTB_INTEL_GEN4_H_
#include "ntb_hw_intel.h"
/* Supported PCI device revision range for ICX */
#define PCI_DEVICE_REVISION_ICX_MIN 0x2
#define PCI_DEVICE_REVISION_ICX_MAX 0xF
/* Intel Gen4 NTB hardware */
/* PCIe config space */
#define GEN4_IMBAR23SZ_OFFSET 0x00c4
#define GEN4_IMBAR45SZ_OFFSET 0x00c5
#define GEN4_EMBAR23SZ_OFFSET 0x00c6
#define GEN4_EMBAR45SZ_OFFSET 0x00c7
#define GEN4_DEVCTRL_OFFSET 0x0048
#define GEN4_DEVSTS_OFFSET 0x004a
#define GEN4_UNCERRSTS_OFFSET 0x0104
#define GEN4_CORERRSTS_OFFSET 0x0110
/* BAR0 MMIO */
#define GEN4_NTBCNTL_OFFSET 0x0000
#define GEN4_IM23XBASE_OFFSET 0x0010 /* IMBAR1XBASE */
#define GEN4_IM23XLMT_OFFSET 0x0018 /* IMBAR1XLMT */
#define GEN4_IM45XBASE_OFFSET 0x0020 /* IMBAR2XBASE */
#define GEN4_IM45XLMT_OFFSET 0x0028 /* IMBAR2XLMT */
#define GEN4_IM_INT_STATUS_OFFSET 0x0040
#define GEN4_IM_INT_DISABLE_OFFSET 0x0048
#define GEN4_INTVEC_OFFSET 0x0050 /* 0-32 vecs */
#define GEN4_IM23XBASEIDX_OFFSET 0x0074
#define GEN4_IM45XBASEIDX_OFFSET 0x0076
#define GEN4_IM_SPAD_OFFSET 0x0080 /* 0-15 SPADs */
#define GEN4_IM_SPAD_SEM_OFFSET 0x00c0 /* SPAD hw semaphore */
#define GEN4_IM_SPAD_STICKY_OFFSET 0x00c4 /* sticky SPAD */
#define GEN4_IM_DOORBELL_OFFSET 0x0100 /* 0-31 doorbells */
#define GEN4_LTR_SWSEL_OFFSET 0x30ec
#define GEN4_LTR_ACTIVE_OFFSET 0x30f0
#define GEN4_LTR_IDLE_OFFSET 0x30f4
#define GEN4_EM_SPAD_OFFSET 0x8080
/* note, link status is now in MMIO and not config space for NTB */
#define GEN4_LINK_CTRL_OFFSET 0xb050
#define GEN4_LINK_STATUS_OFFSET 0xb052
#define GEN4_PPD0_OFFSET 0xb0d4
#define GEN4_PPD1_OFFSET 0xb4c0
#define GEN4_LTSSMSTATEJMP 0xf040
#define GEN4_PPD_CLEAR_TRN 0x0001
#define GEN4_PPD_LINKTRN 0x0008
#define GEN4_PPD_CONN_MASK 0x0300
#define SPR_PPD_CONN_MASK 0x0700
#define GEN4_PPD_CONN_B2B 0x0200
#define GEN4_PPD_DEV_MASK 0x1000
#define GEN4_PPD_DEV_DSD 0x1000
#define GEN4_PPD_DEV_USD 0x0000
#define SPR_PPD_DEV_MASK 0x4000
#define SPR_PPD_DEV_DSD 0x4000
#define SPR_PPD_DEV_USD 0x0000
#define GEN4_LINK_CTRL_LINK_DISABLE 0x0010
#define GEN4_SLOTSTS 0xb05a
#define GEN4_SLOTSTS_DLLSCS 0x100
#define GEN4_PPD_TOPO_MASK (GEN4_PPD_CONN_MASK | GEN4_PPD_DEV_MASK)
#define GEN4_PPD_TOPO_B2B_USD (GEN4_PPD_CONN_B2B | GEN4_PPD_DEV_USD)
#define GEN4_PPD_TOPO_B2B_DSD (GEN4_PPD_CONN_B2B | GEN4_PPD_DEV_DSD)
#define SPR_PPD_TOPO_MASK (SPR_PPD_CONN_MASK | SPR_PPD_DEV_MASK)
#define SPR_PPD_TOPO_B2B_USD (GEN4_PPD_CONN_B2B | SPR_PPD_DEV_USD)
#define SPR_PPD_TOPO_B2B_DSD (GEN4_PPD_CONN_B2B | SPR_PPD_DEV_DSD)
#define GEN4_DB_COUNT 32
#define GEN4_DB_LINK 32
#define GEN4_DB_LINK_BIT BIT_ULL(GEN4_DB_LINK)
#define GEN4_DB_MSIX_VECTOR_COUNT 33
#define GEN4_DB_MSIX_VECTOR_SHIFT 1
#define GEN4_DB_TOTAL_SHIFT 33
#define GEN4_SPAD_COUNT 16
#define NTB_CTL_E2I_BAR23_SNOOP 0x000004
#define NTB_CTL_E2I_BAR23_NOSNOOP 0x000008
#define NTB_CTL_I2E_BAR23_SNOOP 0x000010
#define NTB_CTL_I2E_BAR23_NOSNOOP 0x000020
#define NTB_CTL_E2I_BAR45_SNOOP 0x000040
#define NTB_CTL_E2I_BAR45_NOSNOO 0x000080
#define NTB_CTL_I2E_BAR45_SNOOP 0x000100
#define NTB_CTL_I2E_BAR45_NOSNOOP 0x000200
#define NTB_CTL_BUSNO_DIS_INC 0x000400
#define NTB_CTL_LINK_DOWN 0x010000
#define NTB_SJC_FORCEDETECT 0x000004
#define NTB_LTR_SWSEL_ACTIVE 0x0
#define NTB_LTR_SWSEL_IDLE 0x1
#define NTB_LTR_NS_SHIFT 16
#define NTB_LTR_ACTIVE_VAL 0x0000 /* 0 us */
#define NTB_LTR_ACTIVE_LATSCALE 0x0800 /* 1us scale */
#define NTB_LTR_ACTIVE_REQMNT 0x8000 /* snoop req enable */
#define NTB_LTR_IDLE_VAL 0x0258 /* 600 us */
#define NTB_LTR_IDLE_LATSCALE 0x0800 /* 1us scale */
#define NTB_LTR_IDLE_REQMNT 0x8000 /* snoop req enable */
#define GEN6_PPD0_OFFSET 0xf0d4
ssize_t ndev_ntb4_debugfs_read(struct file *filp, char __user *ubuf,
size_t count, loff_t *offp);
int gen4_init_dev(struct intel_ntb_dev *ndev);
ssize_t ndev_ntb4_debugfs_read(struct file *filp, char __user *ubuf,
size_t count, loff_t *offp);
extern const struct ntb_dev_ops intel_ntb4_ops;
static inline int pdev_is_ICX(struct pci_dev *pdev)
{
if (pdev_is_gen4(pdev) &&
pdev->revision >= PCI_DEVICE_REVISION_ICX_MIN &&
pdev->revision <= PCI_DEVICE_REVISION_ICX_MAX)
return 1;
return 0;
}
static inline int pdev_is_SPR(struct pci_dev *pdev)
{
if (pdev_is_gen4(pdev) &&
pdev->revision > PCI_DEVICE_REVISION_ICX_MAX)
return 1;
return 0;
}
#endif
@@ -0,0 +1,244 @@
/*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 Intel Corporation. All rights reserved.
* Copyright (C) 2015 EMC Corporation. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* BSD LICENSE
*
* Copyright(c) 2012 Intel Corporation. All rights reserved.
* Copyright (C) 2015 EMC Corporation. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copy
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Intel PCIe NTB Linux driver
*/
#ifndef NTB_HW_INTEL_H
#define NTB_HW_INTEL_H
#include <linux/ntb.h>
#include <linux/pci.h>
#include <linux/io-64-nonatomic-lo-hi.h>
/* PCI device IDs */
#define PCI_DEVICE_ID_INTEL_NTB_B2B_JSF 0x3725
#define PCI_DEVICE_ID_INTEL_NTB_PS_JSF 0x3726
#define PCI_DEVICE_ID_INTEL_NTB_SS_JSF 0x3727
#define PCI_DEVICE_ID_INTEL_NTB_B2B_SNB 0x3C0D
#define PCI_DEVICE_ID_INTEL_NTB_PS_SNB 0x3C0E
#define PCI_DEVICE_ID_INTEL_NTB_SS_SNB 0x3C0F
#define PCI_DEVICE_ID_INTEL_NTB_B2B_IVT 0x0E0D
#define PCI_DEVICE_ID_INTEL_NTB_PS_IVT 0x0E0E
#define PCI_DEVICE_ID_INTEL_NTB_SS_IVT 0x0E0F
#define PCI_DEVICE_ID_INTEL_NTB_B2B_HSX 0x2F0D
#define PCI_DEVICE_ID_INTEL_NTB_PS_HSX 0x2F0E
#define PCI_DEVICE_ID_INTEL_NTB_SS_HSX 0x2F0F
#define PCI_DEVICE_ID_INTEL_NTB_B2B_BDX 0x6F0D
#define PCI_DEVICE_ID_INTEL_NTB_PS_BDX 0x6F0E
#define PCI_DEVICE_ID_INTEL_NTB_SS_BDX 0x6F0F
#define PCI_DEVICE_ID_INTEL_NTB_B2B_SKX 0x201C
#define PCI_DEVICE_ID_INTEL_NTB_B2B_ICX 0x347e
#define PCI_DEVICE_ID_INTEL_NTB_B2B_GNR 0x0db4
#define PCI_DEVICE_ID_INTEL_NTB_B2B_DMR 0x7868
/* Ntb control and link status */
#define NTB_CTL_CFG_LOCK BIT(0)
#define NTB_CTL_DISABLE BIT(1)
#define NTB_CTL_S2P_BAR2_SNOOP BIT(2)
#define NTB_CTL_P2S_BAR2_SNOOP BIT(4)
#define NTB_CTL_S2P_BAR4_SNOOP BIT(6)
#define NTB_CTL_P2S_BAR4_SNOOP BIT(8)
#define NTB_CTL_S2P_BAR5_SNOOP BIT(12)
#define NTB_CTL_P2S_BAR5_SNOOP BIT(14)
#define NTB_LNK_STA_ACTIVE_BIT 0x2000
#define NTB_LNK_STA_SPEED_MASK 0x000f
#define NTB_LNK_STA_WIDTH_MASK 0x03f0
#define NTB_LNK_STA_ACTIVE(x) (!!((x) & NTB_LNK_STA_ACTIVE_BIT))
#define NTB_LNK_STA_SPEED(x) ((x) & NTB_LNK_STA_SPEED_MASK)
#define NTB_LNK_STA_WIDTH(x) (((x) & NTB_LNK_STA_WIDTH_MASK) >> 4)
/* flags to indicate unsafe api */
#define NTB_UNSAFE_DB BIT_ULL(0)
#define NTB_UNSAFE_SPAD BIT_ULL(1)
#define NTB_BAR_MASK_64 ~(0xfull)
#define NTB_BAR_MASK_32 ~(0xfu)
struct intel_ntb_dev;
struct intel_ntb_reg {
int (*poll_link)(struct intel_ntb_dev *ndev);
int (*link_is_up)(struct intel_ntb_dev *ndev);
u64 (*db_ioread)(const void __iomem *mmio);
void (*db_iowrite)(u64 db_bits, void __iomem *mmio);
unsigned long ntb_ctl;
resource_size_t db_size;
int mw_bar[];
};
struct intel_ntb_alt_reg {
unsigned long db_bell;
unsigned long db_mask;
unsigned long db_clear;
unsigned long spad;
};
struct intel_ntb_xlat_reg {
unsigned long bar0_base;
unsigned long bar2_xlat;
unsigned long bar2_limit;
unsigned short bar2_idx;
};
struct intel_b2b_addr {
phys_addr_t bar0_addr;
phys_addr_t bar2_addr64;
phys_addr_t bar4_addr64;
phys_addr_t bar4_addr32;
phys_addr_t bar5_addr32;
};
struct intel_ntb_vec {
struct intel_ntb_dev *ndev;
int num;
};
struct intel_ntb_dev {
struct ntb_dev ntb;
/* offset of peer bar0 in b2b bar */
unsigned long b2b_off;
/* mw idx used to access peer bar0 */
unsigned int b2b_idx;
/* BAR45 is split into BAR4 and BAR5 */
bool bar4_split;
u32 ntb_ctl;
u32 lnk_sta;
unsigned char mw_count;
unsigned char spad_count;
unsigned char db_count;
unsigned char db_vec_count;
unsigned char db_vec_shift;
u64 db_valid_mask;
u64 db_link_mask;
u64 db_mask;
/* synchronize rmw access of db_mask and hw reg */
spinlock_t db_mask_lock;
struct msix_entry *msix;
struct intel_ntb_vec *vec;
const struct intel_ntb_reg *reg;
const struct intel_ntb_alt_reg *self_reg;
const struct intel_ntb_alt_reg *peer_reg;
const struct intel_ntb_xlat_reg *xlat_reg;
void __iomem *self_mmio;
void __iomem *peer_mmio;
phys_addr_t peer_addr;
unsigned long last_ts;
struct delayed_work hb_timer;
unsigned long hwerr_flags;
unsigned long unsafe_flags;
unsigned long unsafe_flags_ignore;
struct dentry *debugfs_dir;
struct dentry *debugfs_info;
/* gen4 entries */
int dev_up;
};
#define ntb_ndev(__ntb) container_of(__ntb, struct intel_ntb_dev, ntb)
#define hb_ndev(__work) container_of(__work, struct intel_ntb_dev, \
hb_timer.work)
static inline int pdev_is_gen1(struct pci_dev *pdev)
{
switch (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_SS_BDX:
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_PS_BDX:
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:
case PCI_DEVICE_ID_INTEL_NTB_B2B_BDX:
return 1;
}
return 0;
}
static inline int pdev_is_gen3(struct pci_dev *pdev)
{
if (pdev->device == PCI_DEVICE_ID_INTEL_NTB_B2B_SKX)
return 1;
return 0;
}
static inline int pdev_is_gen4(struct pci_dev *pdev)
{
if (pdev->device == PCI_DEVICE_ID_INTEL_NTB_B2B_ICX)
return 1;
return 0;
}
static inline int pdev_is_gen5(struct pci_dev *pdev)
{
return pdev->device == PCI_DEVICE_ID_INTEL_NTB_B2B_GNR;
}
static inline int pdev_is_gen6(struct pci_dev *pdev)
{
return pdev->device == PCI_DEVICE_ID_INTEL_NTB_B2B_DMR;
}
#endif
+10
View File
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0-only
config NTB_SWITCHTEC
tristate "MicroSemi Switchtec Non-Transparent Bridge Support"
select PCI_SW_SWITCHTEC
help
Enables NTB support for Switchtec PCI switches. This also
selects the Switchtec management driver as they share the same
hardware interface.
If unsure, say N.
+2
View File
@@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_NTB_SWITCHTEC) += ntb_hw_switchtec.o
File diff suppressed because it is too large Load Diff