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
+40
View File
@@ -0,0 +1,40 @@
# SPDX-License-Identifier: GPL-2.0-only
menuconfig NTB
tristate "Non-Transparent Bridge support"
depends on PCI
help
The PCI-E Non-transparent bridge hardware is a point-to-point PCI-E bus
connecting 2 systems. When configured, writes to the device's PCI
mapped memory will be mirrored to a buffer on the remote system. The
ntb Linux driver uses this point-to-point communication as a method to
transfer data from one system to the other.
If unsure, say N.
if NTB
config NTB_MSI
bool "MSI Interrupt Support"
depends on PCI_MSI
help
Support using MSI interrupt forwarding instead of (or in addition to)
hardware doorbells. MSI interrupts typically offer lower latency
than doorbells and more MSI interrupts can be made available to
clients. However this requires an extra memory window and support
in the hardware driver for creating the MSI interrupts.
If unsure, say N.
source "drivers/ntb/hw/Kconfig"
source "drivers/ntb/test/Kconfig"
config NTB_TRANSPORT
tristate "NTB Transport Client"
help
This is a transport driver that enables connected systems to exchange
messages over the ntb hardware. The transport exposes a queue pair api
to client drivers.
If unsure, say N.
endif # NTB
+6
View File
@@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_NTB) += ntb.o hw/ test/
obj-$(CONFIG_NTB_TRANSPORT) += ntb_transport.o
ntb-y := core.o
ntb-$(CONFIG_NTB_MSI) += msi.o
+317
View File
@@ -0,0 +1,317 @@
/*
* 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) 2015 EMC Corporation. All Rights Reserved.
* Copyright (C) 2016 T-Platforms. 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* BSD LICENSE
*
* Copyright (C) 2015 EMC Corporation. All Rights Reserved.
* Copyright (C) 2016 T-Platforms. 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.
*
* PCIe NTB Linux driver
*
* Contact Information:
* Allen Hubbe <Allen.Hubbe@emc.com>
*/
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/ntb.h>
#include <linux/pci.h>
#define DRIVER_NAME "ntb"
#define DRIVER_DESCRIPTION "PCIe NTB Driver Framework"
#define DRIVER_VERSION "1.0"
#define DRIVER_RELDATE "24 March 2015"
#define DRIVER_AUTHOR "Allen Hubbe <Allen.Hubbe@emc.com>"
MODULE_LICENSE("Dual BSD/GPL");
MODULE_VERSION(DRIVER_VERSION);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
static const struct bus_type ntb_bus;
static void ntb_dev_release(struct device *dev);
int __ntb_register_client(struct ntb_client *client, struct module *mod,
const char *mod_name)
{
if (!client)
return -EINVAL;
if (!ntb_client_ops_is_valid(&client->ops))
return -EINVAL;
memset(&client->drv, 0, sizeof(client->drv));
client->drv.bus = &ntb_bus;
client->drv.name = mod_name;
client->drv.owner = mod;
return driver_register(&client->drv);
}
EXPORT_SYMBOL(__ntb_register_client);
void ntb_unregister_client(struct ntb_client *client)
{
driver_unregister(&client->drv);
}
EXPORT_SYMBOL(ntb_unregister_client);
int ntb_register_device(struct ntb_dev *ntb)
{
int ret;
if (!ntb)
return -EINVAL;
if (!ntb->pdev)
return -EINVAL;
if (!ntb->ops)
return -EINVAL;
if (!ntb_dev_ops_is_valid(ntb->ops))
return -EINVAL;
init_completion(&ntb->released);
ntb->dev.bus = &ntb_bus;
ntb->dev.parent = &ntb->pdev->dev;
ntb->dev.release = ntb_dev_release;
dev_set_name(&ntb->dev, "%s", pci_name(ntb->pdev));
ntb->ctx = NULL;
ntb->ctx_ops = NULL;
spin_lock_init(&ntb->ctx_lock);
ret = device_register(&ntb->dev);
if (ret)
put_device(&ntb->dev);
return ret;
}
EXPORT_SYMBOL(ntb_register_device);
void ntb_unregister_device(struct ntb_dev *ntb)
{
device_unregister(&ntb->dev);
wait_for_completion(&ntb->released);
}
EXPORT_SYMBOL(ntb_unregister_device);
int ntb_set_ctx(struct ntb_dev *ntb, void *ctx,
const struct ntb_ctx_ops *ctx_ops)
{
unsigned long irqflags;
if (!ntb_ctx_ops_is_valid(ctx_ops))
return -EINVAL;
if (ntb->ctx_ops)
return -EINVAL;
spin_lock_irqsave(&ntb->ctx_lock, irqflags);
{
ntb->ctx = ctx;
ntb->ctx_ops = ctx_ops;
}
spin_unlock_irqrestore(&ntb->ctx_lock, irqflags);
return 0;
}
EXPORT_SYMBOL(ntb_set_ctx);
void ntb_clear_ctx(struct ntb_dev *ntb)
{
unsigned long irqflags;
spin_lock_irqsave(&ntb->ctx_lock, irqflags);
{
ntb->ctx_ops = NULL;
ntb->ctx = NULL;
}
spin_unlock_irqrestore(&ntb->ctx_lock, irqflags);
}
EXPORT_SYMBOL(ntb_clear_ctx);
void ntb_link_event(struct ntb_dev *ntb)
{
unsigned long irqflags;
spin_lock_irqsave(&ntb->ctx_lock, irqflags);
{
if (ntb->ctx_ops && ntb->ctx_ops->link_event)
ntb->ctx_ops->link_event(ntb->ctx);
}
spin_unlock_irqrestore(&ntb->ctx_lock, irqflags);
}
EXPORT_SYMBOL(ntb_link_event);
void ntb_db_event(struct ntb_dev *ntb, int vector)
{
unsigned long irqflags;
spin_lock_irqsave(&ntb->ctx_lock, irqflags);
{
if (ntb->ctx_ops && ntb->ctx_ops->db_event)
ntb->ctx_ops->db_event(ntb->ctx, vector);
}
spin_unlock_irqrestore(&ntb->ctx_lock, irqflags);
}
EXPORT_SYMBOL(ntb_db_event);
void ntb_msg_event(struct ntb_dev *ntb)
{
unsigned long irqflags;
spin_lock_irqsave(&ntb->ctx_lock, irqflags);
{
if (ntb->ctx_ops && ntb->ctx_ops->msg_event)
ntb->ctx_ops->msg_event(ntb->ctx);
}
spin_unlock_irqrestore(&ntb->ctx_lock, irqflags);
}
EXPORT_SYMBOL(ntb_msg_event);
int ntb_default_port_number(struct ntb_dev *ntb)
{
switch (ntb->topo) {
case NTB_TOPO_PRI:
case NTB_TOPO_B2B_USD:
return NTB_PORT_PRI_USD;
case NTB_TOPO_SEC:
case NTB_TOPO_B2B_DSD:
return NTB_PORT_SEC_DSD;
default:
return 0;
}
}
EXPORT_SYMBOL(ntb_default_port_number);
int ntb_default_peer_port_count(struct ntb_dev *ntb)
{
return NTB_DEF_PEER_CNT;
}
EXPORT_SYMBOL(ntb_default_peer_port_count);
int ntb_default_peer_port_number(struct ntb_dev *ntb, int pidx)
{
if (pidx != NTB_DEF_PEER_IDX)
return -EINVAL;
switch (ntb->topo) {
case NTB_TOPO_PRI:
case NTB_TOPO_B2B_USD:
return NTB_PORT_SEC_DSD;
case NTB_TOPO_SEC:
case NTB_TOPO_B2B_DSD:
return NTB_PORT_PRI_USD;
default:
return 0;
}
}
EXPORT_SYMBOL(ntb_default_peer_port_number);
int ntb_default_peer_port_idx(struct ntb_dev *ntb, int port)
{
int peer_port = ntb_default_peer_port_number(ntb, NTB_DEF_PEER_IDX);
if (peer_port == -EINVAL || port != peer_port)
return -EINVAL;
return 0;
}
EXPORT_SYMBOL(ntb_default_peer_port_idx);
static int ntb_probe(struct device *dev)
{
struct ntb_dev *ntb;
struct ntb_client *client;
int rc;
get_device(dev);
ntb = dev_ntb(dev);
client = drv_ntb_client(dev->driver);
rc = client->ops.probe(client, ntb);
if (rc)
put_device(dev);
return rc;
}
static void ntb_remove(struct device *dev)
{
struct ntb_dev *ntb;
struct ntb_client *client;
if (dev->driver) {
ntb = dev_ntb(dev);
client = drv_ntb_client(dev->driver);
client->ops.remove(client, ntb);
put_device(dev);
}
}
static void ntb_dev_release(struct device *dev)
{
struct ntb_dev *ntb = dev_ntb(dev);
complete(&ntb->released);
}
static const struct bus_type ntb_bus = {
.name = "ntb",
.probe = ntb_probe,
.remove = ntb_remove,
};
static int __init ntb_driver_init(void)
{
return bus_register(&ntb_bus);
}
module_init(ntb_driver_init);
static void __exit ntb_driver_exit(void)
{
bus_unregister(&ntb_bus);
}
module_exit(ntb_driver_exit);
+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
+344
View File
@@ -0,0 +1,344 @@
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/ntb.h>
#include <linux/msi.h>
#include <linux/pci.h>
struct ntb_msi {
u64 base_addr;
u64 end_addr;
void (*desc_changed)(void *ctx);
u32 __iomem *peer_mws[];
};
/**
* ntb_msi_init() - Initialize the MSI context
* @ntb: NTB device context
*
* This function must be called before any other ntb_msi function.
* It initializes the context for MSI operations and maps
* the peer memory windows.
*
* This function reserves the last N outbound memory windows (where N
* is the number of peers).
*
* Return: Zero on success, otherwise a negative error number.
*/
int ntb_msi_init(struct ntb_dev *ntb,
void (*desc_changed)(void *ctx))
{
phys_addr_t mw_phys_addr;
resource_size_t mw_size;
int peer_widx;
int peers;
int ret;
int i;
peers = ntb_peer_port_count(ntb);
if (peers <= 0)
return -EINVAL;
ntb->msi = devm_kzalloc(&ntb->dev, struct_size(ntb->msi, peer_mws, peers),
GFP_KERNEL);
if (!ntb->msi)
return -ENOMEM;
ntb->msi->desc_changed = desc_changed;
for (i = 0; i < peers; i++) {
peer_widx = ntb_peer_mw_count(ntb) - 1 - i;
ret = ntb_peer_mw_get_addr(ntb, peer_widx, &mw_phys_addr,
&mw_size);
if (ret)
goto unroll;
ntb->msi->peer_mws[i] = devm_ioremap(&ntb->dev, mw_phys_addr,
mw_size);
if (!ntb->msi->peer_mws[i]) {
ret = -EFAULT;
goto unroll;
}
}
return 0;
unroll:
for (i = 0; i < peers; i++)
if (ntb->msi->peer_mws[i])
devm_iounmap(&ntb->dev, ntb->msi->peer_mws[i]);
devm_kfree(&ntb->dev, ntb->msi);
ntb->msi = NULL;
return ret;
}
EXPORT_SYMBOL(ntb_msi_init);
/**
* ntb_msi_setup_mws() - Initialize the MSI inbound memory windows
* @ntb: NTB device context
*
* This function sets up the required inbound memory windows. It should be
* called from a work function after a link up event.
*
* Over the entire network, this function will reserves the last N
* inbound memory windows for each peer (where N is the number of peers).
*
* ntb_msi_init() must be called before this function.
*
* Return: Zero on success, otherwise a negative error number.
*/
int ntb_msi_setup_mws(struct ntb_dev *ntb)
{
struct msi_desc *desc;
u64 addr;
int peer, peer_widx;
resource_size_t addr_align, size_align, size_max;
resource_size_t mw_size = SZ_32K;
resource_size_t mw_min_size = mw_size;
int i;
int ret;
if (!ntb->msi)
return -EINVAL;
scoped_guard (msi_descs_lock, &ntb->pdev->dev) {
desc = msi_first_desc(&ntb->pdev->dev, MSI_DESC_ASSOCIATED);
addr = desc->msg.address_lo + ((uint64_t)desc->msg.address_hi << 32);
}
for (peer = 0; peer < ntb_peer_port_count(ntb); peer++) {
peer_widx = ntb_peer_highest_mw_idx(ntb, peer);
if (peer_widx < 0)
return peer_widx;
ret = ntb_mw_get_align(ntb, peer, peer_widx, &addr_align,
NULL, NULL);
if (ret)
return ret;
addr &= ~(addr_align - 1);
}
for (peer = 0; peer < ntb_peer_port_count(ntb); peer++) {
peer_widx = ntb_peer_highest_mw_idx(ntb, peer);
if (peer_widx < 0) {
ret = peer_widx;
goto error_out;
}
ret = ntb_mw_get_align(ntb, peer, peer_widx, NULL,
&size_align, &size_max);
if (ret)
goto error_out;
mw_size = round_up(mw_size, size_align);
mw_size = max(mw_size, size_max);
if (mw_size < mw_min_size)
mw_min_size = mw_size;
ret = ntb_mw_set_trans(ntb, peer, peer_widx,
addr, mw_size);
if (ret)
goto error_out;
}
ntb->msi->base_addr = addr;
ntb->msi->end_addr = addr + mw_min_size;
return 0;
error_out:
for (i = 0; i < peer; i++) {
peer_widx = ntb_peer_highest_mw_idx(ntb, peer);
if (peer_widx < 0)
continue;
ntb_mw_clear_trans(ntb, i, peer_widx);
}
return ret;
}
EXPORT_SYMBOL(ntb_msi_setup_mws);
/**
* ntb_msi_clear_mws() - Clear all inbound memory windows
* @ntb: NTB device context
*
* This function tears down the resources used by ntb_msi_setup_mws().
*/
void ntb_msi_clear_mws(struct ntb_dev *ntb)
{
int peer;
int peer_widx;
for (peer = 0; peer < ntb_peer_port_count(ntb); peer++) {
peer_widx = ntb_peer_highest_mw_idx(ntb, peer);
if (peer_widx < 0)
continue;
ntb_mw_clear_trans(ntb, peer, peer_widx);
}
}
EXPORT_SYMBOL(ntb_msi_clear_mws);
struct ntb_msi_devres {
struct ntb_dev *ntb;
struct msi_desc *entry;
struct ntb_msi_desc *msi_desc;
};
static int ntb_msi_set_desc(struct ntb_dev *ntb, struct msi_desc *entry,
struct ntb_msi_desc *msi_desc)
{
u64 addr;
addr = entry->msg.address_lo +
((uint64_t)entry->msg.address_hi << 32);
if (addr < ntb->msi->base_addr || addr >= ntb->msi->end_addr) {
dev_warn_once(&ntb->dev,
"IRQ %d: MSI Address not within the memory window (%llx, [%llx %llx])\n",
entry->irq, addr, ntb->msi->base_addr,
ntb->msi->end_addr);
return -EFAULT;
}
msi_desc->addr_offset = addr - ntb->msi->base_addr;
msi_desc->data = entry->msg.data;
return 0;
}
static void ntb_msi_write_msg(struct msi_desc *entry, void *data)
{
struct ntb_msi_devres *dr = data;
WARN_ON(ntb_msi_set_desc(dr->ntb, entry, dr->msi_desc));
if (dr->ntb->msi->desc_changed)
dr->ntb->msi->desc_changed(dr->ntb->ctx);
}
static void ntbm_msi_callback_release(struct device *dev, void *res)
{
struct ntb_msi_devres *dr = res;
dr->entry->write_msi_msg = NULL;
dr->entry->write_msi_msg_data = NULL;
}
static int ntbm_msi_setup_callback(struct ntb_dev *ntb, struct msi_desc *entry,
struct ntb_msi_desc *msi_desc)
{
struct ntb_msi_devres *dr;
dr = devres_alloc(ntbm_msi_callback_release,
sizeof(struct ntb_msi_devres), GFP_KERNEL);
if (!dr)
return -ENOMEM;
dr->ntb = ntb;
dr->entry = entry;
dr->msi_desc = msi_desc;
devres_add(&ntb->dev, dr);
dr->entry->write_msi_msg = ntb_msi_write_msg;
dr->entry->write_msi_msg_data = dr;
return 0;
}
/**
* ntbm_msi_request_threaded_irq() - allocate an MSI interrupt
* @ntb: NTB device context
* @handler: Function to be called when the IRQ occurs
* @thread_fn: Function to be called in a threaded interrupt context. NULL
* for clients which handle everything in @handler
* @name: An ascii name for the claiming device, dev_name(dev) if NULL
* @dev_id: A cookie passed back to the handler function
* @msi_desc: MSI descriptor data which triggers the interrupt
*
* This function assigns an interrupt handler to an unused
* MSI interrupt and returns the descriptor used to trigger
* it. The descriptor can then be sent to a peer to trigger
* the interrupt.
*
* The interrupt resource is managed with devres so it will
* be automatically freed when the NTB device is torn down.
*
* If an IRQ allocated with this function needs to be freed
* separately, ntbm_free_irq() must be used.
*
* Return: IRQ number assigned on success, otherwise a negative error number.
*/
int ntbm_msi_request_threaded_irq(struct ntb_dev *ntb, irq_handler_t handler,
irq_handler_t thread_fn,
const char *name, void *dev_id,
struct ntb_msi_desc *msi_desc)
{
struct device *dev = &ntb->pdev->dev;
struct msi_desc *entry;
int ret;
if (!ntb->msi)
return -EINVAL;
guard(msi_descs_lock)(dev);
msi_for_each_desc(entry, dev, MSI_DESC_ASSOCIATED) {
if (irq_has_action(entry->irq))
continue;
ret = devm_request_threaded_irq(&ntb->dev, entry->irq, handler,
thread_fn, 0, name, dev_id);
if (ret)
continue;
if (ntb_msi_set_desc(ntb, entry, msi_desc)) {
devm_free_irq(&ntb->dev, entry->irq, dev_id);
continue;
}
ret = ntbm_msi_setup_callback(ntb, entry, msi_desc);
if (ret) {
devm_free_irq(&ntb->dev, entry->irq, dev_id);
return ret;
}
return entry->irq;
}
return -ENODEV;
}
EXPORT_SYMBOL(ntbm_msi_request_threaded_irq);
/**
* ntb_msi_peer_trigger() - Trigger an interrupt handler on a peer
* @ntb: NTB device context
* @peer: Peer index
* @desc: MSI descriptor data which triggers the interrupt
*
* This function triggers an interrupt on a peer. It requires
* the descriptor structure to have been passed from that peer
* by some other means.
*
* Return: Zero on success, otherwise a negative error number.
*/
int ntb_msi_peer_trigger(struct ntb_dev *ntb, int peer,
struct ntb_msi_desc *desc)
{
int idx;
if (!ntb->msi)
return -EINVAL;
idx = desc->addr_offset / sizeof(*ntb->msi->peer_mws[peer]);
iowrite32(desc->data, &ntb->msi->peer_mws[peer][idx]);
return 0;
}
EXPORT_SYMBOL(ntb_msi_peer_trigger);
File diff suppressed because it is too large Load Diff
+37
View File
@@ -0,0 +1,37 @@
# SPDX-License-Identifier: GPL-2.0-only
config NTB_PINGPONG
tristate "NTB Ping Pong Test Client"
help
This is a simple ping pong driver that exercises the scratchpads and
doorbells of the ntb hardware. This driver may be used to test that
your ntb hardware and drivers are functioning at a basic level.
If unsure, say N.
config NTB_TOOL
tristate "NTB Debugging Tool Test Client"
help
This is a simple debugging driver that enables the doorbell and
scratchpad registers to be read and written from the debugfs. This
enables more complicated debugging to be scripted from user space.
This driver may be used to test that your ntb hardware and drivers are
functioning at a basic level.
If unsure, say N.
config NTB_PERF
tristate "NTB RAW Perf Measuring Tool"
help
This is a tool to measure raw NTB performance by transferring data
to and from the window without additional software interaction.
If unsure, say N.
config NTB_MSI_TEST
tristate "NTB MSI Test Client"
depends on NTB_MSI
help
This tool demonstrates the use of the NTB MSI library to
send MSI interrupts between peers.
If unsure, say N.
+5
View File
@@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_NTB_PINGPONG) += ntb_pingpong.o
obj-$(CONFIG_NTB_TOOL) += ntb_tool.o
obj-$(CONFIG_NTB_PERF) += ntb_perf.o
obj-$(CONFIG_NTB_MSI_TEST) += ntb_msi_test.o
+432
View File
@@ -0,0 +1,432 @@
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/ntb.h>
#include <linux/pci.h>
#include <linux/radix-tree.h>
#include <linux/workqueue.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_VERSION("0.1");
MODULE_AUTHOR("Logan Gunthorpe <logang@deltatee.com>");
MODULE_DESCRIPTION("Test for sending MSI interrupts over an NTB memory window");
static int num_irqs = 4;
module_param(num_irqs, int, 0644);
MODULE_PARM_DESC(num_irqs, "number of irqs to use");
struct ntb_msit_ctx {
struct ntb_dev *ntb;
struct dentry *dbgfs_dir;
struct work_struct setup_work;
struct ntb_msit_isr_ctx {
int irq_idx;
int irq_num;
int occurrences;
struct ntb_msit_ctx *nm;
struct ntb_msi_desc desc;
} *isr_ctx;
struct ntb_msit_peer {
struct ntb_msit_ctx *nm;
int pidx;
int num_irqs;
struct completion init_comp;
struct ntb_msi_desc *msi_desc;
} peers[];
};
static struct dentry *ntb_msit_dbgfs_topdir;
static irqreturn_t ntb_msit_isr(int irq, void *dev)
{
struct ntb_msit_isr_ctx *isr_ctx = dev;
struct ntb_msit_ctx *nm = isr_ctx->nm;
dev_dbg(&nm->ntb->dev, "Interrupt Occurred: %d",
isr_ctx->irq_idx);
isr_ctx->occurrences++;
return IRQ_HANDLED;
}
static void ntb_msit_setup_work(struct work_struct *work)
{
struct ntb_msit_ctx *nm = container_of(work, struct ntb_msit_ctx,
setup_work);
int irq_count = 0;
int irq;
int ret;
uintptr_t i;
ret = ntb_msi_setup_mws(nm->ntb);
if (ret) {
dev_err(&nm->ntb->dev, "Unable to setup MSI windows: %d\n",
ret);
return;
}
for (i = 0; i < num_irqs; i++) {
nm->isr_ctx[i].irq_idx = i;
nm->isr_ctx[i].nm = nm;
if (!nm->isr_ctx[i].irq_num) {
irq = ntbm_msi_request_irq(nm->ntb, ntb_msit_isr,
KBUILD_MODNAME,
&nm->isr_ctx[i],
&nm->isr_ctx[i].desc);
if (irq < 0)
break;
nm->isr_ctx[i].irq_num = irq;
}
ret = ntb_spad_write(nm->ntb, 2 * i + 1,
nm->isr_ctx[i].desc.addr_offset);
if (ret)
break;
ret = ntb_spad_write(nm->ntb, 2 * i + 2,
nm->isr_ctx[i].desc.data);
if (ret)
break;
irq_count++;
}
ntb_spad_write(nm->ntb, 0, irq_count);
ntb_peer_db_set(nm->ntb, BIT(ntb_port_number(nm->ntb)));
}
static void ntb_msit_desc_changed(void *ctx)
{
struct ntb_msit_ctx *nm = ctx;
int i;
dev_dbg(&nm->ntb->dev, "MSI Descriptors Changed\n");
for (i = 0; i < num_irqs; i++) {
ntb_spad_write(nm->ntb, 2 * i + 1,
nm->isr_ctx[i].desc.addr_offset);
ntb_spad_write(nm->ntb, 2 * i + 2,
nm->isr_ctx[i].desc.data);
}
ntb_peer_db_set(nm->ntb, BIT(ntb_port_number(nm->ntb)));
}
static void ntb_msit_link_event(void *ctx)
{
struct ntb_msit_ctx *nm = ctx;
if (!ntb_link_is_up(nm->ntb, NULL, NULL))
return;
schedule_work(&nm->setup_work);
}
static void ntb_msit_copy_peer_desc(struct ntb_msit_ctx *nm, int peer)
{
int i;
struct ntb_msi_desc *desc = nm->peers[peer].msi_desc;
int irq_count = nm->peers[peer].num_irqs;
for (i = 0; i < irq_count; i++) {
desc[i].addr_offset = ntb_peer_spad_read(nm->ntb, peer,
2 * i + 1);
desc[i].data = ntb_peer_spad_read(nm->ntb, peer, 2 * i + 2);
}
dev_info(&nm->ntb->dev, "Found %d interrupts on peer %d\n",
irq_count, peer);
complete_all(&nm->peers[peer].init_comp);
}
static void ntb_msit_db_event(void *ctx, int vec)
{
struct ntb_msit_ctx *nm = ctx;
struct ntb_msi_desc *desc;
u64 peer_mask = ntb_db_read(nm->ntb);
u32 irq_count;
int peer;
ntb_db_clear(nm->ntb, peer_mask);
for (peer = 0; peer < sizeof(peer_mask) * 8; peer++) {
if (!(peer_mask & BIT(peer)))
continue;
irq_count = ntb_peer_spad_read(nm->ntb, peer, 0);
if (irq_count == -1)
continue;
desc = kzalloc_objs(*desc, irq_count, GFP_ATOMIC);
if (!desc)
continue;
kfree(nm->peers[peer].msi_desc);
nm->peers[peer].msi_desc = desc;
nm->peers[peer].num_irqs = irq_count;
ntb_msit_copy_peer_desc(nm, peer);
}
}
static const struct ntb_ctx_ops ntb_msit_ops = {
.link_event = ntb_msit_link_event,
.db_event = ntb_msit_db_event,
};
static int ntb_msit_dbgfs_trigger(void *data, u64 idx)
{
struct ntb_msit_peer *peer = data;
if (idx >= peer->num_irqs)
return -EINVAL;
dev_dbg(&peer->nm->ntb->dev, "trigger irq %llu on peer %u\n",
idx, peer->pidx);
return ntb_msi_peer_trigger(peer->nm->ntb, peer->pidx,
&peer->msi_desc[idx]);
}
DEFINE_DEBUGFS_ATTRIBUTE(ntb_msit_trigger_fops, NULL,
ntb_msit_dbgfs_trigger, "%llu\n");
static int ntb_msit_dbgfs_port_get(void *data, u64 *port)
{
struct ntb_msit_peer *peer = data;
*port = ntb_peer_port_number(peer->nm->ntb, peer->pidx);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(ntb_msit_port_fops, ntb_msit_dbgfs_port_get,
NULL, "%llu\n");
static int ntb_msit_dbgfs_count_get(void *data, u64 *count)
{
struct ntb_msit_peer *peer = data;
*count = peer->num_irqs;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(ntb_msit_count_fops, ntb_msit_dbgfs_count_get,
NULL, "%llu\n");
static int ntb_msit_dbgfs_ready_get(void *data, u64 *ready)
{
struct ntb_msit_peer *peer = data;
*ready = try_wait_for_completion(&peer->init_comp);
return 0;
}
static int ntb_msit_dbgfs_ready_set(void *data, u64 ready)
{
struct ntb_msit_peer *peer = data;
return wait_for_completion_interruptible(&peer->init_comp);
}
DEFINE_DEBUGFS_ATTRIBUTE(ntb_msit_ready_fops, ntb_msit_dbgfs_ready_get,
ntb_msit_dbgfs_ready_set, "%llu\n");
static int ntb_msit_dbgfs_occurrences_get(void *data, u64 *occurrences)
{
struct ntb_msit_isr_ctx *isr_ctx = data;
*occurrences = isr_ctx->occurrences;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(ntb_msit_occurrences_fops,
ntb_msit_dbgfs_occurrences_get,
NULL, "%llu\n");
static int ntb_msit_dbgfs_local_port_get(void *data, u64 *port)
{
struct ntb_msit_ctx *nm = data;
*port = ntb_port_number(nm->ntb);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(ntb_msit_local_port_fops,
ntb_msit_dbgfs_local_port_get,
NULL, "%llu\n");
static void ntb_msit_create_dbgfs(struct ntb_msit_ctx *nm)
{
struct pci_dev *pdev = nm->ntb->pdev;
char buf[32];
int i;
struct dentry *peer_dir;
nm->dbgfs_dir = debugfs_create_dir(pci_name(pdev),
ntb_msit_dbgfs_topdir);
debugfs_create_file("port", 0400, nm->dbgfs_dir, nm,
&ntb_msit_local_port_fops);
for (i = 0; i < ntb_peer_port_count(nm->ntb); i++) {
nm->peers[i].pidx = i;
nm->peers[i].nm = nm;
init_completion(&nm->peers[i].init_comp);
snprintf(buf, sizeof(buf), "peer%d", i);
peer_dir = debugfs_create_dir(buf, nm->dbgfs_dir);
debugfs_create_file_unsafe("trigger", 0200, peer_dir,
&nm->peers[i],
&ntb_msit_trigger_fops);
debugfs_create_file_unsafe("port", 0400, peer_dir,
&nm->peers[i], &ntb_msit_port_fops);
debugfs_create_file_unsafe("count", 0400, peer_dir,
&nm->peers[i],
&ntb_msit_count_fops);
debugfs_create_file_unsafe("ready", 0600, peer_dir,
&nm->peers[i],
&ntb_msit_ready_fops);
}
for (i = 0; i < num_irqs; i++) {
snprintf(buf, sizeof(buf), "irq%d_occurrences", i);
debugfs_create_file_unsafe(buf, 0400, nm->dbgfs_dir,
&nm->isr_ctx[i],
&ntb_msit_occurrences_fops);
}
}
static void ntb_msit_remove_dbgfs(struct ntb_msit_ctx *nm)
{
debugfs_remove_recursive(nm->dbgfs_dir);
}
static int ntb_msit_probe(struct ntb_client *client, struct ntb_dev *ntb)
{
struct ntb_msit_ctx *nm;
int peers;
int ret;
peers = ntb_peer_port_count(ntb);
if (peers <= 0)
return -EINVAL;
if (ntb_spad_is_unsafe(ntb) || ntb_spad_count(ntb) < 2 * num_irqs + 1) {
dev_err(&ntb->dev, "NTB MSI test requires at least %d spads for %d irqs\n",
2 * num_irqs + 1, num_irqs);
return -EFAULT;
}
ret = ntb_spad_write(ntb, 0, -1);
if (ret) {
dev_err(&ntb->dev, "Unable to write spads: %d\n", ret);
return ret;
}
ret = ntb_db_clear_mask(ntb, GENMASK(peers - 1, 0));
if (ret) {
dev_err(&ntb->dev, "Unable to clear doorbell mask: %d\n", ret);
return ret;
}
ret = ntb_msi_init(ntb, ntb_msit_desc_changed);
if (ret) {
dev_err(&ntb->dev, "Unable to initialize MSI library: %d\n",
ret);
return ret;
}
nm = devm_kzalloc(&ntb->dev, struct_size(nm, peers, peers), GFP_KERNEL);
if (!nm)
return -ENOMEM;
nm->isr_ctx = devm_kcalloc(&ntb->dev, num_irqs, sizeof(*nm->isr_ctx),
GFP_KERNEL);
if (!nm->isr_ctx)
return -ENOMEM;
INIT_WORK(&nm->setup_work, ntb_msit_setup_work);
nm->ntb = ntb;
ntb_msit_create_dbgfs(nm);
ret = ntb_set_ctx(ntb, nm, &ntb_msit_ops);
if (ret)
goto remove_dbgfs;
if (!nm->isr_ctx) {
ret = -ENOMEM;
goto remove_dbgfs;
}
ntb_link_enable(ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO);
return 0;
remove_dbgfs:
ntb_msit_remove_dbgfs(nm);
devm_kfree(&ntb->dev, nm->isr_ctx);
devm_kfree(&ntb->dev, nm);
return ret;
}
static void ntb_msit_remove(struct ntb_client *client, struct ntb_dev *ntb)
{
struct ntb_msit_ctx *nm = ntb->ctx;
int i;
ntb_link_disable(ntb);
ntb_db_set_mask(ntb, ntb_db_valid_mask(ntb));
ntb_msi_clear_mws(ntb);
for (i = 0; i < ntb_peer_port_count(ntb); i++)
kfree(nm->peers[i].msi_desc);
ntb_clear_ctx(ntb);
ntb_msit_remove_dbgfs(nm);
}
static struct ntb_client ntb_msit_client = {
.ops = {
.probe = ntb_msit_probe,
.remove = ntb_msit_remove
}
};
static int __init ntb_msit_init(void)
{
int ret;
if (debugfs_initialized())
ntb_msit_dbgfs_topdir = debugfs_create_dir(KBUILD_MODNAME,
NULL);
ret = ntb_register_client(&ntb_msit_client);
if (ret)
debugfs_remove_recursive(ntb_msit_dbgfs_topdir);
return ret;
}
module_init(ntb_msit_init);
static void __exit ntb_msit_exit(void)
{
ntb_unregister_client(&ntb_msit_client);
debugfs_remove_recursive(ntb_msit_dbgfs_topdir);
}
module_exit(ntb_msit_exit);
File diff suppressed because it is too large Load Diff
+432
View File
@@ -0,0 +1,432 @@
/*
* 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) 2015 EMC Corporation. All Rights Reserved.
* Copyright (C) 2017 T-Platforms. 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* BSD LICENSE
*
* Copyright (C) 2015 EMC Corporation. All Rights Reserved.
* Copyright (C) 2017 T-Platforms. 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.
*
* PCIe NTB Pingpong Linux driver
*/
/*
* How to use this tool, by example.
*
* Assuming $DBG_DIR is something like:
* '/sys/kernel/debug/ntb_perf/0000:00:03.0'
* Suppose aside from local device there is at least one remote device
* connected to NTB with index 0.
*-----------------------------------------------------------------------------
* Eg: install driver with specified delay between doorbell event and response
*
* root@self# insmod ntb_pingpong.ko delay_ms=1000
*-----------------------------------------------------------------------------
* Eg: get number of ping-pong cycles performed
*
* root@self# cat $DBG_DIR/count
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/bitops.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/hrtimer.h>
#include <linux/debugfs.h>
#include <linux/ntb.h>
#define DRIVER_NAME "ntb_pingpong"
#define DRIVER_VERSION "2.0"
MODULE_LICENSE("Dual BSD/GPL");
MODULE_VERSION(DRIVER_VERSION);
MODULE_AUTHOR("Allen Hubbe <Allen.Hubbe@emc.com>");
MODULE_DESCRIPTION("PCIe NTB Simple Pingpong Client");
static unsigned int unsafe;
module_param(unsafe, uint, 0644);
MODULE_PARM_DESC(unsafe, "Run even though ntb operations may be unsafe");
static unsigned int delay_ms = 1000;
module_param(delay_ms, uint, 0644);
MODULE_PARM_DESC(delay_ms, "Milliseconds to delay the response to peer");
struct pp_ctx {
struct ntb_dev *ntb;
struct hrtimer timer;
u64 in_db;
u64 out_db;
int out_pidx;
u64 nmask;
u64 pmask;
atomic_t count;
spinlock_t lock;
struct dentry *dbgfs_dir;
};
#define to_pp_timer(__timer) \
container_of(__timer, struct pp_ctx, timer)
static struct dentry *pp_dbgfs_topdir;
static int pp_find_next_peer(struct pp_ctx *pp)
{
u64 link, out_db;
int pidx;
link = ntb_link_is_up(pp->ntb, NULL, NULL);
/* Find next available peer */
if (link & pp->nmask)
pidx = __ffs64(link & pp->nmask);
else if (link & pp->pmask)
pidx = __ffs64(link & pp->pmask);
else
return -ENODEV;
out_db = BIT_ULL(ntb_peer_port_number(pp->ntb, pidx));
spin_lock(&pp->lock);
pp->out_pidx = pidx;
pp->out_db = out_db;
spin_unlock(&pp->lock);
return 0;
}
static void pp_setup(struct pp_ctx *pp)
{
int ret;
ntb_db_set_mask(pp->ntb, pp->in_db);
hrtimer_cancel(&pp->timer);
ret = pp_find_next_peer(pp);
if (ret == -ENODEV) {
dev_dbg(&pp->ntb->dev, "Got no peers, so cancel\n");
return;
}
dev_dbg(&pp->ntb->dev, "Ping-pong started with port %d, db %#llx\n",
ntb_peer_port_number(pp->ntb, pp->out_pidx), pp->out_db);
hrtimer_start(&pp->timer, ms_to_ktime(delay_ms), HRTIMER_MODE_REL);
}
static void pp_clear(struct pp_ctx *pp)
{
hrtimer_cancel(&pp->timer);
ntb_db_set_mask(pp->ntb, pp->in_db);
dev_dbg(&pp->ntb->dev, "Ping-pong cancelled\n");
}
static void pp_ping(struct pp_ctx *pp)
{
u32 count;
count = atomic_read(&pp->count);
spin_lock(&pp->lock);
ntb_peer_spad_write(pp->ntb, pp->out_pidx, 0, count);
ntb_peer_msg_write(pp->ntb, pp->out_pidx, 0, count);
dev_dbg(&pp->ntb->dev, "Ping port %d spad %#x, msg %#x\n",
ntb_peer_port_number(pp->ntb, pp->out_pidx), count, count);
ntb_peer_db_set(pp->ntb, pp->out_db);
ntb_db_clear_mask(pp->ntb, pp->in_db);
spin_unlock(&pp->lock);
}
static void pp_pong(struct pp_ctx *pp)
{
u32 msg_data, spad_data;
int pidx = 0;
/* Read pong data */
spad_data = ntb_spad_read(pp->ntb, 0);
msg_data = ntb_msg_read(pp->ntb, &pidx, 0);
ntb_msg_clear_sts(pp->ntb, -1);
/*
* Scratchpad and message data may differ, since message register can't
* be rewritten unless status is cleared. Additionally either of them
* might be unsupported
*/
dev_dbg(&pp->ntb->dev, "Pong spad %#x, msg %#x (port %d)\n",
spad_data, msg_data, ntb_peer_port_number(pp->ntb, pidx));
atomic_inc(&pp->count);
ntb_db_set_mask(pp->ntb, pp->in_db);
ntb_db_clear(pp->ntb, pp->in_db);
hrtimer_start(&pp->timer, ms_to_ktime(delay_ms), HRTIMER_MODE_REL);
}
static enum hrtimer_restart pp_timer_func(struct hrtimer *t)
{
struct pp_ctx *pp = to_pp_timer(t);
pp_ping(pp);
return HRTIMER_NORESTART;
}
static void pp_link_event(void *ctx)
{
struct pp_ctx *pp = ctx;
pp_setup(pp);
}
static void pp_db_event(void *ctx, int vec)
{
struct pp_ctx *pp = ctx;
pp_pong(pp);
}
static const struct ntb_ctx_ops pp_ops = {
.link_event = pp_link_event,
.db_event = pp_db_event
};
static int pp_check_ntb(struct ntb_dev *ntb)
{
u64 pmask;
if (ntb_db_is_unsafe(ntb)) {
dev_dbg(&ntb->dev, "Doorbell is unsafe\n");
if (!unsafe)
return -EINVAL;
}
if (ntb_spad_is_unsafe(ntb)) {
dev_dbg(&ntb->dev, "Scratchpad is unsafe\n");
if (!unsafe)
return -EINVAL;
}
pmask = GENMASK_ULL(ntb_peer_port_count(ntb), 0);
if ((ntb_db_valid_mask(ntb) & pmask) != pmask) {
dev_err(&ntb->dev, "Unsupported DB configuration\n");
return -EINVAL;
}
if (ntb_spad_count(ntb) < 1 && ntb_msg_count(ntb) < 1) {
dev_err(&ntb->dev, "Scratchpads and messages unsupported\n");
return -EINVAL;
} else if (ntb_spad_count(ntb) < 1) {
dev_dbg(&ntb->dev, "Scratchpads unsupported\n");
} else if (ntb_msg_count(ntb) < 1) {
dev_dbg(&ntb->dev, "Messages unsupported\n");
}
return 0;
}
static struct pp_ctx *pp_create_data(struct ntb_dev *ntb)
{
struct pp_ctx *pp;
pp = devm_kzalloc(&ntb->dev, sizeof(*pp), GFP_KERNEL);
if (!pp)
return ERR_PTR(-ENOMEM);
pp->ntb = ntb;
atomic_set(&pp->count, 0);
spin_lock_init(&pp->lock);
hrtimer_setup(&pp->timer, pp_timer_func, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
return pp;
}
static void pp_init_flds(struct pp_ctx *pp)
{
int pidx, lport, pcnt;
/* Find global port index */
lport = ntb_port_number(pp->ntb);
pcnt = ntb_peer_port_count(pp->ntb);
for (pidx = 0; pidx < pcnt; pidx++) {
if (lport < ntb_peer_port_number(pp->ntb, pidx))
break;
}
pp->in_db = BIT_ULL(lport);
pp->pmask = GENMASK_ULL(pidx, 0) >> 1;
pp->nmask = GENMASK_ULL(pcnt - 1, pidx);
dev_dbg(&pp->ntb->dev, "Inbound db %#llx, prev %#llx, next %#llx\n",
pp->in_db, pp->pmask, pp->nmask);
}
static int pp_mask_events(struct pp_ctx *pp)
{
u64 db_mask, msg_mask;
int ret;
db_mask = ntb_db_valid_mask(pp->ntb);
ret = ntb_db_set_mask(pp->ntb, db_mask);
if (ret)
return ret;
/* Skip message events masking if unsupported */
if (ntb_msg_count(pp->ntb) < 1)
return 0;
msg_mask = ntb_msg_outbits(pp->ntb) | ntb_msg_inbits(pp->ntb);
return ntb_msg_set_mask(pp->ntb, msg_mask);
}
static int pp_setup_ctx(struct pp_ctx *pp)
{
int ret;
ret = ntb_set_ctx(pp->ntb, pp, &pp_ops);
if (ret)
return ret;
ntb_link_enable(pp->ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO);
/* Might be not necessary */
ntb_link_event(pp->ntb);
return 0;
}
static void pp_clear_ctx(struct pp_ctx *pp)
{
ntb_link_disable(pp->ntb);
ntb_clear_ctx(pp->ntb);
}
static void pp_setup_dbgfs(struct pp_ctx *pp)
{
struct pci_dev *pdev = pp->ntb->pdev;
pp->dbgfs_dir = debugfs_create_dir(pci_name(pdev), pp_dbgfs_topdir);
debugfs_create_atomic_t("count", 0600, pp->dbgfs_dir, &pp->count);
}
static void pp_clear_dbgfs(struct pp_ctx *pp)
{
debugfs_remove_recursive(pp->dbgfs_dir);
}
static int pp_probe(struct ntb_client *client, struct ntb_dev *ntb)
{
struct pp_ctx *pp;
int ret;
ret = pp_check_ntb(ntb);
if (ret)
return ret;
pp = pp_create_data(ntb);
if (IS_ERR(pp))
return PTR_ERR(pp);
pp_init_flds(pp);
ret = pp_mask_events(pp);
if (ret)
return ret;
ret = pp_setup_ctx(pp);
if (ret)
return ret;
pp_setup_dbgfs(pp);
return 0;
}
static void pp_remove(struct ntb_client *client, struct ntb_dev *ntb)
{
struct pp_ctx *pp = ntb->ctx;
pp_clear_dbgfs(pp);
pp_clear_ctx(pp);
pp_clear(pp);
}
static struct ntb_client pp_client = {
.ops = {
.probe = pp_probe,
.remove = pp_remove
}
};
static int __init pp_init(void)
{
int ret;
if (debugfs_initialized())
pp_dbgfs_topdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
ret = ntb_register_client(&pp_client);
if (ret)
debugfs_remove_recursive(pp_dbgfs_topdir);
return ret;
}
module_init(pp_init);
static void __exit pp_exit(void)
{
ntb_unregister_client(&pp_client);
debugfs_remove_recursive(pp_dbgfs_topdir);
}
module_exit(pp_exit);
File diff suppressed because it is too large Load Diff