net: wangxun: ngbe: support wangxun 1GbE driver

add support for wangxun 1GbE driver, source files and functions
are the same as wangxun oob ngbe-1.2.5.3.

Signed-off-by: Duanqiang Wen <duanqiangwen@net-swift.com>
This commit is contained in:
Duanqiang Wen 2024-04-07 16:49:26 +08:00 committed by Jianping Liu
parent 2ae9cf86d2
commit 467b4bd334
28 changed files with 44148 additions and 0 deletions

View File

@ -181,6 +181,7 @@ source "drivers/net/ethernet/toshiba/Kconfig"
source "drivers/net/ethernet/tundra/Kconfig"
source "drivers/net/ethernet/via/Kconfig"
source "drivers/net/ethernet/wiznet/Kconfig"
source "drivers/net/ethernet/wangxun/Kconfig"
source "drivers/net/ethernet/xilinx/Kconfig"
source "drivers/net/ethernet/xircom/Kconfig"

View File

@ -93,6 +93,7 @@ obj-$(CONFIG_NET_VENDOR_TOSHIBA) += toshiba/
obj-$(CONFIG_NET_VENDOR_TUNDRA) += tundra/
obj-$(CONFIG_NET_VENDOR_VIA) += via/
obj-$(CONFIG_NET_VENDOR_WIZNET) += wiznet/
obj-$(CONFIG_NET_VENDOR_WANGXUN) += wangxun/
obj-$(CONFIG_NET_VENDOR_XILINX) += xilinx/
obj-$(CONFIG_NET_VENDOR_XIRCOM) += xircom/
obj-$(CONFIG_NET_VENDOR_SYNOPSYS) += synopsys/

View File

@ -0,0 +1,32 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Wangxun network device configuration
#
config NET_VENDOR_WANGXUN
bool "Wangxun devices"
default y
help
If you have a network (Ethernet) card from Wangxun(R), say Y.
Note that the answer to this question doesn't directly affect the
kernel: saying N will just cause the configurator to skip all
the questions about Wangxun(R) cards. If you say Y, you will
be asked for your specific card in the following questions.
if NET_VENDOR_WANGXUN
config NGBE
tristate "Wangxun(R) GbE PCI Express adapters support"
depends on PCI
help
This driver supports Wangxun(R) GbE PCI Express family of
adapters.
More specific information on configuring the driver is in
<file:Documentation/networking/device_drivers/ethernet/wangxun/ngbe.rst>.
To compile this driver as a module, choose M here. The module
will be called ngbe.
endif # NET_VENDOR_WANGXUN

View File

@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the Wangxun network device drivers.
#
obj-$(CONFIG_NGBE) += ngbe/

View File

@ -0,0 +1,16 @@
obj-$(CONFIG_NGBE) += ngbe.o
ngbe-objs := ngbe_main.o \
ngbe_hw.o \
ngbe_phy.o \
ngbe_ethtool.o \
ngbe_lib.o \
ngbe_mbx.o \
ngbe_sriov.o \
ngbe_pcierr.o \
ngbe_param.o \
ngbe_procfs.o \
ngbe_ptp.o \
ngbe_sysfs.o \
ngbe_debugfs.o \
ngbe_kcompat.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,778 @@
/*
* WangXun Gigabit PCI Express Linux driver
* Copyright (c) 2015 - 2017 Beijing WangXun Technology Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* The full GNU General Public License is included in this distribution in
* the file called "COPYING".
*
*/
#include "ngbe.h"
#ifdef HAVE_NGBE_DEBUG_FS
#include <linux/debugfs.h>
#include <linux/module.h>
static struct dentry *ngbe_dbg_root;
static int ngbe_data_mode;
#define NGBE_DATA_FUNC(dm) ((dm) & ~0xFFFF)
#define NGBE_DATA_ARGS(dm) ((dm) & 0xFFFF)
enum ngbe_data_func {
NGBE_FUNC_NONE = (0 << 16),
NGBE_FUNC_DUMP_BAR = (1 << 16),
NGBE_FUNC_DUMP_RDESC = (2 << 16),
NGBE_FUNC_DUMP_TDESC = (3 << 16),
NGBE_FUNC_FLASH_READ = (4 << 16),
NGBE_FUNC_FLASH_WRITE = (5 << 16),
};
/**
* data operation
**/
static ssize_t
ngbe_simple_read_from_pcibar(struct ngbe_adapter *adapter, int res,
void __user *buf, size_t size, loff_t *ppos)
{
loff_t pos = *ppos;
u32 miss, len, limit = pci_resource_len(adapter->pdev, res);
if (pos < 0)
return 0;
limit = (pos + size <= limit ? pos + size : limit);
for (miss = 0; pos < limit && !miss; buf += len, pos += len) {
u32 val = 0, reg = round_down(pos, 4);
u32 off = pos - reg;
len = (reg + 4 <= limit ? 4 - off : 4 - off - (limit - reg - 4));
val = ngbe_rd32(adapter->io_addr + reg);
miss = copy_to_user(buf, &val + off, len);
}
size = pos - *ppos - miss;
*ppos += size;
return size;
}
static ssize_t
ngbe_simple_read_from_flash(struct ngbe_adapter *adapter,
void __user *buf, size_t size, loff_t *ppos)
{
struct ngbe_hw *hw = &adapter->hw;
loff_t pos = *ppos;
size_t ret = 0;
loff_t rpos, rtail;
void __user *to = buf;
size_t available = adapter->hw.flash.dword_size << 2;
if (pos < 0)
return -EINVAL;
if (pos >= available || !size)
return 0;
if (size > available - pos)
size = available - pos;
rpos = round_up(pos, 4);
rtail = round_down(pos + size, 4);
if (rtail < rpos)
return 0;
to += rpos - pos;
while (rpos <= rtail) {
u32 value = ngbe_rd32(adapter->io_addr + rpos);
if (hw->flash.ops.write_buffer(hw, rpos>>2, 1, &value)) {
ret = size;
break;
}
if (4 == copy_to_user(to, &value, 4)) {
ret = size;
break;
}
to += 4;
rpos += 4;
}
if (ret == size)
return -EFAULT;
size -= ret;
*ppos = pos + size;
return size;
}
static ssize_t
ngbe_simple_write_to_flash(struct ngbe_adapter *adapter,
const void __user *from, size_t size, loff_t *ppos, size_t available)
{
return size;
}
static ssize_t
ngbe_dbg_data_ops_read(struct file *filp, char __user *buffer,
size_t size, loff_t *ppos)
{
struct ngbe_adapter *adapter = filp->private_data;
u32 func = NGBE_DATA_FUNC(ngbe_data_mode);
rmb();
switch (func) {
case NGBE_FUNC_DUMP_BAR: {
u32 bar = NGBE_DATA_ARGS(ngbe_data_mode);
return ngbe_simple_read_from_pcibar(adapter, bar, buffer, size,
ppos);
}
case NGBE_FUNC_FLASH_READ: {
return ngbe_simple_read_from_flash(adapter, buffer, size, ppos);
}
case NGBE_FUNC_DUMP_RDESC: {
struct ngbe_ring *ring;
u32 queue = NGBE_DATA_ARGS(ngbe_data_mode);
if (queue >= adapter->num_rx_queues)
return 0;
queue += VMDQ_P(0) * adapter->queues_per_pool;
ring = adapter->rx_ring[queue];
return simple_read_from_buffer(buffer, size, ppos,
ring->desc, ring->size);
}
case NGBE_FUNC_DUMP_TDESC: {
struct ngbe_ring *ring;
u32 queue = NGBE_DATA_ARGS(ngbe_data_mode);
if (queue >= adapter->num_tx_queues)
return 0;
queue += VMDQ_P(0) * adapter->queues_per_pool;
ring = adapter->tx_ring[queue];
return simple_read_from_buffer(buffer, size, ppos,
ring->desc, ring->size);
}
default:
break;
}
return 0;
}
static ssize_t
ngbe_dbg_data_ops_write(struct file *filp,
const char __user *buffer,
size_t size, loff_t *ppos)
{
struct ngbe_adapter *adapter = filp->private_data;
u32 func = NGBE_DATA_FUNC(ngbe_data_mode);
rmb();
switch (func) {
case NGBE_FUNC_FLASH_WRITE: {
u32 size = NGBE_DATA_ARGS(ngbe_data_mode);
if (size > adapter->hw.flash.dword_size << 2)
size = adapter->hw.flash.dword_size << 2;
return ngbe_simple_write_to_flash(adapter, buffer, size, ppos, size);
}
default:
break;
}
return size;
}
static struct file_operations ngbe_dbg_data_ops_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = ngbe_dbg_data_ops_read,
.write = ngbe_dbg_data_ops_write,
};
/**
* reg_ops operation
**/
static char ngbe_dbg_reg_ops_buf[256] = "";
static ssize_t
ngbe_dbg_reg_ops_read(struct file *filp, char __user *buffer,
size_t count, loff_t *ppos)
{
struct ngbe_adapter *adapter = filp->private_data;
char *buf;
int len;
/* don't allow partial reads */
if (*ppos != 0)
return 0;
buf = kasprintf(GFP_KERNEL, "%s: mode=0x%08x\n%s\n",
adapter->netdev->name, ngbe_data_mode,
ngbe_dbg_reg_ops_buf);
if (!buf)
return -ENOMEM;
if (count < strlen(buf)) {
kfree(buf);
return -ENOSPC;
}
len = simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
kfree(buf);
return len;
}
static ssize_t
ngbe_dbg_reg_ops_write(struct file *filp,
const char __user *buffer,
size_t count, loff_t *ppos)
{
struct ngbe_adapter *adapter = filp->private_data;
char *pc = ngbe_dbg_reg_ops_buf;
int len;
/* don't allow partial writes */
if (*ppos != 0)
return 0;
if (count >= sizeof(ngbe_dbg_reg_ops_buf))
return -ENOSPC;
len = simple_write_to_buffer(ngbe_dbg_reg_ops_buf,
sizeof(ngbe_dbg_reg_ops_buf)-1,
ppos,
buffer,
count);
if (len < 0)
return len;
pc[len] = '\0';
if (strncmp(pc, "dump", 4) == 0) {
u32 mode = 0;
u16 args;
pc += 4;
pc += strspn(pc, " \t");
if (!strncmp(pc, "bar", 3)) {
pc += 3;
mode = NGBE_FUNC_DUMP_BAR;
} else if (!strncmp(pc, "rdesc", 5)) {
pc += 5;
mode = NGBE_FUNC_DUMP_RDESC;
} else if (!strncmp(pc, "tdesc", 5)) {
pc += 5;
mode = NGBE_FUNC_DUMP_TDESC;
} else {
ngbe_dump(adapter);
}
if (mode && 1 == sscanf(pc, "%hu", &args)) {
mode |= args;
}
ngbe_data_mode = mode;
} else if (strncmp(pc, "flash", 4) == 0) {
u32 mode = 0;
u16 args;
pc += 5;
pc += strspn(pc, " \t");
if (!strncmp(pc, "read", 3)) {
pc += 4;
mode = NGBE_FUNC_FLASH_READ;
} else if (!strncmp(pc, "write", 5)) {
pc += 5;
mode = NGBE_FUNC_FLASH_WRITE;
}
if (mode && 1 == sscanf(pc, "%hu", &args)) {
mode |= args;
}
ngbe_data_mode = mode;
} else if (strncmp(ngbe_dbg_reg_ops_buf, "write", 5) == 0) {
u32 reg, value;
int cnt;
cnt = sscanf(&ngbe_dbg_reg_ops_buf[5], "%x %x", &reg, &value);
if (cnt == 2) {
wr32(&adapter->hw, reg, value);
e_dev_info("write: 0x%08x = 0x%08x\n", reg, value);
} else {
e_dev_info("write <reg> <value>\n");
}
} else if (strncmp(ngbe_dbg_reg_ops_buf, "read", 4) == 0) {
u32 reg, value;
int cnt;
cnt = sscanf(&ngbe_dbg_reg_ops_buf[4], "%x", &reg);
if (cnt == 1) {
value = rd32(&adapter->hw, reg);
e_dev_info("read 0x%08x = 0x%08x\n", reg, value);
} else {
e_dev_info("read <reg>\n");
}
} else {
e_dev_info("Unknown command %s\n", ngbe_dbg_reg_ops_buf);
e_dev_info("Available commands:\n");
e_dev_info(" read <reg>\n");
e_dev_info(" write <reg> <value>\n");
}
return count;
}
static const struct file_operations ngbe_dbg_reg_ops_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = ngbe_dbg_reg_ops_read,
.write = ngbe_dbg_reg_ops_write,
};
/**
* netdev_ops operation
**/
static char ngbe_dbg_netdev_ops_buf[256] = "";
static ssize_t
ngbe_dbg_netdev_ops_read(struct file *filp,
char __user *buffer,
size_t count, loff_t *ppos)
{
struct ngbe_adapter *adapter = filp->private_data;
char *buf;
int len;
/* don't allow partial reads */
if (*ppos != 0)
return 0;
buf = kasprintf(GFP_KERNEL, "%s: mode=0x%08x\n%s\n",
adapter->netdev->name, ngbe_data_mode,
ngbe_dbg_netdev_ops_buf);
if (!buf)
return -ENOMEM;
if (count < strlen(buf)) {
kfree(buf);
return -ENOSPC;
}
len = simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
kfree(buf);
return len;
}
static ssize_t
ngbe_dbg_netdev_ops_write(struct file *filp,
const char __user *buffer,
size_t count, loff_t *ppos)
{
struct ngbe_adapter *adapter = filp->private_data;
int len;
/* don't allow partial writes */
if (*ppos != 0)
return 0;
if (count >= sizeof(ngbe_dbg_netdev_ops_buf))
return -ENOSPC;
len = simple_write_to_buffer(ngbe_dbg_netdev_ops_buf,
sizeof(ngbe_dbg_netdev_ops_buf)-1,
ppos,
buffer,
count);
if (len < 0)
return len;
ngbe_dbg_netdev_ops_buf[len] = '\0';
if (strncmp(ngbe_dbg_netdev_ops_buf, "tx_timeout", 10) == 0) {
#if defined(HAVE_TX_TIMEOUT_TXQUEUE)
adapter->netdev->netdev_ops->ndo_tx_timeout(adapter->netdev, 0);
#elif defined(HAVE_NET_DEVICE_OPS)
adapter->netdev->netdev_ops->ndo_tx_timeout(adapter->netdev);
#else
adapter->netdev->tx_timeout(adapter->netdev);
#endif /* HAVE_NET_DEVICE_OPS */
e_dev_info("tx_timeout called\n");
} else {
e_dev_info("Unknown command: %s\n", ngbe_dbg_netdev_ops_buf);
e_dev_info("Available commands:\n");
e_dev_info(" tx_timeout\n");
}
return count;
}
static struct file_operations ngbe_dbg_netdev_ops_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = ngbe_dbg_netdev_ops_read,
.write = ngbe_dbg_netdev_ops_write,
};
/**
* ngbe_dbg_adapter_init - setup the debugfs directory for the adapter
* @adapter: the adapter that is starting up
**/
void ngbe_dbg_adapter_init(struct ngbe_adapter *adapter)
{
const char *name = pci_name(adapter->pdev);
struct dentry *pfile;
adapter->ngbe_dbg_adapter = debugfs_create_dir(name, ngbe_dbg_root);
if (!adapter->ngbe_dbg_adapter) {
e_dev_err("debugfs entry for %s failed\n", name);
return;
}
pfile = debugfs_create_file("data", 0600,
adapter->ngbe_dbg_adapter, adapter,
&ngbe_dbg_data_ops_fops);
if (!pfile)
e_dev_err("debugfs netdev_ops for %s failed\n", name);
pfile = debugfs_create_file("reg_ops", 0600,
adapter->ngbe_dbg_adapter, adapter,
&ngbe_dbg_reg_ops_fops);
if (!pfile)
e_dev_err("debugfs reg_ops for %s failed\n", name);
pfile = debugfs_create_file("netdev_ops", 0600,
adapter->ngbe_dbg_adapter, adapter,
&ngbe_dbg_netdev_ops_fops);
if (!pfile)
e_dev_err("debugfs netdev_ops for %s failed\n", name);
}
/**
* ngbe_dbg_adapter_exit - clear out the adapter's debugfs entries
* @pf: the pf that is stopping
**/
void ngbe_dbg_adapter_exit(struct ngbe_adapter *adapter)
{
if (adapter->ngbe_dbg_adapter)
debugfs_remove_recursive(adapter->ngbe_dbg_adapter);
adapter->ngbe_dbg_adapter = NULL;
}
/**
* ngbe_dbg_init - start up debugfs for the driver
**/
void ngbe_dbg_init(void)
{
ngbe_dbg_root = debugfs_create_dir(ngbe_driver_name, NULL);
if (ngbe_dbg_root == NULL)
pr_err("init of debugfs failed\n");
}
/**
* ngbe_dbg_exit - clean out the driver's debugfs entries
**/
void ngbe_dbg_exit(void)
{
debugfs_remove_recursive(ngbe_dbg_root);
}
#endif /* HAVE_NGBE_DEBUG_FS */
struct ngbe_reg_info {
u32 offset;
u32 length;
char *name;
};
static struct ngbe_reg_info ngbe_reg_info_tbl[] = {
/* General Registers */
{NGBE_CFG_PORT_CTL, 1, "CTRL"},
{NGBE_CFG_PORT_ST, 1, "STATUS"},
/* RX Registers */
{NGBE_PX_RR_CFG(0), 1, "SRRCTL"},
{NGBE_PX_RR_RP(0), 1, "RDH"},
{NGBE_PX_RR_WP(0), 1, "RDT"},
{NGBE_PX_RR_CFG(0), 1, "RXDCTL"},
{NGBE_PX_RR_BAL(0), 1, "RDBAL"},
{NGBE_PX_RR_BAH(0), 1, "RDBAH"},
/* TX Registers */
{NGBE_PX_TR_BAL(0), 1, "TDBAL"},
{NGBE_PX_TR_BAH(0), 1, "TDBAH"},
{NGBE_PX_TR_RP(0), 1, "TDH"},
{NGBE_PX_TR_WP(0), 1, "TDT"},
{NGBE_PX_TR_CFG(0), 1, "TXDCTL"},
/* MACVLAN */
{NGBE_PSR_MAC_SWC_VM, 128, "PSR_MAC_SWC_VM"},
{NGBE_PSR_MAC_SWC_AD_L, 32, "PSR_MAC_SWC_AD"},
{NGBE_PSR_VLAN_TBL(0), 128, "PSR_VLAN_TBL"},
/* List Terminator */
{ .name = NULL }
};
/**
* ngbe_regdump - register printout routine
**/
static void
ngbe_regdump(struct ngbe_hw *hw, struct ngbe_reg_info *reg_info)
{
#if 0
int i, n = 0;
u32 buffer[32*8];
switch (reg_info->offset) {
case NGBE_PSR_MAC_SWC_AD_L:
for (i = 0; i < reg_info->length; i++) {
wr32(hw, NGBE_PSR_MAC_SWC_IDX, i);
buffer[n++] =
rd32(hw, NGBE_PSR_MAC_SWC_AD_H);
buffer[n++] =
rd32(hw, NGBE_PSR_MAC_SWC_AD_L);
}
break;
default:
for (i = 0; i < reg_info->length; i++) {
buffer[n++] = rd32(hw,
reg_info->offset + 4*i);
}
break;
}
for (i = 0; n && i < 32; i++) {
pr_info("%-20s[%02x-%02x]", reg_info->name, i*8, i*8 + 7);
for (j = 0; n && j < 8; j++, n--)
pr_cont(" %08x", buffer[i*8 + j]);
pr_cont("\n");
}
BUG_ON(n);
#endif
}
/**
* ngbe_dump - Print registers, tx-rings and rx-rings
**/
void ngbe_dump(struct ngbe_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
struct ngbe_hw *hw = &adapter->hw;
struct ngbe_reg_info *reg_info;
int n = 0;
struct ngbe_ring *tx_ring;
struct ngbe_tx_buffer *tx_buffer;
union ngbe_tx_desc *tx_desc;
struct my_u0 { u64 a; u64 b; } *u0;
struct ngbe_ring *rx_ring;
#ifndef CONFIG_NGBE_DISABLE_PACKET_SPLIT
union ngbe_rx_desc *rx_desc;
struct ngbe_rx_buffer *rx_buffer_info;
u32 staterr;
#endif
int i = 0;
if (!netif_msg_hw(adapter))
return;
/* Print Registers */
dev_info(&adapter->pdev->dev, "Register Dump\n");
pr_info(" Register Name Value\n");
for (reg_info = ngbe_reg_info_tbl; reg_info->name; reg_info++) {
ngbe_regdump(hw, reg_info);
}
/* Print TX Ring Summary */
if (!netdev || !netif_running(netdev))
return;
dev_info(&adapter->pdev->dev, "TX Rings Summary\n");
pr_info(" %s %s %s %s\n",
"Queue [NTU] [NTC] [bi(ntc)->dma ]",
"leng", "ntw", "timestamp");
for (n = 0; n < adapter->num_tx_queues; n++) {
tx_ring = adapter->tx_ring[n];
tx_buffer = &tx_ring->tx_buffer_info[tx_ring->next_to_clean];
pr_info(" %5d %5X %5X %016llX %08X %p %016llX\n",
n, tx_ring->next_to_use, tx_ring->next_to_clean,
(u64)dma_unmap_addr(tx_buffer, dma),
dma_unmap_len(tx_buffer, len),
tx_buffer->next_to_watch,
(u64)tx_buffer->time_stamp);
}
/* Print TX Rings */
if (!netif_msg_tx_done(adapter))
goto rx_ring_summary;
dev_info(&adapter->pdev->dev, "TX Rings Dump\n");
/* Transmit Descriptor Formats
*
* Transmit Descriptor (Read)
* +--------------------------------------------------------------+
* 0 | Buffer Address [63:0] |
* +--------------------------------------------------------------+
* 8 |PAYLEN |POPTS|CC|IDX |STA |DCMD |DTYP |MAC |RSV |DTALEN |
* +--------------------------------------------------------------+
* 63 46 45 40 39 38 36 35 32 31 24 23 20 19 18 17 16 15 0
*
* Transmit Descriptor (Write-Back)
* +--------------------------------------------------------------+
* 0 | RSV [63:0] |
* +--------------------------------------------------------------+
* 8 | RSV | STA | RSV |
* +--------------------------------------------------------------+
* 63 36 35 32 31 0
*/
for (n = 0; n < adapter->num_tx_queues; n++) {
tx_ring = adapter->tx_ring[n];
pr_info("------------------------------------\n");
pr_info("TX QUEUE INDEX = %d\n", tx_ring->queue_index);
pr_info("------------------------------------\n");
pr_info("%s%s %s %s %s %s\n",
"T [desc] [address 63:0 ] ",
"[PlPOIdStDDt Ln] [bi->dma ] ",
"leng", "ntw", "timestamp", "bi->skb");
for (i = 0; tx_ring->desc && (i < tx_ring->count); i++) {
tx_desc = NGBE_TX_DESC(tx_ring, i);
tx_buffer = &tx_ring->tx_buffer_info[i];
u0 = (struct my_u0 *)tx_desc;
if (dma_unmap_len(tx_buffer, len) > 0) {
pr_info("T [0x%03X] %016llX %016llX %016llX "
"%08X %p %016llX %p",
i,
le64_to_cpu(u0->a),
le64_to_cpu(u0->b),
(u64)dma_unmap_addr(tx_buffer, dma),
dma_unmap_len(tx_buffer, len),
tx_buffer->next_to_watch,
(u64)tx_buffer->time_stamp,
tx_buffer->skb);
if (i == tx_ring->next_to_use &&
i == tx_ring->next_to_clean)
pr_cont(" NTC/U\n");
else if (i == tx_ring->next_to_use)
pr_cont(" NTU\n");
else if (i == tx_ring->next_to_clean)
pr_cont(" NTC\n");
else
pr_cont("\n");
if (netif_msg_pktdata(adapter) &&
tx_buffer->skb)
print_hex_dump(KERN_INFO, "",
DUMP_PREFIX_ADDRESS, 16, 1,
tx_buffer->skb->data,
dma_unmap_len(tx_buffer, len),
true);
}
}
}
/* Print RX Rings Summary */
rx_ring_summary:
dev_info(&adapter->pdev->dev, "RX Rings Summary\n");
pr_info("Queue [NTU] [NTC]\n");
for (n = 0; n < adapter->num_rx_queues; n++) {
rx_ring = adapter->rx_ring[n];
pr_info("%5d %5X %5X\n",
n, rx_ring->next_to_use, rx_ring->next_to_clean);
}
/* Print RX Rings */
if (!netif_msg_rx_status(adapter))
return;
dev_info(&adapter->pdev->dev, "RX Rings Dump\n");
/* Receive Descriptor Formats
*
* Receive Descriptor (Read)
* 63 1 0
* +-----------------------------------------------------+
* 0 | Packet Buffer Address [63:1] |A0/NSE|
* +----------------------------------------------+------+
* 8 | Header Buffer Address [63:1] | DD |
* +-----------------------------------------------------+
*
*
* Receive Descriptor (Write-Back)
*
* 63 48 47 32 31 30 21 20 17 16 4 3 0
* +------------------------------------------------------+
* 0 |RSS / Frag Checksum|SPH| HDR_LEN |RSC- |Packet| RSS |
* |/ RTT / PCoE_PARAM | | | CNT | Type | Type |
* |/ Flow Dir Flt ID | | | | | |
* +------------------------------------------------------+
* 8 | VLAN Tag | Length |Extended Error| Xtnd Status/NEXTP |
* +------------------------------------------------------+
* 63 48 47 32 31 20 19 0
*/
#ifndef CONFIG_NGBE_DISABLE_PACKET_SPLIT
for (n = 0; n < adapter->num_rx_queues; n++) {
rx_ring = adapter->rx_ring[n];
pr_info("------------------------------------\n");
pr_info("RX QUEUE INDEX = %d\n", rx_ring->queue_index);
pr_info("------------------------------------\n");
pr_info("%s%s%s",
"R [desc] [ PktBuf A0] ",
"[ HeadBuf DD] [bi->dma ] [bi->skb ] ",
"<-- Adv Rx Read format\n");
pr_info("%s%s%s",
"RWB[desc] [PcsmIpSHl PtRs] ",
"[vl er S cks ln] ---------------- [bi->skb ] ",
"<-- Adv Rx Write-Back format\n");
for (i = 0; i < rx_ring->count; i++) {
rx_buffer_info = &rx_ring->rx_buffer_info[i];
rx_desc = NGBE_RX_DESC(rx_ring, i);
u0 = (struct my_u0 *)rx_desc;
staterr = le32_to_cpu(rx_desc->wb.upper.status_error);
if (staterr & NGBE_RXD_STAT_DD) {
/* Descriptor Done */
pr_info("RWB[0x%03X] %016llX "
"%016llX ---------------- %p", i,
le64_to_cpu(u0->a),
le64_to_cpu(u0->b),
rx_buffer_info->skb);
} else {
pr_info("R [0x%03X] %016llX "
"%016llX %016llX %p", i,
le64_to_cpu(u0->a),
le64_to_cpu(u0->b),
(u64)rx_buffer_info->page_dma,
rx_buffer_info->skb);
if (netif_msg_pktdata(adapter) &&
rx_buffer_info->page_dma) {
print_hex_dump(KERN_INFO, "",
DUMP_PREFIX_ADDRESS, 16, 1,
page_address(rx_buffer_info->page) +
rx_buffer_info->page_offset,
ngbe_rx_bufsz(rx_ring), true);
}
}
if (i == rx_ring->next_to_use)
pr_cont(" NTU\n");
else if (i == rx_ring->next_to_clean)
pr_cont(" NTC\n");
else
pr_cont("\n");
}
}
#endif
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,280 @@
/*
* WangXun Gigabit PCI Express Linux driver
* Copyright (c) 2015 - 2017 Beijing WangXun Technology Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* The full GNU General Public License is included in this distribution in
* the file called "COPYING".
*/
#ifndef _NGBE_HW_H_
#define _NGBE_HW_H_
#define NGBE_EMC_INTERNAL_DATA 0x00
#define NGBE_EMC_INTERNAL_THERM_LIMIT 0x20
#define NGBE_EMC_DIODE1_DATA 0x01
#define NGBE_EMC_DIODE1_THERM_LIMIT 0x19
#define NGBE_EMC_DIODE2_DATA 0x23
#define NGBE_EMC_DIODE2_THERM_LIMIT 0x1A
#define NGBE_EMC_DIODE3_DATA 0x2A
#define NGBE_EMC_DIODE3_THERM_LIMIT 0x30
#define SPI_CLK_DIV 3
#define SPI_CMD_ERASE_CHIP 4 // SPI erase chip command
#define SPI_CMD_ERASE_SECTOR 3 // SPI erase sector command
#define SPI_CMD_WRITE_DWORD 0 // SPI write a dword command
#define SPI_CMD_READ_DWORD 1 // SPI read a dword command
#define SPI_CMD_USER_CMD 5 // SPI user command
#define SPI_CLK_CMD_OFFSET 28 // SPI command field offset in Command register
#define SPI_CLK_DIV_OFFSET 25 // SPI clock divide field offset in Command register
#define SPI_TIME_OUT_VALUE 10000
#define SPI_SECTOR_SIZE (4 * 1024) // FLASH sector size is 64KB
#define SPI_H_CMD_REG_ADDR 0x10104 // SPI Command register address
#define SPI_H_DAT_REG_ADDR 0x10108 // SPI Data register address
#define SPI_H_STA_REG_ADDR 0x1010c // SPI Status register address
#define SPI_H_USR_CMD_REG_ADDR 0x10110 // SPI User Command register address
#define SPI_CMD_CFG1_ADDR 0x10118 // Flash command configuration register 1
#define MISC_RST_REG_ADDR 0x1000c // Misc reset register address
#define MGR_FLASH_RELOAD_REG_ADDR 0x101a0 // MGR reload flash read
#define MAC_ADDR0_WORD0_OFFSET_1G 0x006000c // MAC Address for LAN0, stored in external FLASH
#define MAC_ADDR0_WORD1_OFFSET_1G 0x0060014
#define MAC_ADDR1_WORD0_OFFSET_1G 0x006800c // MAC Address for LAN1, stored in external FLASH
#define MAC_ADDR1_WORD1_OFFSET_1G 0x0068014
#define MAC_ADDR2_WORD0_OFFSET_1G 0x007000c // MAC Address for LAN2, stored in external FLASH
#define MAC_ADDR2_WORD1_OFFSET_1G 0x0070014
#define MAC_ADDR3_WORD0_OFFSET_1G 0x007800c // MAC Address for LAN3, stored in external FLASH
#define MAC_ADDR3_WORD1_OFFSET_1G 0x0078014
#define PRODUCT_SERIAL_NUM_OFFSET_1G 0x00f0000 // Product Serial Number, stored in external FLASH last sector
struct ngbe_hic_read_cab {
union ngbe_hic_hdr2 hdr;
union {
u8 d8[252];
u16 d16[126];
u32 d32[63];
} dbuf;
};
/**
* Packet Type decoding
**/
/* ngbe_dec_ptype.mac: outer mac */
enum ngbe_dec_ptype_mac {
NGBE_DEC_PTYPE_MAC_IP = 0,
NGBE_DEC_PTYPE_MAC_L2 = 2,
NGBE_DEC_PTYPE_MAC_FCOE = 3,
};
/* ngbe_dec_ptype.[e]ip: outer&encaped ip */
#define NGBE_DEC_PTYPE_IP_FRAG (0x4)
enum ngbe_dec_ptype_ip {
NGBE_DEC_PTYPE_IP_NONE = 0,
NGBE_DEC_PTYPE_IP_IPV4 = 1,
NGBE_DEC_PTYPE_IP_IPV6 = 2,
NGBE_DEC_PTYPE_IP_FGV4 =
(NGBE_DEC_PTYPE_IP_FRAG | NGBE_DEC_PTYPE_IP_IPV4),
NGBE_DEC_PTYPE_IP_FGV6 =
(NGBE_DEC_PTYPE_IP_FRAG | NGBE_DEC_PTYPE_IP_IPV6),
};
/* ngbe_dec_ptype.etype: encaped type */
enum ngbe_dec_ptype_etype {
NGBE_DEC_PTYPE_ETYPE_NONE = 0,
NGBE_DEC_PTYPE_ETYPE_IPIP = 1, /* IP+IP */
NGBE_DEC_PTYPE_ETYPE_IG = 2, /* IP+GRE */
NGBE_DEC_PTYPE_ETYPE_IGM = 3, /* IP+GRE+MAC */
NGBE_DEC_PTYPE_ETYPE_IGMV = 4, /* IP+GRE+MAC+VLAN */
};
/* ngbe_dec_ptype.proto: payload proto */
enum ngbe_dec_ptype_prot {
NGBE_DEC_PTYPE_PROT_NONE = 0,
NGBE_DEC_PTYPE_PROT_UDP = 1,
NGBE_DEC_PTYPE_PROT_TCP = 2,
NGBE_DEC_PTYPE_PROT_SCTP = 3,
NGBE_DEC_PTYPE_PROT_ICMP = 4,
NGBE_DEC_PTYPE_PROT_TS = 5, /* time sync */
};
/* ngbe_dec_ptype.layer: payload layer */
enum ngbe_dec_ptype_layer {
NGBE_DEC_PTYPE_LAYER_NONE = 0,
NGBE_DEC_PTYPE_LAYER_PAY2 = 1,
NGBE_DEC_PTYPE_LAYER_PAY3 = 2,
NGBE_DEC_PTYPE_LAYER_PAY4 = 3,
};
struct ngbe_dec_ptype {
u32 ptype:8;
u32 known:1;
u32 mac:2; /* outer mac */
u32 ip:3; /* outer ip*/
u32 etype:3; /* encaped type */
u32 eip:3; /* encaped ip */
u32 prot:4; /* payload proto */
u32 layer:3; /* payload layer */
};
typedef struct ngbe_dec_ptype ngbe_dptype;
u16 ngbe_get_pcie_msix_count(struct ngbe_hw *hw);
int ngbe_init_hw(struct ngbe_hw *hw);
int ngbe_start_hw(struct ngbe_hw *hw);
int ngbe_clear_hw_cntrs(struct ngbe_hw *hw);
int ngbe_read_pba_string(struct ngbe_hw *hw, u8 *pba_num,
u32 pba_num_size);
int ngbe_get_mac_addr(struct ngbe_hw *hw, u8 *mac_addr);
int ngbe_get_bus_info(struct ngbe_hw *hw);
void ngbe_set_pci_config_data(struct ngbe_hw *hw, u16 link_status);
void ngbe_set_lan_id_multi_port_pcie(struct ngbe_hw *hw);
int ngbe_stop_adapter(struct ngbe_hw *hw);
int ngbe_led_on(struct ngbe_hw *hw, u32 index);
int ngbe_led_off(struct ngbe_hw *hw, u32 index);
int ngbe_set_rar(struct ngbe_hw *hw, u32 index, u8 *addr, u64 pools,
u32 enable_addr);
int ngbe_clear_rar(struct ngbe_hw *hw, u32 index);
int ngbe_init_rx_addrs(struct ngbe_hw *hw);
int ngbe_update_mc_addr_list(struct ngbe_hw *hw, u8 *mc_addr_list,
u32 mc_addr_count,
ngbe_mc_addr_itr func, bool clear);
int ngbe_update_uc_addr_list(struct ngbe_hw *hw, u8 *addr_list,
u32 addr_count, ngbe_mc_addr_itr func);
int ngbe_enable_mc(struct ngbe_hw *hw);
int ngbe_disable_mc(struct ngbe_hw *hw);
int ngbe_disable_sec_rx_path(struct ngbe_hw *hw);
int ngbe_enable_sec_rx_path(struct ngbe_hw *hw);
int ngbe_fc_enable(struct ngbe_hw *hw);
void ngbe_fc_autoneg(struct ngbe_hw *hw);
int ngbe_setup_fc(struct ngbe_hw *hw);
int ngbe_validate_mac_addr(u8 *mac_addr);
int ngbe_acquire_swfw_sync(struct ngbe_hw *hw, u32 mask);
void ngbe_release_swfw_sync(struct ngbe_hw *hw, u32 mask);
int ngbe_disable_pcie_master(struct ngbe_hw *hw);
int ngbe_set_vmdq(struct ngbe_hw *hw, u32 rar, u32 vmdq);
int ngbe_set_vmdq_san_mac(struct ngbe_hw *hw, u32 vmdq);
int ngbe_clear_vmdq(struct ngbe_hw *hw, u32 rar, u32 vmdq);
int ngbe_insert_mac_addr(struct ngbe_hw *hw, u8 *addr, u32 vmdq);
int ngbe_init_uta_tables(struct ngbe_hw *hw);
int ngbe_set_vfta(struct ngbe_hw *hw, u32 vlan,
u32 vind, bool vlan_on);
int ngbe_set_vlvf(struct ngbe_hw *hw, u32 vlan, u32 vind,
bool vlan_on, bool *vfta_changed);
int ngbe_clear_vfta(struct ngbe_hw *hw);
int ngbe_find_vlvf_slot(struct ngbe_hw *hw, u32 vlan);
void ngbe_set_mac_anti_spoofing(struct ngbe_hw *hw, bool enable, int pf);
void ngbe_set_vlan_anti_spoofing(struct ngbe_hw *hw, bool enable, int vf);
void ngbe_set_ethertype_anti_spoofing(struct ngbe_hw *hw,
bool enable, int vf);
int ngbe_get_device_caps(struct ngbe_hw *hw, u16 *device_caps);
void ngbe_set_rxpba(struct ngbe_hw *hw, int num_pb, u32 headroom,
int strategy);
int ngbe_set_fw_drv_ver(struct ngbe_hw *hw, u8 maj, u8 min,
u8 build, u8 ver);
int ngbe_reset_hostif(struct ngbe_hw *hw);
u8 ngbe_calculate_checksum(u8 *buffer, u32 length);
int ngbe_host_interface_command(struct ngbe_hw *hw, u32 *buffer,
u32 length, u32 timeout, bool return_data);
void ngbe_clear_tx_pending(struct ngbe_hw *hw);
void ngbe_stop_mac_link_on_d3(struct ngbe_hw *hw);
bool ngbe_mng_present(struct ngbe_hw *hw);
bool ngbe_check_mng_access(struct ngbe_hw *hw);
int ngbe_get_thermal_sensor_data(struct ngbe_hw *hw);
int ngbe_init_thermal_sensor_thresh(struct ngbe_hw *hw);
void ngbe_enable_rx(struct ngbe_hw *hw);
void ngbe_disable_rx(struct ngbe_hw *hw);
int ngbe_setup_mac_link_multispeed_fiber(struct ngbe_hw *hw,
u32 speed,
bool autoneg_wait_to_complete);
int ngbe_check_flash_load(struct ngbe_hw *hw, u32 check_bit);
/* @ngbe_api.h */
void ngbe_atr_compute_perfect_hash(union ngbe_atr_input *input,
union ngbe_atr_input *mask);
u32 ngbe_atr_compute_sig_hash(union ngbe_atr_hash_dword input,
union ngbe_atr_hash_dword common);
int ngbe_get_link_capabilities(struct ngbe_hw *hw,
u32 *speed, bool *autoneg);
enum ngbe_media_type ngbe_get_media_type(struct ngbe_hw *hw);
void ngbe_disable_tx_laser_multispeed_fiber(struct ngbe_hw *hw);
void ngbe_enable_tx_laser_multispeed_fiber(struct ngbe_hw *hw);
void ngbe_flap_tx_laser_multispeed_fiber(struct ngbe_hw *hw);
void ngbe_set_hard_rate_select_speed(struct ngbe_hw *hw,
u32 speed);
int ngbe_setup_mac_link(struct ngbe_hw *hw, u32 speed,
bool autoneg_wait_to_complete);
void ngbe_init_mac_link_ops(struct ngbe_hw *hw);
int ngbe_reset_hw(struct ngbe_hw *hw);
int ngbe_identify_phy(struct ngbe_hw *hw);
void ngbe_init_ops_common(struct ngbe_hw *hw);
int ngbe_enable_rx_dma(struct ngbe_hw *hw, u32 regval);
void ngbe_init_ops(struct ngbe_hw *hw);
int ngbe_setup_eee(struct ngbe_hw *hw, bool enable_eee);
int ngbe_init_flash_params(struct ngbe_hw *hw);
int ngbe_read_flash_buffer(struct ngbe_hw *hw, u32 offset,
u32 dwords, u32 *data);
int ngbe_write_flash_buffer(struct ngbe_hw *hw, u32 offset,
u32 dwords, u32 *data);
int ngbe_read_eeprom(struct ngbe_hw *hw,
u16 offset, u16 *data);
int ngbe_read_eeprom_buffer(struct ngbe_hw *hw, u16 offset,
u16 words, u16 *data);
int ngbe_init_eeprom_params(struct ngbe_hw *hw);
int ngbe_update_eeprom_checksum(struct ngbe_hw *hw);
int ngbe_calc_eeprom_checksum(struct ngbe_hw *hw);
int ngbe_validate_eeprom_checksum(struct ngbe_hw *hw,
u16 *checksum_val);
int ngbe_upgrade_flash(struct ngbe_hw *hw, u32 region,
const u8 *data, u32 size);
int ngbe_write_ee_hostif_buffer(struct ngbe_hw *hw,
u16 offset, u16 words, u16 *data);
int ngbe_write_ee_hostif(struct ngbe_hw *hw, u16 offset,
u16 data);
int ngbe_write_ee_hostif32(struct ngbe_hw *hw, u16 offset,
u32 data);
int ngbe_read_ee_hostif_buffer(struct ngbe_hw *hw,
u16 offset, u16 words, u16 *data);
int ngbe_read_ee_hostif(struct ngbe_hw *hw, u16 offset, u16 *data);
int ngbe_read_ee_hostif32(struct ngbe_hw *hw, u16 offset, u32 *data);
u32 ngbe_rd32_epcs(struct ngbe_hw *hw, u32 addr);
void ngbe_wr32_epcs(struct ngbe_hw *hw, u32 addr, u32 data);
void ngbe_wr32_ephy(struct ngbe_hw *hw, u32 addr, u32 data);
int ngbe_upgrade_flash_hostif(struct ngbe_hw *hw, u32 region,
const u8 *data, u32 size);
int ngbe_eepromcheck_cap(struct ngbe_hw *hw, u16 offset,
u32 *data);
int ngbe_phy_signal_set(struct ngbe_hw *hw);
int ngbe_flash_read_dword(struct ngbe_hw *hw, u32 addr, u32 *data);
int ngbe_is_lldp(struct ngbe_hw *hw);
int ngbe_hic_write_lldp(struct ngbe_hw *hw, u32 open);
#endif /* _NGBE_HW_H_ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,806 @@
/*
* WangXun Gigabit PCI Express Linux driver
* Copyright (c) 2015 - 2017 Beijing WangXun Technology Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* The full GNU General Public License is included in this distribution in
* the file called "COPYING".
*
*/
#include "ngbe.h"
#include "ngbe_sriov.h"
/**
* ngbe_cache_ring_vmdq - Descriptor ring to register mapping for VMDq
* @adapter: board private structure to initialize
*
* Cache the descriptor ring offsets for VMDq to the assigned rings. It
* will also try to cache the proper offsets if RSS/FCoE/SRIOV are enabled along
* with VMDq.
*
**/
static bool ngbe_cache_ring_vmdq(struct ngbe_adapter *adapter)
{
struct ngbe_ring_feature *vmdq = &adapter->ring_feature[RING_F_VMDQ];
//struct ngbe_ring_feature *rss = &adapter->ring_feature[RING_F_RSS];
int i;
u16 reg_idx;
/* only proceed if VMDq is enabled */
if (!(adapter->flags & NGBE_FLAG_VMDQ_ENABLED))
return false;
/* start at VMDq register offset for SR-IOV enabled setups */
reg_idx = vmdq->offset;
for (i = 0; i < adapter->num_rx_queues; i++) {
/* If we are greater than indices move to next pool */
adapter->rx_ring[i]->reg_idx = reg_idx + i;
}
reg_idx = vmdq->offset;
for (i = 0; i < adapter->num_tx_queues; i++) {
/* If we are greater than indices move to next pool */
adapter->tx_ring[i]->reg_idx = reg_idx + i;
}
return true;
}
/**
* ngbe_cache_ring_rss - Descriptor ring to register mapping for RSS
* @adapter: board private structure to initialize
*
* Cache the descriptor ring offsets for RSS, ATR, FCoE, and SR-IOV.
*
**/
static bool ngbe_cache_ring_rss(struct ngbe_adapter *adapter)
{
u16 i, reg_i;
for (i = 0; i < adapter->num_rx_queues; i++)
adapter->rx_ring[i]->reg_idx = i;
for (i = 0, reg_i = 0; i < adapter->num_tx_queues; i++, reg_i++)
adapter->tx_ring[i]->reg_idx = reg_i;
for (i = 0; i < adapter->num_xdp_queues; i++, reg_i++)
adapter->xdp_ring[i]->reg_idx = reg_i;
return true;
}
/**
* ngbe_cache_ring_register - Descriptor ring to register mapping
* @adapter: board private structure to initialize
*
* Once we know the feature-set enabled for the device, we'll cache
* the register offset the descriptor ring is assigned to.
*
* Note, the order the various feature calls is important. It must start with
* the "most" features enabled at the same time, then trickle down to the
* least amount of features turned on at once.
**/
static void ngbe_cache_ring_register(struct ngbe_adapter *adapter)
{
if (ngbe_cache_ring_vmdq(adapter))
return;
ngbe_cache_ring_rss(adapter);
}
#define NGBE_RSS_64Q_MASK 0x3F
#define NGBE_RSS_16Q_MASK 0xF
#define NGBE_RSS_8Q_MASK 0x7
#define NGBE_RSS_4Q_MASK 0x3
#define NGBE_RSS_2Q_MASK 0x1
#define NGBE_RSS_DISABLED_MASK 0x0
/**
* ngbe_set_vmdq_queues: Allocate queues for VMDq devices
* @adapter: board private structure to initialize
*
* When VMDq (Virtual Machine Devices queue) is enabled, allocate queues
* and VM pools where appropriate. If RSS is available, then also try and
* enable RSS and map accordingly.
*
**/
static bool ngbe_set_vmdq_queues(struct ngbe_adapter *adapter)
{
u16 vmdq_i = adapter->ring_feature[RING_F_VMDQ].limit;
u16 vmdq_m = 0;
u16 rss_i = adapter->ring_feature[RING_F_RSS].limit;
u16 rss_m = NGBE_RSS_DISABLED_MASK;
/* only proceed if VMDq is enabled */
if (!(adapter->flags & NGBE_FLAG_VMDQ_ENABLED))
return false;
/* Add starting offset to total pool count */
vmdq_i += adapter->ring_feature[RING_F_VMDQ].offset;
/* double check we are limited to maximum pools */
vmdq_i = min_t(u16, NGBE_MAX_VMDQ_INDICES, vmdq_i);
/* when VMDQ on, disable RSS */
rss_i = 1;
/* remove the starting offset from the pool count */
vmdq_i -= adapter->ring_feature[RING_F_VMDQ].offset;
/* save features for later use */
adapter->ring_feature[RING_F_VMDQ].indices = vmdq_i;
adapter->ring_feature[RING_F_VMDQ].mask = vmdq_m;
/* limit RSS based on user input and save for later use */
adapter->ring_feature[RING_F_RSS].indices = rss_i;
adapter->ring_feature[RING_F_RSS].mask = rss_m;
adapter->queues_per_pool = rss_i;
adapter->num_rx_queues = vmdq_i * rss_i;
#ifdef HAVE_TX_MQ
adapter->num_tx_queues = vmdq_i * rss_i;
#else
adapter->num_tx_queues = vmdq_i;
#endif /* HAVE_TX_MQ */
adapter->num_xdp_queues = 0;
return true;
}
#ifdef HAVE_XDP_SUPPORT
static int ngbe_xdp_queues(struct ngbe_adapter *adapter)
{
int queues = min_t(int, NGBE_MAX_XDP_QS, nr_cpu_ids);
return adapter->xdp_prog ? queues : 0;
}
#endif
/**
* ngbe_set_rss_queues: Allocate queues for RSS
* @adapter: board private structure to initialize
*
* This is our "base" multiqueue mode. RSS (Receive Side Scaling) will try
* to allocate one Rx queue per CPU, and if available, one Tx queue per CPU.
*
**/
static bool ngbe_set_rss_queues(struct ngbe_adapter *adapter)
{
struct ngbe_ring_feature *f;
u16 rss_i;
/* set mask for 16 queue limit of RSS */
f = &adapter->ring_feature[RING_F_RSS];
rss_i = f->limit;
f->indices = rss_i;
f->mask = NGBE_RSS_8Q_MASK;
adapter->num_rx_queues = rss_i;
#ifdef HAVE_TX_MQ
adapter->num_tx_queues = rss_i;
#endif
#ifdef HAVE_XDP_SUPPORT
if (adapter->xdp_prog) {
adapter->num_xdp_queues = min_t(int, ngbe_xdp_queues(adapter), rss_i);
}
#endif
return true;
}
/*
* ngbe_set_num_queues: Allocate queues for device, feature dependent
* @adapter: board private structure to initialize
*
* This is the top level queue allocation routine. The order here is very
* important, starting with the "most" number of features turned on at once,
* and ending with the smallest set of features. This way large combinations
* can be allocated if they're turned on, and smaller combinations are the
* fallthrough conditions.
*
**/
static void ngbe_set_num_queues(struct ngbe_adapter *adapter)
{
/* Start with base case */
adapter->num_rx_queues = 1;
adapter->num_tx_queues = 1;
adapter->num_xdp_queues = 0;
adapter->queues_per_pool = 1;
if (ngbe_set_vmdq_queues(adapter))
return;
ngbe_set_rss_queues(adapter);
}
/**
* ngbe_acquire_msix_vectors - acquire MSI-X vectors
* @adapter: board private structure
*
* Attempts to acquire a suitable range of MSI-X vector interrupts. Will
* return a negative error code if unable to acquire MSI-X vectors for any
* reason.
*/
static int ngbe_acquire_msix_vectors(struct ngbe_adapter *adapter)
{
struct ngbe_hw *hw = &adapter->hw;
int i, vectors, vector_threshold;
if (!(adapter->flags & NGBE_FLAG_MSIX_CAPABLE))
return -EOPNOTSUPP;
/* We start by asking for one vector per queue pair */
vectors = max(adapter->num_rx_queues, adapter->num_tx_queues);
vectors = max(vectors, adapter->num_xdp_queues);
/* It is easy to be greedy for MSI-X vectors. However, it really
* doesn't do much good if we have a lot more vectors than CPUs. We'll
* be somewhat conservative and only ask for (roughly) the same number
* of vectors as there are CPUs.
*/
vectors = min_t(int, vectors, num_online_cpus());
/* Some vectors are necessary for non-queue interrupts */
vectors += NON_Q_VECTORS;
/* Hardware can only support a maximum of hw.mac->max_msix_vectors.
* With features such as RSS and VMDq, we can easily surpass the
* number of Rx and Tx descriptor queues supported by our device.
* Thus, we cap the maximum in the rare cases where the CPU count also
* exceeds our vector limit
*/
vectors = min_t(int, vectors, hw->mac.max_msix_vectors);
/* We want a minimum of two MSI-X vectors for (1) a TxQ[0] + RxQ[0]
* handler, and (2) an Other (Link Status Change, etc.) handler.
*/
vector_threshold = MIN_MSIX_COUNT;
/* we need to alloc (7vfs+1pf+1misc) or (8vfs+1misc) msix entries */
if (adapter->flags2 & NGBE_FLAG2_SRIOV_MISC_IRQ_REMAP) {
vectors += adapter->ring_feature[RING_F_VMDQ].offset;
}
adapter->msix_entries = kcalloc(vectors,
sizeof(struct msix_entry),
GFP_KERNEL);
if (!adapter->msix_entries)
return -ENOMEM;
for (i = 0; i < vectors; i++)
adapter->msix_entries[i].entry = i;
vectors = pci_enable_msix_range(adapter->pdev, adapter->msix_entries,
vector_threshold, vectors);
if (vectors < 0) {
/* A negative count of allocated vectors indicates an error in
* acquiring within the specified range of MSI-X vectors */
e_dev_warn("Failed to allocate MSI-X interrupts. Err: %d\n",
vectors);
adapter->flags &= ~NGBE_FLAG_MSIX_ENABLED;
kfree(adapter->msix_entries);
adapter->msix_entries = NULL;
return vectors;
}
if (adapter->flags2 & NGBE_FLAG2_SRIOV_MISC_IRQ_REMAP) {
if (vectors < 9) {
adapter->flags2 &= ~NGBE_FLAG2_SRIOV_MISC_IRQ_REMAP;
e_dev_warn("Remain available irqs < 9. Disable MISC IRQ REMAP.\n");
}
else
vectors -= adapter->ring_feature[RING_F_VMDQ].offset;
}
/* we successfully allocated some number of vectors within our
* requested range.
*/
adapter->flags |= NGBE_FLAG_MSIX_ENABLED;
/* Adjust for only the vectors we'll use, which is minimum
* of max_q_vectors, or the number of vectors we were allocated.
*/
vectors -= NON_Q_VECTORS;
adapter->num_q_vectors = min_t(int, vectors, adapter->max_q_vectors);
return 0;
}
static void ngbe_add_ring(struct ngbe_ring *ring,
struct ngbe_ring_container *head)
{
ring->next = head->ring;
head->ring = ring;
head->count++;
}
/**
* ngbe_alloc_q_vector - Allocate memory for a single interrupt vector
* @adapter: board private structure to initialize
* @v_count: q_vectors allocated on adapter, used for ring interleaving
* @v_idx: index of vector in adapter struct
* @txr_count: total number of Tx rings to allocate
* @txr_idx: index of first Tx ring to allocate
* @rxr_count: total number of Rx rings to allocate
* @rxr_idx: index of first Rx ring to allocate
*
* We allocate one q_vector. If allocation fails we return -ENOMEM.
**/
static int ngbe_alloc_q_vector(struct ngbe_adapter *adapter,
unsigned int v_count, unsigned int v_idx,
unsigned int txr_count, unsigned int txr_idx,
unsigned int xdp_count, unsigned int xdp_idx,
unsigned int rxr_count, unsigned int rxr_idx)
{
struct ngbe_q_vector *q_vector;
struct ngbe_ring *ring;
int node = -1;
#ifdef HAVE_IRQ_AFFINITY_HINT
int cpu = -1;
u8 tcs = netdev_get_num_tc(adapter->netdev);
#endif
int ring_count, size;
/* note this will allocate space for the ring structure as well! */
ring_count = txr_count + rxr_count + xdp_count;
size = sizeof(struct ngbe_q_vector) +
(sizeof(struct ngbe_ring) * ring_count);
#ifdef HAVE_IRQ_AFFINITY_HINT
/* customize cpu for Flow Director mapping */
if ((tcs <= 1) && !(adapter->flags & NGBE_FLAG_VMDQ_ENABLED)) {
u16 rss_i = adapter->ring_feature[RING_F_RSS].indices;
if (rss_i > 1 && adapter->atr_sample_rate) {
if (cpu_online(v_idx)) {
cpu = v_idx;
node = cpu_to_node(cpu);
}
}
}
#endif
/* allocate q_vector and rings */
q_vector = kzalloc_node(size, GFP_KERNEL, node);
if (!q_vector)
q_vector = kzalloc(size, GFP_KERNEL);
if (!q_vector)
return -ENOMEM;
/* setup affinity mask and node */
#ifdef HAVE_IRQ_AFFINITY_HINT
if (cpu != -1)
cpumask_set_cpu(cpu, &q_vector->affinity_mask);
#endif
q_vector->numa_node = node;
/* initialize CPU for DCA */
q_vector->cpu = -1;
#ifndef NGBE_NO_LRO
/* initialize LRO */
__skb_queue_head_init(&q_vector->lrolist.active);
#endif
/* initialize NAPI */
#ifdef HAVE_NOT_NAPI_WEIGHT
netif_napi_add(adapter->netdev, &q_vector->napi,
ngbe_poll);
#else
netif_napi_add(adapter->netdev, &q_vector->napi,
ngbe_poll, 64);
#endif
#ifndef HAVE_NETIF_NAPI_ADD_CALLS_NAPI_HASH_ADD
#ifdef HAVE_NDO_BUSY_POLL
napi_hash_add(&q_vector->napi);
#endif
#endif /*HAVE_NETIF_NAPI_ADD_CALLS_NAPI_HASH_ADD*/
#ifdef HAVE_NDO_BUSY_POLL
/* initialize busy poll */
atomic_set(&q_vector->state, NGBE_QV_STATE_DISABLE);
#endif
/* tie q_vector and adapter together */
adapter->q_vector[v_idx] = q_vector;
q_vector->adapter = adapter;
q_vector->v_idx = v_idx;
/* initialize work limits */
q_vector->tx.work_limit = adapter->tx_work_limit;
q_vector->rx.work_limit = adapter->rx_work_limit;
/* initialize pointer to rings */
ring = q_vector->ring;
/* intialize ITR */
if (txr_count && !rxr_count) {
/* tx only vector */
if (adapter->tx_itr_setting == 1)
q_vector->itr = NGBE_7K_ITR;
else
q_vector->itr = adapter->tx_itr_setting;
} else {
/* rx or rx/tx vector */
if (adapter->rx_itr_setting == 1)
q_vector->itr = NGBE_7K_ITR;
else
q_vector->itr = adapter->rx_itr_setting;
}
while (txr_count) {
/* assign generic ring traits */
ring->dev = pci_dev_to_dev(adapter->pdev);
ring->netdev = adapter->netdev;
/* configure backlink on ring */
ring->q_vector = q_vector;
/* update q_vector Tx values */
ngbe_add_ring(ring, &q_vector->tx);
/* apply Tx specific ring traits */
ring->count = adapter->tx_ring_count;
if (adapter->num_vmdqs > 1)
ring->queue_index =
txr_idx % adapter->queues_per_pool;
else
ring->queue_index = txr_idx;
clear_ring_xdp(ring);
/* assign ring to adapter */
adapter->tx_ring[txr_idx] = ring;
/* update count and index */
txr_count--;
txr_idx += v_count;
/* push pointer to next ring */
ring++;
}
while (xdp_count) {
/* assign generic ring traits */
ring->dev = pci_dev_to_dev(adapter->pdev);
ring->netdev = adapter->netdev;
/* configure backlink on ring */
ring->q_vector = q_vector;
/* update q_vector Tx values */
ngbe_add_ring(ring, &q_vector->tx);
/* apply Tx specific ring traits */
ring->count = adapter->tx_ring_count;
ring->queue_index = xdp_idx;
set_ring_xdp(ring);
/* assign ring to adapter */
adapter->xdp_ring[xdp_idx] = ring;
/* update count and index */
xdp_count--;
xdp_idx += v_count;
/* push pointer to next ring */
ring++;
}
while (rxr_count) {
/* assign generic ring traits */
ring->dev = pci_dev_to_dev(adapter->pdev);
ring->netdev = adapter->netdev;
/* configure backlink on ring */
ring->q_vector = q_vector;
/* update q_vector Rx values */
ngbe_add_ring(ring, &q_vector->rx);
/* apply Rx specific ring traits */
ring->count = adapter->rx_ring_count;
if (adapter->num_vmdqs > 1)
ring->queue_index =
rxr_idx % adapter->queues_per_pool;
else
ring->queue_index = rxr_idx;
/* assign ring to adapter */
adapter->rx_ring[rxr_idx] = ring;
/* update count and index */
rxr_count--;
rxr_idx += v_count;
/* push pointer to next ring */
ring++;
}
return 0;
}
/**
* ngbe_free_q_vector - Free memory allocated for specific interrupt vector
* @adapter: board private structure to initialize
* @v_idx: Index of vector to be freed
*
* This function frees the memory allocated to the q_vector. In addition if
* NAPI is enabled it will delete any references to the NAPI struct prior
* to freeing the q_vector.
**/
static void ngbe_free_q_vector(struct ngbe_adapter *adapter, int v_idx)
{
struct ngbe_q_vector *q_vector = adapter->q_vector[v_idx];
struct ngbe_ring *ring;
ngbe_for_each_ring(ring, q_vector->tx) {
if (ring_is_xdp(ring))
adapter->xdp_ring[ring->queue_index] = NULL;
else
adapter->tx_ring[ring->queue_index] = NULL;
}
#ifdef HAVE_XDP_SUPPORT
if (static_key_enabled((struct static_key *)&ngbe_xdp_locking_key))
static_branch_dec(&ngbe_xdp_locking_key);
#endif
ngbe_for_each_ring(ring, q_vector->rx)
adapter->rx_ring[ring->queue_index] = NULL;
adapter->q_vector[v_idx] = NULL;
#ifdef HAVE_NDO_BUSY_POLL
napi_hash_del(&q_vector->napi);
#endif
netif_napi_del(&q_vector->napi);
#ifndef NGBE_NO_LRO
__skb_queue_purge(&q_vector->lrolist.active);
#endif
kfree_rcu(q_vector, rcu);
}
/**
* ngbe_alloc_q_vectors - Allocate memory for interrupt vectors
* @adapter: board private structure to initialize
*
* We allocate one q_vector per queue interrupt. If allocation fails we
* return -ENOMEM.
**/
static int ngbe_alloc_q_vectors(struct ngbe_adapter *adapter)
{
unsigned int q_vectors = adapter->num_q_vectors;
unsigned int rxr_remaining = adapter->num_rx_queues;
unsigned int txr_remaining = adapter->num_tx_queues;
unsigned int xdp_remaining = adapter->num_xdp_queues;
unsigned int rxr_idx = 0, txr_idx = 0, xdp_idx = 0, v_idx = 0;
int err;
if (q_vectors >= (rxr_remaining + txr_remaining + xdp_remaining)) {
for (; rxr_remaining; v_idx++) {
err = ngbe_alloc_q_vector(adapter, q_vectors, v_idx,
0, 0, 0, 0, 1, rxr_idx);
if (err)
goto err_out;
/* update counts and index */
rxr_remaining--;
rxr_idx++;
}
}
for (; v_idx < q_vectors; v_idx++) {
int rqpv = DIV_ROUND_UP(rxr_remaining, q_vectors - v_idx);
int tqpv = DIV_ROUND_UP(txr_remaining, q_vectors - v_idx);
int xqpv = DIV_ROUND_UP(xdp_remaining, q_vectors - v_idx);
err = ngbe_alloc_q_vector(adapter, q_vectors, v_idx,
tqpv, txr_idx,
xqpv, xdp_idx,
rqpv, rxr_idx);
if (err)
goto err_out;
/* update counts and index */
rxr_remaining -= rqpv;
txr_remaining -= tqpv;
xdp_remaining -= xqpv;
rxr_idx++;
txr_idx++;
xdp_idx++;
}
return 0;
err_out:
adapter->num_tx_queues = 0;
adapter->num_rx_queues = 0;
adapter->num_xdp_queues = 0;
adapter->num_q_vectors = 0;
while (v_idx--)
ngbe_free_q_vector(adapter, v_idx);
return -ENOMEM;
}
/**
* ngbe_free_q_vectors - Free memory allocated for interrupt vectors
* @adapter: board private structure to initialize
*
* This function frees the memory allocated to the q_vectors. In addition if
* NAPI is enabled it will delete any references to the NAPI struct prior
* to freeing the q_vector.
**/
static void ngbe_free_q_vectors(struct ngbe_adapter *adapter)
{
int v_idx = adapter->num_q_vectors;
adapter->num_tx_queues = 0;
adapter->num_rx_queues = 0;
adapter->num_xdp_queues = 0;
adapter->num_q_vectors = 0;
while (v_idx--)
ngbe_free_q_vector(adapter, v_idx);
}
void ngbe_reset_interrupt_capability(struct ngbe_adapter *adapter)
{
if (adapter->flags & NGBE_FLAG_MSIX_ENABLED) {
adapter->flags &= ~NGBE_FLAG_MSIX_ENABLED;
pci_disable_msix(adapter->pdev);
kfree(adapter->msix_entries);
adapter->msix_entries = NULL;
} else if (adapter->flags & NGBE_FLAG_MSI_ENABLED) {
adapter->flags &= ~NGBE_FLAG_MSI_ENABLED;
pci_disable_msi(adapter->pdev);
}
}
/**
* ngbe_set_interrupt_capability - set MSI-X or MSI if supported
* @adapter: board private structure to initialize
*
* Attempt to configure the interrupts using the best available
* capabilities of the hardware and the kernel.
**/
void ngbe_set_interrupt_capability(struct ngbe_adapter *adapter)
{
int err;
/* We will try to get MSI-X interrupts first */
if (!ngbe_acquire_msix_vectors(adapter))
return;
/* At this point, we do not have MSI-X capabilities. We need to
* reconfigure or disable various features which require MSI-X
* capability.
*/
/* Disable VMDq support */
e_dev_warn("Disabling VMQd support\n");
adapter->flags &= ~NGBE_FLAG_VMDQ_ENABLED;
#ifdef CONFIG_PCI_IOV
/* Disable SR-IOV support */
e_dev_warn("Disabling SR-IOV support\n");
ngbe_disable_sriov(adapter);
if (adapter->flags2 & NGBE_FLAG2_SRIOV_MISC_IRQ_REMAP)
adapter->flags2 &= ~NGBE_FLAG2_SRIOV_MISC_IRQ_REMAP;
#endif /* CONFIG_PCI_IOV */
/* Disable RSS */
e_dev_warn("Disabling RSS support\n");
adapter->ring_feature[RING_F_RSS].limit = 1;
/* recalculate number of queues now that many features have been
* changed or disabled.
*/
ngbe_set_num_queues(adapter);
adapter->num_q_vectors = 1;
if (!(adapter->flags & NGBE_FLAG_MSI_CAPABLE))
return;
err = pci_enable_msi(adapter->pdev);
if (err)
e_dev_warn("Failed to allocate MSI interrupt, falling back to "
"legacy. Error: %d\n",
err);
else
adapter->flags |= NGBE_FLAG_MSI_ENABLED;
}
/**
* ngbe_init_interrupt_scheme - Determine proper interrupt scheme
* @adapter: board private structure to initialize
*
* We determine which interrupt scheme to use based on...
* - Kernel support (MSI, MSI-X)
* - which can be user-defined (via MODULE_PARAM)
* - Hardware queue count (num_*_queues)
* - defined by miscellaneous hardware support/features (RSS, etc.)
**/
int ngbe_init_interrupt_scheme(struct ngbe_adapter *adapter)
{
int err;
/* if assigned vfs >= 7, the PF queue irq remain seq 0 and misc irq move from
* seq 1 to seq 8. it needs extra processions.
*/
if (adapter->num_vfs >= NGBE_MAX_VF_FUNCTIONS - 1) {
adapter->flags2 |= NGBE_FLAG2_SRIOV_MISC_IRQ_REMAP;
adapter->irq_remap_offset = adapter->num_vfs;
}
/* Number of supported queues */
ngbe_set_num_queues(adapter);
/* Set interrupt mode */
ngbe_set_interrupt_capability(adapter);
/* Allocate memory for queues */
err = ngbe_alloc_q_vectors(adapter);
if (err) {
e_err(probe, "Unable to allocate memory for queue vectors\n");
ngbe_reset_interrupt_capability(adapter);
return err;
}
ngbe_cache_ring_register(adapter);
set_bit(__NGBE_DOWN, &adapter->state);
return 0;
}
/**
* ngbe_clear_interrupt_scheme - Clear the current interrupt scheme settings
* @adapter: board private structure to clear interrupt scheme on
*
* We go through and clear interrupt specific resources and reset the structure
* to pre-load conditions
**/
void ngbe_clear_interrupt_scheme(struct ngbe_adapter *adapter)
{
ngbe_free_q_vectors(adapter);
ngbe_reset_interrupt_capability(adapter);
/* remove this flags */
if (adapter->flags2 & NGBE_FLAG2_SRIOV_MISC_IRQ_REMAP) {
adapter->flags2 &= ~NGBE_FLAG2_SRIOV_MISC_IRQ_REMAP;
}
}
void ngbe_tx_ctxtdesc(struct ngbe_ring *tx_ring, u32 vlan_macip_lens,
u32 fcoe_sof_eof, u32 type_tucmd, u32 mss_l4len_idx)
{
struct ngbe_tx_context_desc *context_desc;
u16 i = tx_ring->next_to_use;
context_desc = NGBE_TX_CTXTDESC(tx_ring, i);
i++;
tx_ring->next_to_use = (i < tx_ring->count) ? i : 0;
/* set bits to identify this as an advanced context descriptor */
type_tucmd |= NGBE_TXD_DTYP_CTXT;
context_desc->vlan_macip_lens = cpu_to_le32(vlan_macip_lens);
context_desc->seqnum_seed = cpu_to_le32(fcoe_sof_eof);
context_desc->type_tucmd_mlhl = cpu_to_le32(type_tucmd);
context_desc->mss_l4len_idx = cpu_to_le32(mss_l4len_idx);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,692 @@
/*
* WangXun Gigabit PCI Express Linux driver
* Copyright (c) 2015 - 2017 Beijing WangXun Technology Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* The full GNU General Public License is included in this distribution in
* the file called "COPYING".
*
*/
#include "ngbe_type.h"
#include "ngbe.h"
#include "ngbe_mbx.h"
/**
* ngbe_read_mbx - Reads a message from the mailbox
* @hw: pointer to the HW structure
* @msg: The message buffer
* @size: Length of buffer
* @mbx_id: id of mailbox to read
*
* returns SUCCESS if it successfuly read message from buffer
**/
int ngbe_read_mbx(struct ngbe_hw *hw, u32 *msg, u16 size, u16 mbx_id)
{
struct ngbe_mbx_info *mbx = &hw->mbx;
int err = NGBE_ERR_MBX;
/* limit read to size of mailbox */
if (size > mbx->size)
size = mbx->size;
err = hw->mbx.ops.read(hw, msg, size, mbx_id);
return err;
}
/**
* ngbe_write_mbx - Write a message to the mailbox
* @hw: pointer to the HW structure
* @msg: The message buffer
* @size: Length of buffer
* @mbx_id: id of mailbox to write
*
* returns SUCCESS if it successfully copied message into the buffer
**/
int ngbe_write_mbx(struct ngbe_hw *hw, u32 *msg, u16 size, u16 mbx_id)
{
struct ngbe_mbx_info *mbx = &hw->mbx;
int err = 0;
if (size > mbx->size) {
err = NGBE_ERR_MBX;
ERROR_REPORT2(NGBE_ERROR_ARGUMENT,
"Invalid mailbox message size %d", size);
} else
err = hw->mbx.ops.write(hw, msg, size, mbx_id);
return err;
}
/**
* ngbe_check_for_msg - checks to see if someone sent us mail
* @hw: pointer to the HW structure
* @mbx_id: id of mailbox to check
*
* returns SUCCESS if the Status bit was found or else ERR_MBX
**/
int ngbe_check_for_msg(struct ngbe_hw *hw, u16 mbx_id)
{
int err = NGBE_ERR_MBX;
err = hw->mbx.ops.check_for_msg(hw, mbx_id);
return err;
}
/**
* ngbe_check_for_ack - checks to see if someone sent us ACK
* @hw: pointer to the HW structure
* @mbx_id: id of mailbox to check
*
* returns SUCCESS if the Status bit was found or else ERR_MBX
**/
int ngbe_check_for_ack(struct ngbe_hw *hw, u16 mbx_id)
{
int err = NGBE_ERR_MBX;
err = hw->mbx.ops.check_for_ack(hw, mbx_id);
return err;
}
/**
* ngbe_check_for_rst - checks to see if other side has reset
* @hw: pointer to the HW structure
* @mbx_id: id of mailbox to check
*
* returns SUCCESS if the Status bit was found or else ERR_MBX
**/
int ngbe_check_for_rst(struct ngbe_hw *hw, u16 mbx_id)
{
struct ngbe_mbx_info *mbx = &hw->mbx;
int err = NGBE_ERR_MBX;
if (mbx->ops.check_for_rst)
err = mbx->ops.check_for_rst(hw, mbx_id);
return err;
}
/**
* ngbe_poll_for_msg - Wait for message notification
* @hw: pointer to the HW structure
* @mbx_id: id of mailbox to write
*
* returns SUCCESS if it successfully received a message notification
**/
static int ngbe_poll_for_msg(struct ngbe_hw *hw, u16 mbx_id)
{
struct ngbe_mbx_info *mbx = &hw->mbx;
int countdown = mbx->timeout;
if (!countdown || !mbx->ops.check_for_msg)
goto out;
while (countdown && hw->mbx.ops.check_for_msg(hw, mbx_id)) {
countdown--;
if (!countdown)
break;
udelay(mbx->udelay);
}
if (countdown == 0)
ERROR_REPORT2(NGBE_ERROR_POLLING,
"Polling for VF%d mailbox message timedout", mbx_id);
out:
return countdown ? 0 : NGBE_ERR_MBX;
}
/**
* ngbe_poll_for_ack - Wait for message acknowledngbeent
* @hw: pointer to the HW structure
* @mbx_id: id of mailbox to write
*
* returns SUCCESS if it successfully received a message acknowledngbeent
**/
static int ngbe_poll_for_ack(struct ngbe_hw *hw, u16 mbx_id)
{
struct ngbe_mbx_info *mbx = &hw->mbx;
int countdown = mbx->timeout;
if (!countdown || !mbx->ops.check_for_ack)
goto out;
while (countdown && hw->mbx.ops.check_for_ack(hw, mbx_id)) {
countdown--;
if (!countdown)
break;
udelay(mbx->udelay);
}
if (countdown == 0)
ERROR_REPORT2(NGBE_ERROR_POLLING,
"Polling for VF%d mailbox ack timedout", mbx_id);
out:
return countdown ? 0 : NGBE_ERR_MBX;
}
/**
* ngbe_read_posted_mbx - Wait for message notification and receive message
* @hw: pointer to the HW structure
* @msg: The message buffer
* @size: Length of buffer
* @mbx_id: id of mailbox to write
*
* returns SUCCESS if it successfully received a message notification and
* copied it into the receive buffer.
**/
int ngbe_read_posted_mbx(struct ngbe_hw *hw, u32 *msg, u16 size, u16 mbx_id)
{
struct ngbe_mbx_info *mbx = &hw->mbx;
int err = NGBE_ERR_MBX;
if (!mbx->ops.read)
goto out;
err = ngbe_poll_for_msg(hw, mbx_id);
/* if ack received read message, otherwise we timed out */
if (!err)
err = hw->mbx.ops.read(hw, msg, size, mbx_id);
out:
return err;
}
/**
* ngbe_write_posted_mbx - Write a message to the mailbox, wait for ack
* @hw: pointer to the HW structure
* @msg: The message buffer
* @size: Length of buffer
* @mbx_id: id of mailbox to write
*
* returns SUCCESS if it successfully copied message into the buffer and
* received an ack to that message within delay * timeout period
**/
int ngbe_write_posted_mbx(struct ngbe_hw *hw, u32 *msg, u16 size,
u16 mbx_id)
{
struct ngbe_mbx_info *mbx = &hw->mbx;
int err;
/* exit if either we can't write or there isn't a defined timeout */
if (!mbx->timeout)
return NGBE_ERR_MBX;
/* send msg */
err = hw->mbx.ops.write(hw, msg, size, mbx_id);
/* if msg sent wait until we receive an ack */
if (!err)
err = ngbe_poll_for_ack(hw, mbx_id);
return err;
}
/**
* ngbe_init_mbx_ops - Initialize MB function pointers
* @hw: pointer to the HW structure
*
* Setups up the mailbox read and write message function pointers
**/
void ngbe_init_mbx_ops(struct ngbe_hw *hw)
{
struct ngbe_mbx_info *mbx = &hw->mbx;
mbx->ops.read_posted = ngbe_read_posted_mbx;
mbx->ops.write_posted = ngbe_write_posted_mbx;
}
/**
* ngbe_read_v2p_mailbox - read v2p mailbox
* @hw: pointer to the HW structure
*
* This function is used to read the v2p mailbox without losing the read to
* clear status bits.
**/
static u32 ngbe_read_v2p_mailbox(struct ngbe_hw *hw)
{
u32 v2p_mailbox = rd32(hw, NGBE_VXMAILBOX);
v2p_mailbox |= hw->mbx.v2p_mailbox;
hw->mbx.v2p_mailbox |= v2p_mailbox & NGBE_VXMAILBOX_R2C_BITS;
return v2p_mailbox;
}
/**
* ngbe_check_for_bit_vf - Determine if a status bit was set
* @hw: pointer to the HW structure
* @mask: bitmask for bits to be tested and cleared
*
* This function is used to check for the read to clear bits within
* the V2P mailbox.
**/
static int ngbe_check_for_bit_vf(struct ngbe_hw *hw, u32 mask)
{
u32 mailbox = ngbe_read_v2p_mailbox(hw);
hw->mbx.v2p_mailbox &= ~mask;
return (mailbox & mask ? 0 : NGBE_ERR_MBX);
}
/**
* ngbe_check_for_msg_vf - checks to see if the PF has sent mail
* @hw: pointer to the HW structure
* @mbx_id: id of mailbox to check
*
* returns SUCCESS if the PF has set the Status bit or else ERR_MBX
**/
static int ngbe_check_for_msg_vf(struct ngbe_hw *hw, u16 mbx_id)
{
int err = NGBE_ERR_MBX;
UNREFERENCED_PARAMETER(mbx_id);
/* read clear the pf sts bit */
if (!ngbe_check_for_bit_vf(hw, NGBE_VXMAILBOX_PFSTS)) {
err = 0;
hw->mbx.stats.reqs++;
}
return err;
}
/**
* ngbe_check_for_ack_vf - checks to see if the PF has ACK'd
* @hw: pointer to the HW structure
* @mbx_id: id of mailbox to check
*
* returns SUCCESS if the PF has set the ACK bit or else ERR_MBX
**/
static int ngbe_check_for_ack_vf(struct ngbe_hw *hw, u16 mbx_id)
{
int err = NGBE_ERR_MBX;
UNREFERENCED_PARAMETER(mbx_id);
/* read clear the pf ack bit */
if (!ngbe_check_for_bit_vf(hw, NGBE_VXMAILBOX_PFACK)) {
err = 0;
hw->mbx.stats.acks++;
}
return err;
}
/**
* ngbe_check_for_rst_vf - checks to see if the PF has reset
* @hw: pointer to the HW structure
* @mbx_id: id of mailbox to check
*
* returns true if the PF has set the reset done bit or else false
**/
static int ngbe_check_for_rst_vf(struct ngbe_hw *hw, u16 mbx_id)
{
int err = NGBE_ERR_MBX;
UNREFERENCED_PARAMETER(mbx_id);
if (!ngbe_check_for_bit_vf(hw, (NGBE_VXMAILBOX_RSTD |
NGBE_VXMAILBOX_RSTI))) {
err = 0;
hw->mbx.stats.rsts++;
}
return err;
}
/**
* ngbe_obtain_mbx_lock_vf - obtain mailbox lock
* @hw: pointer to the HW structure
*
* return SUCCESS if we obtained the mailbox lock
**/
static int ngbe_obtain_mbx_lock_vf(struct ngbe_hw *hw)
{
int err = NGBE_ERR_MBX;
u32 mailbox;
/* Take ownership of the buffer */
wr32(hw, NGBE_VXMAILBOX, NGBE_VXMAILBOX_VFU);
/* reserve mailbox for vf use */
mailbox = ngbe_read_v2p_mailbox(hw);
if (mailbox & NGBE_VXMAILBOX_VFU)
err = 0;
else
ERROR_REPORT2(NGBE_ERROR_POLLING,
"Failed to obtain mailbox lock for VF");
return err;
}
/**
* ngbe_write_mbx_vf - Write a message to the mailbox
* @hw: pointer to the HW structure
* @msg: The message buffer
* @size: Length of buffer
* @mbx_id: id of mailbox to write
*
* returns SUCCESS if it successfully copied message into the buffer
**/
static int ngbe_write_mbx_vf(struct ngbe_hw *hw, u32 *msg, u16 size,
u16 mbx_id)
{
int err;
u16 i;
UNREFERENCED_PARAMETER(mbx_id);
/* lock the mailbox to prevent pf/vf race condition */
err = ngbe_obtain_mbx_lock_vf(hw);
if (err)
goto out_no_write;
/* flush msg and acks as we are overwriting the message buffer */
ngbe_check_for_msg_vf(hw, 0);
ngbe_check_for_ack_vf(hw, 0);
/* copy the caller specified message to the mailbox memory buffer */
for (i = 0; i < size; i++)
wr32a(hw, NGBE_VXMBMEM, i, msg[i]);
/* update stats */
hw->mbx.stats.msgs_tx++;
/* Drop VFU and interrupt the PF to tell it a message has been sent */
wr32(hw, NGBE_VXMAILBOX, NGBE_VXMAILBOX_REQ);
out_no_write:
return err;
}
/**
* ngbe_read_mbx_vf - Reads a message from the inbox intended for vf
* @hw: pointer to the HW structure
* @msg: The message buffer
* @size: Length of buffer
* @mbx_id: id of mailbox to read
*
* returns SUCCESS if it successfuly read message from buffer
**/
static int ngbe_read_mbx_vf(struct ngbe_hw *hw, u32 *msg, u16 size,
u16 mbx_id)
{
int err = 0;
u16 i;
UNREFERENCED_PARAMETER(mbx_id);
/* lock the mailbox to prevent pf/vf race condition */
err = ngbe_obtain_mbx_lock_vf(hw);
if (err)
goto out_no_read;
/* copy the message from the mailbox memory buffer */
for (i = 0; i < size; i++)
msg[i] = rd32a(hw, NGBE_VXMBMEM, i);
/* Acknowledge receipt and release mailbox, then we're done */
wr32(hw, NGBE_VXMAILBOX, NGBE_VXMAILBOX_ACK);
/* update stats */
hw->mbx.stats.msgs_rx++;
out_no_read:
return err;
}
/**
* ngbe_init_mbx_params_vf - set initial values for vf mailbox
* @hw: pointer to the HW structure
*
* Initializes the hw->mbx struct to correct values for vf mailbox
*/
void ngbe_init_mbx_params_vf(struct ngbe_hw *hw)
{
struct ngbe_mbx_info *mbx = &hw->mbx;
/* start mailbox as timed out and let the reset_hw call set the timeout
* value to begin communications */
mbx->timeout = 0;
mbx->udelay = NGBE_VF_MBX_INIT_DELAY;
mbx->size = NGBE_VXMAILBOX_SIZE;
mbx->ops.read = ngbe_read_mbx_vf;
mbx->ops.write = ngbe_write_mbx_vf;
mbx->ops.read_posted = ngbe_read_posted_mbx;
mbx->ops.write_posted = ngbe_write_posted_mbx;
mbx->ops.check_for_msg = ngbe_check_for_msg_vf;
mbx->ops.check_for_ack = ngbe_check_for_ack_vf;
mbx->ops.check_for_rst = ngbe_check_for_rst_vf;
mbx->stats.msgs_tx = 0;
mbx->stats.msgs_rx = 0;
mbx->stats.reqs = 0;
mbx->stats.acks = 0;
mbx->stats.rsts = 0;
}
static int ngbe_check_for_bit_pf(struct ngbe_hw *hw, u32 mask)
{
u32 mbvficr = rd32(hw, NGBE_MBVFICR);
int err = NGBE_ERR_MBX;
if (mbvficr & mask) {
err = 0;
wr32(hw, NGBE_MBVFICR, mask);
}
return err;
}
/**
* ngbe_check_for_msg_pf - checks to see if the VF has sent mail
* @hw: pointer to the HW structure
* @vf: the VF index
*
* returns SUCCESS if the VF has set the Status bit or else ERR_MBX
**/
static int ngbe_check_for_msg_pf(struct ngbe_hw *hw, u16 vf)
{
int err = NGBE_ERR_MBX;
u32 vf_bit = vf;
if (!ngbe_check_for_bit_pf(hw, NGBE_MBVFICR_VFREQ_VF1 << vf_bit)) {
err = 0;
hw->mbx.stats.reqs++;
}
return err;
}
/**
* ngbe_check_for_ack_pf - checks to see if the VF has ACKed
* @hw: pointer to the HW structure
* @vf: the VF index
*
* returns SUCCESS if the VF has set the Status bit or else ERR_MBX
**/
static int ngbe_check_for_ack_pf(struct ngbe_hw *hw, u16 vf)
{
int err = NGBE_ERR_MBX;
u32 vf_bit = vf;
if (!ngbe_check_for_bit_pf(hw, NGBE_MBVFICR_VFACK_VF1 << vf_bit)) {
err = 0;
hw->mbx.stats.acks++;
}
return err;
}
/**
* ngbe_check_for_rst_pf - checks to see if the VF has reset
* @hw: pointer to the HW structure
* @vf: the VF index
*
* returns SUCCESS if the VF has set the Status bit or else ERR_MBX
**/
static int ngbe_check_for_rst_pf(struct ngbe_hw *hw, u16 vf)
{
u32 vflre = 0;
int err = NGBE_ERR_MBX;
vflre = rd32(hw, NGBE_VFLRE);
if (vflre & (1 << vf)) {
err = 0;
wr32(hw, NGBE_VFLREC, (1 << vf));
hw->mbx.stats.rsts++;
}
return err;
}
/**
* ngbe_obtain_mbx_lock_pf - obtain mailbox lock
* @hw: pointer to the HW structure
* @vf: the VF index
*
* return SUCCESS if we obtained the mailbox lock
**/
static int ngbe_obtain_mbx_lock_pf(struct ngbe_hw *hw, u16 vf)
{
int err = NGBE_ERR_MBX;
u32 mailbox;
/* Take ownership of the buffer */
wr32(hw, NGBE_PXMAILBOX(vf), NGBE_PXMAILBOX_PFU);
/* reserve mailbox for vf use */
mailbox = rd32(hw, NGBE_PXMAILBOX(vf));
if (mailbox & NGBE_PXMAILBOX_PFU)
err = 0;
else
ERROR_REPORT2(NGBE_ERROR_POLLING,
"Failed to obtain mailbox lock for PF%d", vf);
return err;
}
/**
* ngbe_write_mbx_pf - Places a message in the mailbox
* @hw: pointer to the HW structure
* @msg: The message buffer
* @size: Length of buffer
* @vf: the VF index
*
* returns SUCCESS if it successfully copied message into the buffer
**/
static int ngbe_write_mbx_pf(struct ngbe_hw *hw, u32 *msg, u16 size,
u16 vf)
{
int err;
u16 i;
/* lock the mailbox to prevent pf/vf race condition */
err = ngbe_obtain_mbx_lock_pf(hw, vf);
if (err)
goto out_no_write;
/* flush msg and acks as we are overwriting the message buffer */
ngbe_check_for_msg_pf(hw, vf);
ngbe_check_for_ack_pf(hw, vf);
/* copy the caller specified message to the mailbox memory buffer */
for (i = 0; i < size; i++)
wr32a(hw, NGBE_PXMBMEM(vf), i, msg[i]);
/* Interrupt VF to tell it a message has been sent and release buffer*/
wr32(hw, NGBE_PXMAILBOX(vf), NGBE_PXMAILBOX_STS);
/* update stats */
hw->mbx.stats.msgs_tx++;
out_no_write:
return err;
}
/**
* ngbe_read_mbx_pf - Read a message from the mailbox
* @hw: pointer to the HW structure
* @msg: The message buffer
* @size: Length of buffer
* @vf: the VF index
*
* This function copies a message from the mailbox buffer to the caller's
* memory buffer. The presumption is that the caller knows that there was
* a message due to a VF request so no polling for message is needed.
**/
static int ngbe_read_mbx_pf(struct ngbe_hw *hw, u32 *msg, u16 size,
u16 vf)
{
int err;
u16 i;
/* lock the mailbox to prevent pf/vf race condition */
err = ngbe_obtain_mbx_lock_pf(hw, vf);
if (err)
goto out_no_read;
/* copy the message to the mailbox memory buffer */
for (i = 0; i < size; i++)
msg[i] = rd32a(hw, NGBE_PXMBMEM(vf), i);
/* Acknowledge the message and release buffer */
wr32(hw, NGBE_PXMAILBOX(vf), NGBE_PXMAILBOX_ACK);
/* update stats */
hw->mbx.stats.msgs_rx++;
out_no_read:
return err;
}
/**
* ngbe_init_mbx_params_pf - set initial values for pf mailbox
* @hw: pointer to the HW structure
*
* Initializes the hw->mbx struct to correct values for pf mailbox
*/
void ngbe_init_mbx_params_pf(struct ngbe_hw *hw)
{
struct ngbe_mbx_info *mbx = &hw->mbx;
mbx->timeout = 0;
mbx->udelay = 0;
mbx->size = NGBE_VXMAILBOX_SIZE;
mbx->ops.read = ngbe_read_mbx_pf;
mbx->ops.write = ngbe_write_mbx_pf;
mbx->ops.read_posted = ngbe_read_posted_mbx;
mbx->ops.write_posted = ngbe_write_posted_mbx;
mbx->ops.check_for_msg = ngbe_check_for_msg_pf;
mbx->ops.check_for_ack = ngbe_check_for_ack_pf;
mbx->ops.check_for_rst = ngbe_check_for_rst_pf;
mbx->stats.msgs_tx = 0;
mbx->stats.msgs_rx = 0;
mbx->stats.reqs = 0;
mbx->stats.acks = 0;
mbx->stats.rsts = 0;
}

View File

@ -0,0 +1,172 @@
/*
* WangXun Gigabit PCI Express Linux driver
* Copyright (c) 2015 - 2017 Beijing WangXun Technology Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* The full GNU General Public License is included in this distribution in
* the file called "COPYING".
*/
#ifndef _NGBE_MBX_H_
#define _NGBE_MBX_H_
#define NGBE_VXMAILBOX_SIZE (16)
/**
* VF Registers
**/
#define NGBE_VXMAILBOX 0x00600
#define NGBE_VXMAILBOX_REQ ((0x1) << 0) /* Request for PF Ready bit */
#define NGBE_VXMAILBOX_ACK ((0x1) << 1) /* Ack PF message received */
#define NGBE_VXMAILBOX_VFU ((0x1) << 2) /* VF owns the mailbox buffer */
#define NGBE_VXMAILBOX_PFU ((0x1) << 3) /* PF owns the mailbox buffer */
#define NGBE_VXMAILBOX_PFSTS ((0x1) << 4) /* PF wrote a message in the MB */
#define NGBE_VXMAILBOX_PFACK ((0x1) << 5) /* PF ack the previous VF msg */
#define NGBE_VXMAILBOX_RSTI ((0x1) << 6) /* PF has reset indication */
#define NGBE_VXMAILBOX_RSTD ((0x1) << 7) /* PF has indicated reset done */
#define NGBE_VXMAILBOX_R2C_BITS (NGBE_VXMAILBOX_RSTD | \
NGBE_VXMAILBOX_PFSTS | NGBE_VXMAILBOX_PFACK)
#define NGBE_VXMBMEM 0x00C00 /* 16*4B */
/**
* PF Registers
**/
#define NGBE_PXMAILBOX(i) (0x00600 + (4 * (i))) /* i=[0,7] */
#define NGBE_PXMAILBOX_STS ((0x1) << 0) /* Initiate message send to VF */
#define NGBE_PXMAILBOX_ACK ((0x1) << 1) /* Ack message recv'd from VF */
#define NGBE_PXMAILBOX_VFU ((0x1) << 2) /* VF owns the mailbox buffer */
#define NGBE_PXMAILBOX_PFU ((0x1) << 3) /* PF owns the mailbox buffer */
#define NGBE_PXMAILBOX_RVFU ((0x1) << 4) /* Reset VFU - used when VF stuck*/
#define NGBE_PXMBMEM(i) (0x5000 + (64 * (i))) /* i=[0,7] */
#define NGBE_VFLRP(i) (0x00490 + (4 * (i))) /* i=[0,1] */
#define NGBE_VFLRE 0x004A0
#define NGBE_VFLREC 0x004A8
/* SR-IOV specific macros */
#define NGBE_MBVFICR 0x00480
#define NGBE_MBVFICR_INDEX(vf) ((vf) >> 4)
#define NGBE_MBVFICR_VFREQ_MASK (0x0000FFFF) /* bits for VF messages */
#define NGBE_MBVFICR_VFREQ_VF1 (0x00000001) /* bit for VF 1 message */
#define NGBE_MBVFICR_VFACK_MASK (0xFFFF0000) /* bits for VF acks */
#define NGBE_MBVFICR_VFACK_VF1 (0x00010000) /* bit for VF 1 ack */
/**
* Messages
**/
/* If it's a NGBE_VF_* msg then it originates in the VF and is sent to the
* PF. The reverse is true if it is NGBE_PF_*.
* Message ACK's are the value or'd with 0xF0000000
*/
#define NGBE_VT_MSGTYPE_ACK 0x80000000 /* Messages below or'd with
* this are the ACK */
#define NGBE_VT_MSGTYPE_NACK 0x40000000 /* Messages below or'd with
* this are the NACK */
#define NGBE_VT_MSGTYPE_CTS 0x20000000 /* Indicates that VF is still
* clear to send requests */
#define NGBE_VT_MSGINFO_SHIFT 16
/* bits 23:16 are used for extra info for certain messages */
#define NGBE_VT_MSGINFO_MASK (0xFF << NGBE_VT_MSGINFO_SHIFT)
/* definitions to support mailbox API version negotiation */
/*
* each element denotes a version of the API; existing numbers may not
* change; any additions must go at the end
*/
enum ngbe_pfvf_api_rev {
ngbe_mbox_api_null,
ngbe_mbox_api_10, /* API version 1.0, linux/freebsd VF driver */
ngbe_mbox_api_11, /* API version 1.1, linux/freebsd VF driver */
ngbe_mbox_api_12, /* API version 1.2, linux/freebsd VF driver */
ngbe_mbox_api_13, /* API version 1.3, linux/freebsd VF driver */
ngbe_mbox_api_20, /* API version 2.0, solaris Phase1 VF driver */
ngbe_mbox_api_unknown, /* indicates that API version is not known */
};
/* mailbox API, legacy requests */
#define NGBE_VF_RESET 0x01 /* VF requests reset */
#define NGBE_VF_SET_MAC_ADDR 0x02 /* VF requests PF to set MAC addr */
#define NGBE_VF_SET_MULTICAST 0x03 /* VF requests PF to set MC addr */
#define NGBE_VF_SET_VLAN 0x04 /* VF requests PF to set VLAN */
/* mailbox API, version 1.0 VF requests */
#define NGBE_VF_SET_LPE 0x05 /* VF requests PF to set VMOLR.LPE */
#define NGBE_VF_SET_MACVLAN 0x06 /* VF requests PF for unicast filter */
#define NGBE_VF_API_NEGOTIATE 0x08 /* negotiate API version */
/* mailbox API, version 1.1 VF requests */
#define NGBE_VF_GET_QUEUES 0x09 /* get queue configuration */
#define NGBE_NOFITY_VF_LINK_STATUS 0x01
/* mailbox API, version 1.2 VF requests */
#define NGBE_VF_GET_RETA 0x0a /* VF request for RETA */
#define NGBE_VF_GET_RSS_KEY 0x0b /* get RSS key */
#define NGBE_VF_UPDATE_XCAST_MODE 0x0c
#define NGBE_VF_BACKUP 0x8001 /* VF requests backup */
#define NGBE_VF_GET_LINK_STATUS 0x20 /* VF get link status from PF */
/* mode choices for IXGBE_VF_UPDATE_XCAST_MODE */
enum ngbevf_xcast_modes {
NGBEVF_XCAST_MODE_NONE = 0,
NGBEVF_XCAST_MODE_MULTI,
NGBEVF_XCAST_MODE_ALLMULTI,
NGBEVF_XCAST_MODE_PROMISC,
};
/* GET_QUEUES return data indices within the mailbox */
#define NGBE_VF_TX_QUEUES 1 /* number of Tx queues supported */
#define NGBE_VF_RX_QUEUES 2 /* number of Rx queues supported */
#define NGBE_VF_TRANS_VLAN 3 /* Indication of port vlan */
#define NGBE_VF_DEF_QUEUE 4 /* Default queue offset */
/* length of permanent address message returned from PF */
#define NGBE_VF_PERMADDR_MSG_LEN 4
/* word in permanent address message with the current multicast type */
#define NGBE_VF_MC_TYPE_WORD 3
#define NGBE_PF_CONTROL_MSG 0x0100 /* PF control message */
/* mailbox API, version 2.0 VF requests */
#define NGBE_VF_API_NEGOTIATE 0x08 /* negotiate API version */
#define NGBE_VF_GET_QUEUES 0x09 /* get queue configuration */
#define NGBE_VF_ENABLE_MACADDR 0x0A /* enable MAC address */
#define NGBE_VF_DISABLE_MACADDR 0x0B /* disable MAC address */
#define NGBE_VF_GET_MACADDRS 0x0C /* get all configured MAC addrs */
#define NGBE_VF_SET_MCAST_PROMISC 0x0D /* enable multicast promiscuous */
#define NGBE_VF_GET_MTU 0x0E /* get bounds on MTU */
#define NGBE_VF_SET_MTU 0x0F /* set a specific MTU */
/* mailbox API, version 2.0 PF requests */
#define NGBE_PF_TRANSPARENT_VLAN 0x0101 /* enable transparent vlan */
#define NGBE_VF_MBX_INIT_TIMEOUT 2000 /* number of retries on mailbox */
#define NGBE_VF_MBX_INIT_DELAY 500 /* microseconds between retries */
int ngbe_read_mbx(struct ngbe_hw *, u32 *, u16, u16);
int ngbe_write_mbx(struct ngbe_hw *, u32 *, u16, u16);
int ngbe_read_posted_mbx(struct ngbe_hw *, u32 *, u16, u16);
int ngbe_write_posted_mbx(struct ngbe_hw *, u32 *, u16, u16);
int ngbe_check_for_msg(struct ngbe_hw *, u16);
int ngbe_check_for_ack(struct ngbe_hw *, u16);
int ngbe_check_for_rst(struct ngbe_hw *, u16);
void ngbe_init_mbx_ops(struct ngbe_hw *hw);
void ngbe_init_mbx_params_vf(struct ngbe_hw *);
void ngbe_init_mbx_params_pf(struct ngbe_hw *);
#endif /* _NGBE_MBX_H_ */

View File

@ -0,0 +1,219 @@
/*
* WangXun Gigabit PCI Express Linux driver
* Copyright (c) 2015 - 2017 Beijing WangXun Technology Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* The full GNU General Public License is included in this distribution in
* the file called "COPYING".
*/
/* glue for the OS independent part of ngbe
* includes register access macros
*/
#ifndef _NGBE_OSDEP_H_
#define _NGBE_OSDEP_H_
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/if_ether.h>
#include <linux/sched.h>
#include "ngbe_kcompat.h"
#define NGBE_CPU_TO_BE16(_x) cpu_to_be16(_x)
#define NGBE_BE16_TO_CPU(_x) be16_to_cpu(_x)
#define NGBE_CPU_TO_BE32(_x) cpu_to_be32(_x)
#define NGBE_BE32_TO_CPU(_x) be32_to_cpu(_x)
#define msec_delay(_x) msleep(_x)
#define usec_delay(_x) udelay(_x)
#define IOMEM __iomem
#define NGBE_NAME "ngbe"
/* #define DBG 1 */
#define DPRINTK(nlevel, klevel, fmt, args...) \
((void)((NETIF_MSG_##nlevel & adapter->msg_enable) && \
printk(KERN_##klevel NGBE_NAME ": %s: %s: " fmt, \
adapter->netdev->name, \
__func__, ## args)))
#ifndef _WIN32
#define ngbe_emerg(fmt, ...) printk(KERN_EMERG fmt, ## __VA_ARGS__)
#define ngbe_alert(fmt, ...) printk(KERN_ALERT fmt, ## __VA_ARGS__)
#define ngbe_crit(fmt, ...) printk(KERN_CRIT fmt, ## __VA_ARGS__)
#define ngbe_error(fmt, ...) printk(KERN_ERR fmt, ## __VA_ARGS__)
#define ngbe_warn(fmt, ...) printk(KERN_WARNING fmt, ## __VA_ARGS__)
#define ngbe_notice(fmt, ...) printk(KERN_NOTICE fmt, ## __VA_ARGS__)
#define ngbe_info(fmt, ...) printk(KERN_INFO fmt, ## __VA_ARGS__)
#define ngbe_print(fmt, ...) printk(KERN_DEBUG fmt, ## __VA_ARGS__)
#define ngbe_trace(fmt, ...) printk(KERN_INFO fmt, ## __VA_ARGS__)
#else /* _WIN32 */
#define ngbe_error(lvl, fmt, ...) \
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL, \
"%s-error: %s@%d, " fmt, \
"ngbe", __FUNCTION__, __LINE__, ## __VA_ARGS__)
#endif /* !_WIN32 */
#ifdef DBG
#ifndef _WIN32
#define ngbe_debug(fmt, ...) \
printk(KERN_DEBUG \
"%s-debug: %s@%d, " fmt, \
"ngbe", __FUNCTION__, __LINE__, ## __VA_ARGS__)
#else /* _WIN32 */
#define ngbe_debug(fmt, ...) \
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL, \
"%s-debug: %s@%d, " fmt, \
"ngbe", __FUNCTION__, __LINE__, ## __VA_ARGS__)
#endif /* _WIN32 */
#else /* DBG */
#define ngbe_debug(fmt, ...) do {} while (0)
#endif /* DBG */
#ifdef DBG
#define ASSERT(_x) BUG_ON(!(_x))
#define DEBUGOUT(S) printk(KERN_DEBUG S)
#define DEBUGOUT1(S, A...) printk(KERN_DEBUG S, ## A)
#define DEBUGOUT2(S, A...) printk(KERN_DEBUG S, ## A)
#define DEBUGOUT3(S, A...) printk(KERN_DEBUG S, ## A)
#define DEBUGOUT4(S, A...) printk(KERN_DEBUG S, ## A)
#define DEBUGOUT5(S, A...) printk(KERN_DEBUG S, ## A)
#define DEBUGOUT6(S, A...) printk(KERN_DEBUG S, ## A)
#define DEBUGFUNC(fmt, ...) ngbe_debug(fmt, ## __VA_ARGS__)
#else
#define ASSERT(_x) do {} while (0)
#define DEBUGOUT(S) do {} while (0)
#define DEBUGOUT1(S, A...) do {} while (0)
#define DEBUGOUT2(S, A...) do {} while (0)
#define DEBUGOUT3(S, A...) do {} while (0)
#define DEBUGOUT4(S, A...) do {} while (0)
#define DEBUGOUT5(S, A...) do {} while (0)
#define DEBUGOUT6(S, A...) do {} while (0)
#define DEBUGFUNC(fmt, ...) do {} while (0)
#endif
#define NGBE_SFP_DETECT_RETRIES 2
struct ngbe_hw;
struct ngbe_msg {
u16 msg_enable;
};
struct net_device *ngbe_hw_to_netdev(const struct ngbe_hw *hw);
struct ngbe_msg *ngbe_hw_to_msg(const struct ngbe_hw *hw);
#define hw_dbg(hw, format, arg...) \
netdev_dbg(ngbe_hw_to_netdev(hw), format, ## arg)
#define hw_err(hw, format, arg...) \
netdev_err(ngbe_hw_to_netdev(hw), format, ## arg)
#define e_dev_info(format, arg...) \
dev_info(pci_dev_to_dev(adapter->pdev), format, ## arg)
#define e_dev_warn(format, arg...) \
dev_warn(pci_dev_to_dev(adapter->pdev), format, ## arg)
#define e_dev_err(format, arg...) \
dev_err(pci_dev_to_dev(adapter->pdev), format, ## arg)
#define e_dev_notice(format, arg...) \
dev_notice(pci_dev_to_dev(adapter->pdev), format, ## arg)
#define e_dbg(msglvl, format, arg...) \
netif_dbg(adapter, msglvl, adapter->netdev, format, ## arg)
#define e_info(msglvl, format, arg...) \
netif_info(adapter, msglvl, adapter->netdev, format, ## arg)
#define e_err(msglvl, format, arg...) \
netif_err(adapter, msglvl, adapter->netdev, format, ## arg)
#define e_warn(msglvl, format, arg...) \
netif_warn(adapter, msglvl, adapter->netdev, format, ## arg)
#define e_crit(msglvl, format, arg...) \
netif_crit(adapter, msglvl, adapter->netdev, format, ## arg)
#define NGBE_FAILED_READ_CFG_DWORD 0xffffffffU
#define NGBE_FAILED_READ_CFG_WORD 0xffffU
#define NGBE_FAILED_READ_CFG_BYTE 0xffU
extern u32 ngbe_read_reg(struct ngbe_hw *hw, u32 reg, bool quiet);
extern u16 ngbe_read_pci_cfg_word(struct ngbe_hw *hw, u32 reg);
extern void ngbe_write_pci_cfg_word(struct ngbe_hw *hw, u32 reg, u16 value);
#define NGBE_READ_PCIE_WORD ngbe_read_pci_cfg_word
#define NGBE_WRITE_PCIE_WORD ngbe_write_pci_cfg_word
#define NGBE_R32_Q(h, r) ngbe_read_reg(h, r, true)
#ifndef writeq
#define writeq(val, addr) do { writel((u32) (val), addr); \
writel((u32) (val >> 32), (addr + 4)); \
} while (0);
#endif
#define NGBE_EEPROM_GRANT_ATTEMPS 100
#define NGBE_HTONL(_i) htonl(_i)
#define NGBE_NTOHL(_i) ntohl(_i)
#define NGBE_NTOHS(_i) ntohs(_i)
#define NGBE_CPU_TO_LE32(_i) cpu_to_le32(_i)
#define NGBE_LE32_TO_CPUS(_i) le32_to_cpus(_i)
enum {
NGBE_ERROR_SOFTWARE,
NGBE_ERROR_POLLING,
NGBE_ERROR_INVALID_STATE,
NGBE_ERROR_UNSUPPORTED,
NGBE_ERROR_ARGUMENT,
NGBE_ERROR_CAUTION,
};
#define ERROR_REPORT(level, format, arg...) do { \
switch (level) { \
case NGBE_ERROR_SOFTWARE: \
case NGBE_ERROR_CAUTION: \
case NGBE_ERROR_POLLING: \
netif_warn(ngbe_hw_to_msg(hw), drv, ngbe_hw_to_netdev(hw), \
format, ## arg); \
break; \
case NGBE_ERROR_INVALID_STATE: \
case NGBE_ERROR_UNSUPPORTED: \
case NGBE_ERROR_ARGUMENT: \
netif_err(ngbe_hw_to_msg(hw), hw, ngbe_hw_to_netdev(hw), \
format, ## arg); \
break; \
default: \
break; \
} \
} while (0)
#define ERROR_REPORT1 ERROR_REPORT
#define ERROR_REPORT2 ERROR_REPORT
#define ERROR_REPORT3 ERROR_REPORT
#define UNREFERENCED_XPARAMETER
#define UNREFERENCED_1PARAMETER(_p) do { \
uninitialized_var(_p); \
} while (0)
#define UNREFERENCED_2PARAMETER(_p, _q) do { \
uninitialized_var(_p); \
uninitialized_var(_q); \
} while (0)
#define UNREFERENCED_3PARAMETER(_p, _q, _r) do { \
uninitialized_var(_p); \
uninitialized_var(_q); \
uninitialized_var(_r); \
} while (0)
#define UNREFERENCED_4PARAMETER(_p, _q, _r, _s) do { \
uninitialized_var(_p); \
uninitialized_var(_q); \
uninitialized_var(_r); \
uninitialized_var(_s); \
} while (0)
#define UNREFERENCED_PARAMETER(_p) UNREFERENCED_1PARAMETER(_p)
#endif /* _NGBE_OSDEP_H_ */

View File

@ -0,0 +1,932 @@
/*
* WangXun Gigabit PCI Express Linux driver
* Copyright (c) 2015 - 2017 Beijing WangXun Technology Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* The full GNU General Public License is included in this distribution in
* the file called "COPYING".
*
*/
#include <linux/types.h>
#include <linux/module.h>
#include "ngbe.h"
/* This is the only thing that needs to be changed to adjust the
* maximum number of ports that the driver can manage.
*/
#define NGBE_MAX_NIC 32
#define OPTION_UNSET -1
#define OPTION_DISABLED 0
#define OPTION_ENABLED 1
#define STRINGIFY(foo) #foo /* magic for getting defines into strings */
#define XSTRINGIFY(bar) STRINGIFY(bar)
/* All parameters are treated the same, as an integer array of values.
* This macro just reduces the need to repeat the same declaration code
* over and over (plus this helps to avoid typo bugs).
*/
#define NGBE_PARAM_INIT { [0 ... NGBE_MAX_NIC] = OPTION_UNSET }
#ifndef module_param_array
/* Module Parameters are always initialized to -1, so that the driver
* can tell the difference between no user specified value or the
* user asking for the default value.
* The true default values are loaded in when ngbe_check_options is called.
*
* This is a GCC extension to ANSI C.
* See the item "Labelled Elements in Initializers" in the section
* "Extensions to the C Language Family" of the GCC documentation.
*/
#define NGBE_PARAM(X, desc) \
static const int __devinitconst X[NGBE_MAX_NIC+1] = NGBE_PARAM_INIT; \
MODULE_PARM(X, "1-" __MODULE_STRING(NGBE_MAX_NIC) "i"); \
MODULE_PARM_DESC(X, desc);
#else /* !module_param_array */
#define NGBE_PARAM(X, desc) \
static int __devinitdata X[NGBE_MAX_NIC+1] = NGBE_PARAM_INIT; \
static unsigned int num_##X; \
module_param_array_named(X, X, int, &num_##X, 0); \
MODULE_PARM_DESC(X, desc);
#endif /* module_param_array */
/* IntMode (Interrupt Mode)
*
* Valid Range: 0-2
* - 0 - Legacy Interrupt
* - 1 - MSI Interrupt
* - 2 - MSI-X Interrupt(s)
*
* Default Value: 2
*/
NGBE_PARAM(InterruptType, "Change Interrupt Mode (0=Legacy, 1=MSI, 2=MSI-X), "
"default IntMode (deprecated)");
NGBE_PARAM(IntMode, "Change Interrupt Mode (0=Legacy, 1=MSI, 2=MSI-X), "
"default 2");
#define NGBE_INT_LEGACY 0
#define NGBE_INT_MSI 1
#define NGBE_INT_MSIX 2
#define NGBE_DEFAULT_INT NGBE_INT_MSIX
/* MQ - Multiple Queue enable/disable
*
* Valid Range: 0, 1
* - 0 - disables MQ
* - 1 - enables MQ
*
* Default Value: 1
*/
NGBE_PARAM(MQ, "Disable or enable Multiple Queues, default 1");
/* RSS - Receive-Side Scaling (RSS) Descriptor Queues
*
* Valid Range: 0-64
* - 0 - enables RSS and sets the Desc. Q's to min(64, num_online_cpus()).
* - 1-64 - enables RSS and sets the Desc. Q's to the specified value.
*
* Default Value: 0
*/
NGBE_PARAM(RSS, "Number of Receive-Side Scaling Descriptor Queues, "
"default 0=number of cpus");
/* VMDQ - Virtual Machine Device Queues (VMDQ)
*
* Valid Range: 1-16
* - 1 Disables VMDQ by allocating only a single queue.
* - 2-16 - enables VMDQ and sets the Desc. Q's to the specified value.
*
* Default Value: 1
*/
#define NGBE_DEFAULT_NUM_VMDQ 8
NGBE_PARAM(VMDQ, "Number of Virtual Machine Device Queues: 0/1 = disable, "
"2-16 enable (default=" XSTRINGIFY(NGBE_DEFAULT_NUM_VMDQ) ")");
#ifdef CONFIG_PCI_IOV
/* max_vfs - SR I/O Virtualization
*
* Valid Range: 0-63
* - 0 Disables SR-IOV
* - 1-63 - enables SR-IOV and sets the number of VFs enabled
*
* Default Value: 0
*/
#define MAX_SRIOV_VFS 8
NGBE_PARAM(max_vfs, "Number of Virtual Functions: 0 = disable (default), "
"1-" XSTRINGIFY(MAX_SRIOV_VFS) " = enable "
"this many VFs");
/* VEPA - Set internal bridge to VEPA mode
*
* Valid Range: 0-1
* - 0 Set bridge to VEB mode
* - 1 Set bridge to VEPA mode
*
* Default Value: 0
*/
/*
*Note:
*=====
* This provides ability to ensure VEPA mode on the internal bridge even if
* the kernel does not support the netdev bridge setting operations.
*/
NGBE_PARAM(VEPA, "VEPA Bridge Mode: 0 = VEB (default), 1 = VEPA");
#endif
/* Interrupt Throttle Rate (interrupts/sec)
*
* Valid Range: 980-500000 (0=off, 1=dynamic)
*
* Default Value: 1
*/
#define DEFAULT_ITR (NGBE_STATIC_ITR == 0) || \
(NGBE_STATIC_ITR == 1)?NGBE_STATIC_ITR:(u16)((1000000/NGBE_STATIC_ITR) << 2)
NGBE_PARAM(InterruptThrottleRate, "Maximum interrupts per second, per vector, "
"(0,1,980-500000), default 1");
#define MAX_ITR NGBE_MAX_INT_RATE
#define MIN_ITR NGBE_MIN_INT_RATE
#ifndef NGBE_NO_LLI
/* LLIPort (Low Latency Interrupt TCP Port)
*
* Valid Range: 0 - 65535
*
* Default Value: 0 (disabled)
*/
NGBE_PARAM(LLIPort, "Low Latency Interrupt TCP Port (0-65535)");
#define DEFAULT_LLIPORT 0
#define MAX_LLIPORT 0xFFFF
#define MIN_LLIPORT 0
/* LLISize (Low Latency Interrupt on Packet Size)
*
* Valid Range: 0 - 1500
*
* Default Value: 0 (disabled)
*/
NGBE_PARAM(LLISize, "Low Latency Interrupt on Packet Size (0-1500)");
#define DEFAULT_LLISIZE 0
#define MAX_LLISIZE 1500
#define MIN_LLISIZE 0
/* LLIEType (Low Latency Interrupt Ethernet Type)
*
* Valid Range: 0 - 0x8fff
*
* Default Value: 0 (disabled)
*/
NGBE_PARAM(LLIEType, "Low Latency Interrupt Ethernet Protocol Type");
#define DEFAULT_LLIETYPE 0
#define MAX_LLIETYPE 0x8fff
#define MIN_LLIETYPE 0
/* LLIVLANP (Low Latency Interrupt on VLAN priority threshold)
*
* Valid Range: 0 - 7
*
* Default Value: 0 (disabled)
*/
NGBE_PARAM(LLIVLANP, "Low Latency Interrupt on VLAN priority threshold");
#define DEFAULT_LLIVLANP 0
#define MAX_LLIVLANP 7
#define MIN_LLIVLANP 0
#endif /* NGBE_NO_LLI */
#ifdef HAVE_TX_MQ
/* Software ATR packet sample rate
*
* Valid Range: 0-255 0 = off, 1-255 = rate of Tx packet inspection
*
* Default Value: 20
*/
NGBE_PARAM(AtrSampleRate, "Software ATR Tx packet sample rate");
#define NGBE_MAX_ATR_SAMPLE_RATE 255
#define NGBE_MIN_ATR_SAMPLE_RATE 1
#define NGBE_ATR_SAMPLE_RATE_OFF 0
#define NGBE_DEFAULT_ATR_SAMPLE_RATE 20
#endif /* HAVE_TX_MQ */
/* Enable/disable Large Receive Offload
*
* Valid Values: 0(off), 1(on)
*
* Default Value: 1
*/
NGBE_PARAM(LRO, "Large Receive Offload (0,1), default 1 = on");
/* Enable/disable support for DMA coalescing
*
* Valid Values: 0(off), 41 - 10000(on)
*
* Default Value: 0
*/
NGBE_PARAM(dmac_watchdog,
"DMA coalescing watchdog in microseconds (0,41-10000),"
"default 0 = off");
/* Rx buffer mode
*
* Valid Range: 0-1 0 = no header split, 1 = hdr split
*
* Default Value: 0
*/
NGBE_PARAM(RxBufferMode, "0=(default)no header split\n"
"\t\t\t1=hdr split for recognized packet\n");
#define NGBE_RXBUFMODE_NO_HEADER_SPLIT 0
#define NGBE_RXBUFMODE_HEADER_SPLIT 1
#define NGBE_DEFAULT_RXBUFMODE NGBE_RXBUFMODE_NO_HEADER_SPLIT
struct ngbe_option {
enum { enable_option, range_option, list_option } type;
const char *name;
const char *err;
const char *msg;
int def;
union {
struct { /* range_option info */
int min;
int max;
} r;
struct { /* list_option info */
int nr;
const struct ngbe_opt_list {
int i;
char *str;
} *p;
} l;
} arg;
};
static int __devinit ngbe_validate_option(u32 *value,
struct ngbe_option *opt)
{
int val = (int)*value;
if (val == OPTION_UNSET) {
ngbe_info("ngbe: Invalid %s specified (%d), %s\n",
opt->name, val, opt->err);
*value = (u32)opt->def;
return 0;
}
switch (opt->type) {
case enable_option:
switch (val) {
case OPTION_ENABLED:
ngbe_info("ngbe: %s Enabled\n", opt->name);
return 0;
case OPTION_DISABLED:
ngbe_info("ngbe: %s Disabled\n", opt->name);
return 0;
}
break;
case range_option:
if ((val >= opt->arg.r.min && val <= opt->arg.r.max) ||
val == opt->def) {
if (opt->msg)
ngbe_info("ngbe: %s set to %d, %s\n",
opt->name, val, opt->msg);
else
ngbe_info("ngbe: %s set to %d\n",
opt->name, val);
return 0;
}
break;
case list_option: {
int i;
const struct ngbe_opt_list *ent;
for (i = 0; i < opt->arg.l.nr; i++) {
ent = &opt->arg.l.p[i];
if (val == ent->i) {
if (ent->str[0] != '\0')
ngbe_info("%s\n", ent->str);
return 0;
}
}
}
break;
default:
BUG_ON(1);
}
ngbe_info("ngbe: Invalid %s specified (%d), %s\n",
opt->name, val, opt->err);
*value = (u32)opt->def;
return -1;
}
/**
* ngbe_check_options - Range Checking for Command Line Parameters
* @adapter: board private structure
*
* This routine checks all command line parameters for valid user
* input. If an invalid value is given, or if no user specified
* value exists, a default value is used. The final value is stored
* in a variable in the adapter structure.
**/
void __devinit ngbe_check_options(struct ngbe_adapter *adapter)
{
u32 bd = adapter->bd_number;
u32 *aflags = &adapter->flags;
struct ngbe_ring_feature *feature = adapter->ring_feature;
u32 vmdq;
if (bd >= NGBE_MAX_NIC) {
ngbe_notice("Warning: no configuration for board #%d\n", bd);
ngbe_notice("Using defaults for all values\n");
#ifndef module_param_array
bd = NGBE_MAX_NIC;
#endif
}
{ /* Interrupt Mode */
u32 int_mode;
static struct ngbe_option opt = {
.type = range_option,
.name = "Interrupt Mode",
.err =
"using default of "__MODULE_STRING(NGBE_DEFAULT_INT),
.def = NGBE_DEFAULT_INT,
.arg = { .r = { .min = NGBE_INT_LEGACY,
.max = NGBE_INT_MSIX} }
};
#ifdef module_param_array
if (num_IntMode > bd || num_InterruptType > bd) {
#endif
int_mode = IntMode[bd];
if (int_mode == OPTION_UNSET)
int_mode = InterruptType[bd];
ngbe_validate_option(&int_mode, &opt);
switch (int_mode) {
case NGBE_INT_MSIX:
if (!(*aflags & NGBE_FLAG_MSIX_CAPABLE))
ngbe_info(
"Ignoring MSI-X setting; "
"support unavailable\n");
break;
case NGBE_INT_MSI:
if (!(*aflags & NGBE_FLAG_MSI_CAPABLE)) {
ngbe_info(
"Ignoring MSI setting; "
"support unavailable\n");
} else {
*aflags &= ~NGBE_FLAG_MSIX_CAPABLE;
}
break;
case NGBE_INT_LEGACY:
default:
*aflags &= ~NGBE_FLAG_MSIX_CAPABLE;
*aflags &= ~NGBE_FLAG_MSI_CAPABLE;
break;
}
#ifdef module_param_array
} else {
/* default settings */
if (opt.def == NGBE_INT_MSIX &&
*aflags & NGBE_FLAG_MSIX_CAPABLE) {
*aflags |= NGBE_FLAG_MSIX_CAPABLE;
*aflags |= NGBE_FLAG_MSI_CAPABLE;
} else if (opt.def == NGBE_INT_MSI &&
*aflags & NGBE_FLAG_MSI_CAPABLE) {
*aflags &= ~NGBE_FLAG_MSIX_CAPABLE;
*aflags |= NGBE_FLAG_MSI_CAPABLE;
} else {
*aflags &= ~NGBE_FLAG_MSIX_CAPABLE;
*aflags &= ~NGBE_FLAG_MSI_CAPABLE;
}
}
#endif
}
{ /* Multiple Queue Support */
static struct ngbe_option opt = {
.type = enable_option,
.name = "Multiple Queue Support",
.err = "defaulting to Enabled",
.def = OPTION_ENABLED
};
#ifdef module_param_array
if (num_MQ > bd) {
#endif
u32 mq = MQ[bd];
ngbe_validate_option(&mq, &opt);
if (mq)
*aflags |= NGBE_FLAG_MQ_CAPABLE;
else
*aflags &= ~NGBE_FLAG_MQ_CAPABLE;
#ifdef module_param_array
} else {
if (opt.def == OPTION_ENABLED)
*aflags |= NGBE_FLAG_MQ_CAPABLE;
else
*aflags &= ~NGBE_FLAG_MQ_CAPABLE;
}
#endif
/* Check Interoperability */
if ((*aflags & NGBE_FLAG_MQ_CAPABLE) &&
!(*aflags & NGBE_FLAG_MSIX_CAPABLE)) {
DPRINTK(PROBE, INFO,
"Multiple queues are not supported while MSI-X "
"is disabled. Disabling Multiple Queues.\n");
*aflags &= ~NGBE_FLAG_MQ_CAPABLE;
}
}
{ /* Receive-Side Scaling (RSS) */
static struct ngbe_option opt = {
.type = range_option,
.name = "Receive-Side Scaling (RSS)",
.err = "using default.",
.def = 0,
.arg = { .r = { .min = 0,
.max = 1} }
};
u32 rss = RSS[bd];
/* adjust Max allowed RSS queues based on MAC type */
opt.arg.r.max = min_t(int, ngbe_max_rss_indices(adapter),
num_online_cpus());
#ifdef module_param_array
if (num_RSS > bd) {
#endif
ngbe_validate_option(&rss, &opt);
/* base it off num_online_cpus() with hardware limit */
if (!rss)
rss = min_t(int, opt.arg.r.max,
num_online_cpus());
feature[RING_F_RSS].limit = (u16)rss;
#ifdef module_param_array
} else if (opt.def == 0) {
rss = min_t(int, ngbe_max_rss_indices(adapter),
num_online_cpus());
feature[RING_F_RSS].limit = rss;
}
#endif
/* Check Interoperability */
if (rss > 1) {
if (!(*aflags & NGBE_FLAG_MQ_CAPABLE)) {
DPRINTK(PROBE, INFO,
"Multiqueue is disabled. "
"Limiting RSS.\n");
feature[RING_F_RSS].limit = 1;
}
}
adapter->flags2 |= NGBE_FLAG2_RSS_ENABLED;
}
{ /* Virtual Machine Device Queues (VMDQ) */
static struct ngbe_option opt = {
.type = range_option,
.name = "Virtual Machine Device Queues (VMDQ)",
.err = "defaulting to Disabled",
.def = OPTION_DISABLED,
.arg = { .r = { .min = OPTION_DISABLED,
.max = NGBE_MAX_VMDQ_INDICES
} }
};
#ifdef module_param_array
if (num_VMDQ > bd) {
#endif
vmdq = VMDQ[bd];
ngbe_validate_option(&vmdq, &opt);
/* zero or one both mean disabled from our driver's
* perspective */
if (vmdq > 1) {
*aflags |= NGBE_FLAG_VMDQ_ENABLED;
} else
*aflags &= ~NGBE_FLAG_VMDQ_ENABLED;
feature[RING_F_VMDQ].limit = (u16)vmdq;
#ifdef module_param_array
} else {
if (opt.def == OPTION_DISABLED)
*aflags &= ~NGBE_FLAG_VMDQ_ENABLED;
else
*aflags |= NGBE_FLAG_VMDQ_ENABLED;
feature[RING_F_VMDQ].limit = opt.def;
}
#endif
/* Check Interoperability */
if (*aflags & NGBE_FLAG_VMDQ_ENABLED) {
if (!(*aflags & NGBE_FLAG_MQ_CAPABLE)) {
DPRINTK(PROBE, INFO,
"VMDQ is not supported while multiple "
"queues are disabled. "
"Disabling VMDQ.\n");
*aflags &= ~NGBE_FLAG_VMDQ_ENABLED;
feature[RING_F_VMDQ].limit = 0;
}
}
}
#ifdef CONFIG_PCI_IOV
{ /* Single Root I/O Virtualization (SR-IOV) */
static struct ngbe_option opt = {
.type = range_option,
.name = "I/O Virtualization (IOV)",
.err = "defaulting to Disabled",
.def = OPTION_DISABLED,
.arg = { .r = { .min = OPTION_DISABLED,
.max = MAX_SRIOV_VFS} }
};
#ifdef module_param_array
if (num_max_vfs > bd) {
#endif
u32 vfs = max_vfs[bd];
if (ngbe_validate_option(&vfs, &opt)) {
vfs = 0;
DPRINTK(PROBE, INFO,
"max_vfs out of range "
"Disabling SR-IOV.\n");
}
adapter->num_vfs = vfs;
if (vfs)
*aflags |= NGBE_FLAG_SRIOV_ENABLED;
else
*aflags &= ~NGBE_FLAG_SRIOV_ENABLED;
#ifdef module_param_array
} else {
if (opt.def == OPTION_DISABLED) {
adapter->num_vfs = 0;
*aflags &= ~NGBE_FLAG_SRIOV_ENABLED;
} else {
adapter->num_vfs = opt.def;
*aflags |= NGBE_FLAG_SRIOV_ENABLED;
}
}
#endif
/* Check Interoperability */
if (*aflags & NGBE_FLAG_SRIOV_ENABLED) {
if (!(*aflags & NGBE_FLAG_SRIOV_CAPABLE)) {
DPRINTK(PROBE, INFO,
"IOV is not supported on this "
"hardware. Disabling IOV.\n");
*aflags &= ~NGBE_FLAG_SRIOV_ENABLED;
adapter->num_vfs = 0;
} else if (!(*aflags & NGBE_FLAG_MQ_CAPABLE)) {
DPRINTK(PROBE, INFO,
"IOV is not supported while multiple "
"queues are disabled. "
"Disabling IOV.\n");
*aflags &= ~NGBE_FLAG_SRIOV_ENABLED;
adapter->num_vfs = 0;
}
}
}
{ /* VEPA Bridge Mode enable for SR-IOV mode */
static struct ngbe_option opt = {
.type = range_option,
.name = "VEPA Bridge Mode Enable",
.err = "defaulting to disabled",
.def = OPTION_DISABLED,
.arg = { .r = { .min = OPTION_DISABLED,
.max = OPTION_ENABLED} }
};
#ifdef module_param_array
if (num_VEPA > bd) {
#endif
u32 vepa = VEPA[bd];
ngbe_validate_option(&vepa, &opt);
if (vepa)
adapter->flags |=
NGBE_FLAG_SRIOV_VEPA_BRIDGE_MODE;
#ifdef module_param_array
} else {
if (opt.def == OPTION_ENABLED)
adapter->flags |=
NGBE_FLAG_SRIOV_VEPA_BRIDGE_MODE;
}
#endif
}
#endif /* CONFIG_PCI_IOV */
{ /* Interrupt Throttling Rate */
static struct ngbe_option opt = {
.type = range_option,
.name = "Interrupt Throttling Rate (ints/sec)",
.err = "using default of "__MODULE_STRING(DEFAULT_ITR),
.def = DEFAULT_ITR,
.arg = { .r = { .min = MIN_ITR,
.max = MAX_ITR } }
};
#ifdef module_param_array
if (num_InterruptThrottleRate > bd) {
#endif
u32 itr = InterruptThrottleRate[bd];
switch (itr) {
case 0:
DPRINTK(PROBE, INFO, "%s turned off\n",
opt.name);
adapter->rx_itr_setting = 0;
break;
case 1:
DPRINTK(PROBE, INFO, "dynamic interrupt "
"throttling enabled\n");
adapter->rx_itr_setting = 1;
break;
default:
ngbe_validate_option(&itr, &opt);
/* the first bit is used as control */
adapter->rx_itr_setting = (u16)((1000000/itr) << 2);
break;
}
adapter->tx_itr_setting = adapter->rx_itr_setting;
#ifdef module_param_array
} else {
adapter->rx_itr_setting = opt.def;
adapter->tx_itr_setting = opt.def;
}
#endif
}
#ifndef NGBE_NO_LLI
{ /* Low Latency Interrupt TCP Port*/
static struct ngbe_option opt = {
.type = range_option,
.name = "Low Latency Interrupt TCP Port",
.err = "using default of "
__MODULE_STRING(DEFAULT_LLIPORT),
.def = DEFAULT_LLIPORT,
.arg = { .r = { .min = MIN_LLIPORT,
.max = MAX_LLIPORT } }
};
#ifdef module_param_array
if (num_LLIPort > bd) {
#endif
adapter->lli_port = LLIPort[bd];
if (adapter->lli_port) {
ngbe_validate_option(&adapter->lli_port, &opt);
} else {
DPRINTK(PROBE, INFO, "%s turned off\n",
opt.name);
}
#ifdef module_param_array
} else {
adapter->lli_port = opt.def;
}
#endif
}
{ /* Low Latency Interrupt on Packet Size */
static struct ngbe_option opt = {
.type = range_option,
.name = "Low Latency Interrupt on Packet Size",
.err = "using default of "
__MODULE_STRING(DEFAULT_LLISIZE),
.def = DEFAULT_LLISIZE,
.arg = { .r = { .min = MIN_LLISIZE,
.max = MAX_LLISIZE } }
};
#ifdef module_param_array
if (num_LLISize > bd) {
#endif
adapter->lli_size = LLISize[bd];
if (adapter->lli_size) {
ngbe_validate_option(&adapter->lli_size, &opt);
} else {
DPRINTK(PROBE, INFO, "%s turned off\n",
opt.name);
}
#ifdef module_param_array
} else {
adapter->lli_size = opt.def;
}
#endif
}
{ /* Low Latency Interrupt EtherType*/
static struct ngbe_option opt = {
.type = range_option,
.name = "Low Latency Interrupt on Ethernet Protocol "
"Type",
.err = "using default of "
__MODULE_STRING(DEFAULT_LLIETYPE),
.def = DEFAULT_LLIETYPE,
.arg = { .r = { .min = MIN_LLIETYPE,
.max = MAX_LLIETYPE } }
};
#ifdef module_param_array
if (num_LLIEType > bd) {
#endif
adapter->lli_etype = LLIEType[bd];
if (adapter->lli_etype) {
ngbe_validate_option(&adapter->lli_etype,
&opt);
} else {
DPRINTK(PROBE, INFO, "%s turned off\n",
opt.name);
}
#ifdef module_param_array
} else {
adapter->lli_etype = opt.def;
}
#endif
}
{ /* LLI VLAN Priority */
static struct ngbe_option opt = {
.type = range_option,
.name = "Low Latency Interrupt on VLAN priority "
"threshold",
.err = "using default of "
__MODULE_STRING(DEFAULT_LLIVLANP),
.def = DEFAULT_LLIVLANP,
.arg = { .r = { .min = MIN_LLIVLANP,
.max = MAX_LLIVLANP } }
};
#ifdef module_param_array
if (num_LLIVLANP > bd) {
#endif
adapter->lli_vlan_pri = LLIVLANP[bd];
if (adapter->lli_vlan_pri) {
ngbe_validate_option(&adapter->lli_vlan_pri,
&opt);
} else {
DPRINTK(PROBE, INFO, "%s turned off\n",
opt.name);
}
#ifdef module_param_array
} else {
adapter->lli_vlan_pri = opt.def;
}
#endif
}
#endif /* NGBE_NO_LLI */
#ifdef HAVE_TX_MQ
{ /* Flow Director ATR Tx sample packet rate */
static struct ngbe_option opt = {
.type = range_option,
.name = "Software ATR Tx packet sample rate",
.err = "using default of "
__MODULE_STRING(NGBE_DEFAULT_ATR_SAMPLE_RATE),
.def = NGBE_DEFAULT_ATR_SAMPLE_RATE,
.arg = {.r = {.min = NGBE_ATR_SAMPLE_RATE_OFF,
.max = NGBE_MAX_ATR_SAMPLE_RATE} }
};
static const char atr_string[] =
"ATR Tx Packet sample rate set to";
if (num_AtrSampleRate > bd) {
adapter->atr_sample_rate = AtrSampleRate[bd];
if (adapter->atr_sample_rate) {
ngbe_validate_option(&adapter->atr_sample_rate,
&opt);
DPRINTK(PROBE, INFO, "%s %d\n", atr_string,
adapter->atr_sample_rate);
}
} else {
adapter->atr_sample_rate = opt.def;
}
}
#endif /* HAVE_TX_MQ */
{ /* LRO - Set Large Receive Offload */
struct ngbe_option opt = {
.type = enable_option,
.name = "LRO - Large Receive Offload",
.err = "defaulting to Disabled",
/* lro switch to ON when run on SW and FT platform */
/* emerald temp setting */
#if defined(TXGBE_SUPPORT_DEEPIN_SW) || \
defined(TXGBE_SUPPORT_KYLIN_SW) || \
defined(TXGBE_SUPPORT_KYLIN_FT)
.def = OPTION_ENABLED
#else
.def = OPTION_DISABLED
#endif
};
struct net_device *netdev = adapter->netdev;
#ifdef NGBE_NO_LRO
opt.def = OPTION_DISABLED;
#endif
#ifdef module_param_array
if (num_LRO > bd) {
#endif
u32 lro = LRO[bd];
ngbe_validate_option(&lro, &opt);
if (lro)
netdev->features |= NETIF_F_LRO;
else
netdev->features &= ~NETIF_F_LRO;
#ifdef module_param_array
} else if (opt.def == OPTION_ENABLED) {
netdev->features |= NETIF_F_LRO;
} else {
netdev->features &= ~NETIF_F_LRO;
}
#endif
#ifdef NGBE_NO_LRO
if ((netdev->features & NETIF_F_LRO)) {
DPRINTK(PROBE, INFO,
"RSC is not supported on this "
"hardware. Disabling RSC.\n");
netdev->features &= ~NETIF_F_LRO;
}
#endif /* NGBE_NO_LRO */
}
{ /* DMA Coalescing */
struct ngbe_option opt = {
.type = range_option,
.name = "dmac_watchdog",
.err = "defaulting to 0 (disabled)",
.def = 0,
.arg = { .r = { .min = 41, .max = 10000 } },
};
const char *cmsg = "DMA coalescing not supported on this "
"hardware";
opt.err = cmsg;
opt.msg = cmsg;
opt.arg.r.min = 0;
opt.arg.r.max = 0;
#ifdef module_param_array
if (num_dmac_watchdog > bd) {
#endif
u32 dmac_wd = dmac_watchdog[bd];
ngbe_validate_option(&dmac_wd, &opt);
adapter->hw.mac.dmac_config.watchdog_timer = (u16)dmac_wd;
#ifdef module_param_array
} else {
adapter->hw.mac.dmac_config.watchdog_timer = opt.def;
}
#endif
}
{ /* Rx buffer mode */
u32 rx_buf_mode;
static struct ngbe_option opt = {
.type = range_option,
.name = "Rx buffer mode",
.err = "using default of "
__MODULE_STRING(NGBE_DEFAULT_RXBUFMODE),
.def = NGBE_DEFAULT_RXBUFMODE,
.arg = {.r = {.min = NGBE_RXBUFMODE_NO_HEADER_SPLIT,
.max = NGBE_RXBUFMODE_HEADER_SPLIT} }
};
#ifdef module_param_array
if (num_RxBufferMode > bd) {
#endif
rx_buf_mode = RxBufferMode[bd];
ngbe_validate_option(&rx_buf_mode, &opt);
switch (rx_buf_mode) {
case NGBE_RXBUFMODE_NO_HEADER_SPLIT:
*aflags &= ~NGBE_FLAG_RX_HS_ENABLED;
break;
case NGBE_RXBUFMODE_HEADER_SPLIT:
*aflags |= NGBE_FLAG_RX_HS_ENABLED;
break;
default:
break;
}
#ifdef module_param_array
} else {
*aflags &= ~NGBE_FLAG_RX_HS_ENABLED;
}
#endif
}
}

View File

@ -0,0 +1,293 @@
#include <linux/delay.h>
#include <linux/pci.h>
#include "ngbe_pcierr.h"
#include "ngbe.h"
#define NGBE_ROOT_PORT_INTR_ON_MESG_MASK (PCI_ERR_ROOT_CMD_COR_EN| \
PCI_ERR_ROOT_CMD_NONFATAL_EN| \
PCI_ERR_ROOT_CMD_FATAL_EN)
#ifndef PCI_ERS_RESULT_NO_AER_DRIVER
/* No AER capabilities registered for the driver */
#define PCI_ERS_RESULT_NO_AER_DRIVER ((__force pci_ers_result_t) 6)
#endif
#if (RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0)))
/* redefinition because centos 6 can't use pci_walk_bus in pci.h*/
struct rw_semaphore pci_bus_sem;
/** pci_walk_bus - walk devices on/under bus, calling callback.
* @top bus whose devices should be walked
* @cb callback to be called for each device found
* @userdata arbitrary pointer to be passed to callback.
*
* Walk the given bus, including any bridged devices
* on buses under this bus. Call the provided callback
* on each device found.
*
* We check the return of @cb each time. If it returns anything
* other than 0, we break out.
*
*/
void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *),
void *userdata)
{
struct pci_dev *dev;
struct pci_bus *bus;
struct list_head *next;
int retval;
bus = top;
down_read(&pci_bus_sem);
next = top->devices.next;
for (;;) {
if (next == &bus->devices) {
/* end of this bus, go up or finish */
if (bus == top)
break;
next = bus->self->bus_list.next;
bus = bus->self->bus;
continue;
}
dev = list_entry(next, struct pci_dev, bus_list);
if (dev->subordinate) {
/* this is a pci-pci bridge, do its devices next */
next = dev->subordinate->devices.next;
bus = dev->subordinate;
} else
next = dev->bus_list.next;
retval = cb(dev, userdata);
if (retval)
break;
}
up_read(&pci_bus_sem);
}
#endif
static pci_ers_result_t merge_result(enum pci_ers_result orig,
enum pci_ers_result new)
{
if (new == PCI_ERS_RESULT_NO_AER_DRIVER)
return PCI_ERS_RESULT_NO_AER_DRIVER;
if (new == PCI_ERS_RESULT_NONE)
return orig;
switch (orig) {
case PCI_ERS_RESULT_CAN_RECOVER:
case PCI_ERS_RESULT_RECOVERED:
orig = new;
break;
case PCI_ERS_RESULT_DISCONNECT:
if (new == PCI_ERS_RESULT_NEED_RESET)
orig = PCI_ERS_RESULT_NEED_RESET;
break;
default:
break;
}
return orig;
}
static int ngbe_report_error_detected(struct pci_dev *dev,
pci_channel_state_t state,
enum pci_ers_result *result)
{
pci_ers_result_t vote;
const struct pci_error_handlers *err_handler;
device_lock(&dev->dev);
if (
!dev->driver ||
!dev->driver->err_handler ||
!dev->driver->err_handler->error_detected) {
/*
* If any device in the subtree does not have an error_detected
* callback, PCI_ERS_RESULT_NO_AER_DRIVER prevents subsequent
* error callbacks of "any" device in the subtree, and will
* exit in the disconnected error state.
*/
if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE)
vote = PCI_ERS_RESULT_NO_AER_DRIVER;
else
vote = PCI_ERS_RESULT_NONE;
} else {
err_handler = dev->driver->err_handler;
vote = err_handler->error_detected(dev, state);
}
*result = merge_result(*result, vote);
device_unlock(&dev->dev);
return 0;
}
static int ngbe_report_frozen_detected(struct pci_dev *dev, void *data)
{
return ngbe_report_error_detected(dev, pci_channel_io_frozen, data);
}
static int ngbe_report_mmio_enabled(struct pci_dev *dev, void *data)
{
pci_ers_result_t vote, *result = data;
const struct pci_error_handlers *err_handler;
device_lock(&dev->dev);
if (!dev->driver ||
!dev->driver->err_handler ||
!dev->driver->err_handler->mmio_enabled)
goto out;
err_handler = dev->driver->err_handler;
vote = err_handler->mmio_enabled(dev);
*result = merge_result(*result, vote);
out:
device_unlock(&dev->dev);
return 0;
}
static int ngbe_report_slot_reset(struct pci_dev *dev, void *data)
{
pci_ers_result_t vote, *result = data;
const struct pci_error_handlers *err_handler;
device_lock(&dev->dev);
if (!dev->driver ||
!dev->driver->err_handler ||
!dev->driver->err_handler->slot_reset)
goto out;
err_handler = dev->driver->err_handler;
vote = err_handler->slot_reset(dev);
*result = merge_result(*result, vote);
out:
device_unlock(&dev->dev);
return 0;
}
static int ngbe_report_resume(struct pci_dev *dev, void *data)
{
const struct pci_error_handlers *err_handler;
device_lock(&dev->dev);
dev->error_state = pci_channel_io_normal;
if (
!dev->driver ||
!dev->driver->err_handler ||
!dev->driver->err_handler->resume)
goto out;
err_handler = dev->driver->err_handler;
err_handler->resume(dev);
out:
device_unlock(&dev->dev);
return 0;
}
void ngbe_pcie_do_recovery(struct pci_dev *dev)
{
pci_ers_result_t status = PCI_ERS_RESULT_CAN_RECOVER;
struct pci_bus *bus;
u32 reg32;
int pos;
int delay = 1;
u32 id;
u16 ctrl;
/*
* Error recovery runs on all subordinates of the first downstream port.
* If the downstream port detected the error, it is cleared at the end.
*/
if (!(pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM))
dev = dev->bus->self;
bus = dev->subordinate;
pci_walk_bus(bus, ngbe_report_frozen_detected, &status);
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
if (pos) {
/* Disable Root's interrupt in response to error messages */
pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, &reg32);
reg32 &= ~NGBE_ROOT_PORT_INTR_ON_MESG_MASK;
pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
}
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &ctrl);
ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl);
/*
* * PCI spec v3.0 7.6.4.2 requires minimum Trst of 1ms. Double
* * this to 2ms to ensure that we meet the minimum requirement.
* */
msleep(2);
ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl);
/*
* * Trhfa for conventional PCI is 2^25 clock cycles.
* * Assuming a minimum 33MHz clock this results in a 1s
* * delay before we can consider subordinate devices to
* * be re-initialized. PCIe has some ways to shorten this,
* * but we don't make use of them yet.
* */
ssleep(1);
pci_read_config_dword(dev, PCI_COMMAND, &id);
while (id == ~0) {
if (delay > 60000) {
pci_warn(dev, "not ready %dms after %s; giving up\n",
delay - 1, "bus_reset");
return;
}
if (delay > 1000)
pci_info(dev, "not ready %dms after %s; waiting\n",
delay - 1, "bus_reset");
msleep(delay);
delay *= 2;
pci_read_config_dword(dev, PCI_COMMAND, &id);
}
if (delay > 1000)
pci_info(dev, "ready %dms after %s\n", delay - 1,
"bus_reset");
pci_info(dev, "Root Port link has been reset\n");
if (pos) {
/* Clear Root Error Status */
pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &reg32);
pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, reg32);
/* Enable Root Port's interrupt in response to error messages */
pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, &reg32);
reg32 |= NGBE_ROOT_PORT_INTR_ON_MESG_MASK;
pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
}
if (status == PCI_ERS_RESULT_CAN_RECOVER) {
status = PCI_ERS_RESULT_RECOVERED;
pci_dbg(dev, "broadcast mmio_enabled message\n");
pci_walk_bus(bus, ngbe_report_mmio_enabled, &status);
}
if (status == PCI_ERS_RESULT_NEED_RESET) {
/*
* TODO: Should call platform-specific
* functions to reset slot before calling
* drivers' slot_reset callbacks?
*/
status = PCI_ERS_RESULT_RECOVERED;
pci_dbg(dev, "broadcast slot_reset message\n");
pci_walk_bus(bus, ngbe_report_slot_reset, &status);
}
if (status != PCI_ERS_RESULT_RECOVERED)
goto failed;
pci_dbg(dev, "broadcast resume message\n");
pci_walk_bus(bus, ngbe_report_resume, &status);
failed:
return;
}

View File

@ -0,0 +1,6 @@
#ifndef _NGBE_PCIERR_H_
#define _NGBE_PCIERR_H_
void ngbe_pcie_do_recovery(struct pci_dev *dev);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,203 @@
/*
* WangXun Gigabit PCI Express Linux driver
* Copyright (c) 2015 - 2017 Beijing WangXun Technology Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* The full GNU General Public License is included in this distribution in
* the file called "COPYING".
*/
#ifndef _NGBE_PHY_H_
#define _NGBE_PHY_H_
#include "ngbe_type.h"
#include "ngbe.h"
/* EEPROM byte offsets */
#define NGBE_SFF_IDENTIFIER 0x0
#define NGBE_SFF_IDENTIFIER_SFP 0x3
#define NGBE_SFF_VENDOR_OUI_BYTE0 0x25
#define NGBE_SFF_VENDOR_OUI_BYTE1 0x26
#define NGBE_SFF_VENDOR_OUI_BYTE2 0x27
#define NGBE_SFF_1GBE_COMP_CODES 0x6
#define NGBE_SFF_10GBE_COMP_CODES 0x3
#define NGBE_SFF_CABLE_TECHNOLOGY 0x8
#define NGBE_SFF_CABLE_SPEC_COMP 0x3C
#define NGBE_SFF_SFF_8472_SWAP 0x5C
#define NGBE_SFF_SFF_8472_COMP 0x5E
#define NGBE_SFF_SFF_8472_OSCB 0x6E
#define NGBE_SFF_SFF_8472_ESCB 0x76
#define NGBE_SFF_IDENTIFIER_QSFP_PLUS 0xD
#define NGBE_SFF_QSFP_VENDOR_OUI_BYTE0 0xA5
#define NGBE_SFF_QSFP_VENDOR_OUI_BYTE1 0xA6
#define NGBE_SFF_QSFP_VENDOR_OUI_BYTE2 0xA7
#define NGBE_SFF_QSFP_CONNECTOR 0x82
#define NGBE_SFF_QSFP_10GBE_COMP 0x83
#define NGBE_SFF_QSFP_1GBE_COMP 0x86
#define NGBE_SFF_QSFP_CABLE_LENGTH 0x92
#define NGBE_SFF_QSFP_DEVICE_TECH 0x93
/* Bitmasks */
#define NGBE_SFF_DA_PASSIVE_CABLE 0x4
#define NGBE_SFF_DA_ACTIVE_CABLE 0x8
#define NGBE_SFF_DA_SPEC_ACTIVE_LIMITING 0x4
#define NGBE_SFF_1GBASESX_CAPABLE 0x1
#define NGBE_SFF_1GBASELX_CAPABLE 0x2
#define NGBE_SFF_1GBASET_CAPABLE 0x8
#define NGBE_SFF_10GBASESR_CAPABLE 0x10
#define NGBE_SFF_10GBASELR_CAPABLE 0x20
#define NGBE_SFF_SOFT_RS_SELECT_MASK 0x8
#define NGBE_SFF_SOFT_RS_SELECT_10G 0x8
#define NGBE_SFF_SOFT_RS_SELECT_1G 0x0
#define NGBE_SFF_ADDRESSING_MODE 0x4
#define NGBE_SFF_QSFP_DA_ACTIVE_CABLE 0x1
#define NGBE_SFF_QSFP_DA_PASSIVE_CABLE 0x8
#define NGBE_SFF_QSFP_CONNECTOR_NOT_SEPARABLE 0x23
#define NGBE_SFF_QSFP_TRANSMITER_850NM_VCSEL 0x0
#define NGBE_I2C_EEPROM_READ_MASK 0x100
#define NGBE_I2C_EEPROM_STATUS_MASK 0x3
#define NGBE_I2C_EEPROM_STATUS_NO_OPERATION 0x0
#define NGBE_I2C_EEPROM_STATUS_PASS 0x1
#define NGBE_I2C_EEPROM_STATUS_FAIL 0x2
#define NGBE_I2C_EEPROM_STATUS_IN_PROGRESS 0x3
#define NGBE_CS4227 0xBE /* CS4227 address */
#define NGBE_CS4227_GLOBAL_ID_LSB 0
#define NGBE_CS4227_SCRATCH 2
#define NGBE_CS4227_GLOBAL_ID_VALUE 0x03E5
#define NGBE_CS4227_SCRATCH_VALUE 0x5aa5
#define NGBE_CS4227_RETRIES 5
#define NGBE_CS4227_LINE_SPARE22_MSB 0x12AD /* Reg to program speed */
#define NGBE_CS4227_LINE_SPARE24_LSB 0x12B0 /* Reg to program EDC */
#define NGBE_CS4227_HOST_SPARE22_MSB 0x1AAD /* Reg to program speed */
#define NGBE_CS4227_HOST_SPARE24_LSB 0x1AB0 /* Reg to program EDC */
#define NGBE_CS4227_EDC_MODE_CX1 0x0002
#define NGBE_CS4227_EDC_MODE_SR 0x0004
#define NGBE_CS4227_RESET_HOLD 500 /* microseconds */
#define NGBE_CS4227_RESET_DELAY 500 /* milliseconds */
#define NGBE_CS4227_CHECK_DELAY 30 /* milliseconds */
#define NGBE_PE 0xE0 /* Port expander address */
#define NGBE_PE_OUTPUT 1 /* Output register offset */
#define NGBE_PE_CONFIG 3 /* Config register offset */
#define NGBE_PE_BIT1 (1 << 1)
/* Flow control defines */
#define NGBE_TAF_SYM_PAUSE (0x1)
#define NGBE_TAF_ASM_PAUSE (0x2)
/* Bit-shift macros */
#define NGBE_SFF_VENDOR_OUI_BYTE0_SHIFT 24
#define NGBE_SFF_VENDOR_OUI_BYTE1_SHIFT 16
#define NGBE_SFF_VENDOR_OUI_BYTE2_SHIFT 8
/* Vendor OUIs: format of OUI is 0x[byte0][byte1][byte2][00] */
#define NGBE_SFF_VENDOR_OUI_TYCO 0x00407600
#define NGBE_SFF_VENDOR_OUI_FTL 0x00906500
#define NGBE_SFF_VENDOR_OUI_AVAGO 0x00176A00
#define NGBE_SFF_VENDOR_OUI_INTEL 0x001B2100
/* I2C SDA and SCL timing parameters for standard mode */
#define NGBE_I2C_T_HD_STA 4
#define NGBE_I2C_T_LOW 5
#define NGBE_I2C_T_HIGH 4
#define NGBE_I2C_T_SU_STA 5
#define NGBE_I2C_T_HD_DATA 5
#define NGBE_I2C_T_SU_DATA 1
#define NGBE_I2C_T_RISE 1
#define NGBE_I2C_T_FALL 1
#define NGBE_I2C_T_SU_STO 4
#define NGBE_I2C_T_BUF 5
#ifndef NGBE_SFP_DETECT_RETRIES
#define NGBE_SFP_DETECT_RETRIES 10
#endif /* NGBE_SFP_DETECT_RETRIES */
/* SFP+ SFF-8472 Compliance */
#define NGBE_SFF_SFF_8472_UNSUP 0x00
bool ngbe_check_reset_blocked(struct ngbe_hw *hw);
enum ngbe_phy_type ngbe_get_phy_type_from_id(struct ngbe_hw *hw);
void ngbe_init_phy_ops_common(struct ngbe_hw *hw);
int ngbe_phy_read_reg_mdi( struct ngbe_hw *hw,
u32 reg_addr,
u32 device_type,
u16 *phy_data);
int ngbe_phy_write_reg_mdi( struct ngbe_hw *hw,
u32 reg_addr,
u32 device_type,
u16 phy_data);
int ngbe_phy_read_reg_sds_mii_yt8521s(struct ngbe_hw *hw,
u32 reg_addr,
u32 device_type,
u16 *phy_data);
int ngbe_phy_write_reg_sds_mii_yt8521s(struct ngbe_hw *hw,
u32 reg_addr,
u32 device_type,
u16 phy_data);
int ngbe_phy_read_reg_ext_yt8521s(struct ngbe_hw *hw,
u32 reg_addr,
u32 device_type,
u16 *phy_data);
int ngbe_phy_write_reg_ext_yt8521s(struct ngbe_hw *hw,
u32 reg_addr,
u32 device_type,
u16 phy_data);
int ngbe_phy_read_reg_sds_ext_yt8521s(struct ngbe_hw *hw,
u32 reg_addr,
u32 device_type,
u16 *phy_data);
int ngbe_phy_write_reg_sds_ext_yt8521s(struct ngbe_hw *hw,
u32 reg_addr,
u32 device_type,
u16 phy_data);
int ngbe_phy_init(struct ngbe_hw *hw);
int ngbe_phy_identify(struct ngbe_hw *hw);
int ngbe_phy_reset(struct ngbe_hw *hw);
u32 ngbe_phy_setup_link(struct ngbe_hw *hw,
u32 speed,
bool need_restart_AN);
u32 ngbe_phy_led_ctrl(struct ngbe_hw *hw);
int ngbe_phy_reset_m88e1512(struct ngbe_hw *hw);
u32 ngbe_phy_setup_link_m88e1512( struct ngbe_hw *hw,
u32 speed,
bool autoneg_wait_to_complete);
int ngbe_phy_check_overtemp(struct ngbe_hw *hw);
int ngbe_mv_suspend(struct ngbe_hw *hw);
int ngbe_yt_suspend(struct ngbe_hw *hw);
int ngbe_phy_check_event(struct ngbe_hw *hw);
int ngbe_phy_check_event_m88e1512(struct ngbe_hw *hw);
int ngbe_phy_set_pause_advertisement_m88e1512(struct ngbe_hw *hw,
u16 pause_bit);
int ngbe_phy_get_advertised_pause_m88e1512(struct ngbe_hw *hw, u8 *pause_bit);
int ngbe_phy_get_lp_advertised_pause_m88e1512(struct ngbe_hw *hw, u8 *pause_bit);
int ngbe_phy_check_event_yt8521s(struct ngbe_hw *hw);
int ngbe_phy_get_advertised_pause_yt8521s(struct ngbe_hw *hw, u8 *pause_bit);
int ngbe_phy_get_lp_advertised_pause_yt8521s(struct ngbe_hw *hw, u8 *pause_bit);
int ngbe_phy_reset_yt8521s(struct ngbe_hw *hw);
u32 ngbe_phy_setup_link_yt8521s( struct ngbe_hw *hw,
u32 speed,
bool autoneg_wait_to_complete);
int ngbe_phy_set_pause_advertisement_yt8521s(struct ngbe_hw *hw,
u16 pause_bit);
int ngbe_gphy_wait_mdio_access_on(struct ngbe_hw *hw);
int ngbe_gphy_efuse_calibration(struct ngbe_hw *hw);
int ngbe_gphy_dis_eee(struct ngbe_hw *hw);
#endif /* _NGBE_PHY_H_ */

View File

@ -0,0 +1,924 @@
/*
* WangXun Gigabit PCI Express Linux driver
* Copyright (c) 2015 - 2017 Beijing WangXun Technology Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* The full GNU General Public License is included in this distribution in
* the file called "COPYING".
*
*/
#include "ngbe.h"
#include "ngbe_hw.h"
#include "ngbe_type.h"
#ifdef NGBE_PROCFS
#ifndef NGBE_SYSFS
#include <linux/module.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/device.h>
#include <linux/netdevice.h>
static struct proc_dir_entry *ngbe_top_dir;
static struct net_device_stats *procfs_get_stats(struct net_device *netdev)
{
#ifndef HAVE_NETDEV_STATS_IN_NETDEV
struct ngbe_adapter *adapter;
#endif
if (netdev == NULL)
return NULL;
#ifdef HAVE_NETDEV_STATS_IN_NETDEV
/* only return the current stats */
return &netdev->stats;
#else
adapter = netdev_priv(netdev);
/* only return the current stats */
return &adapter->net_stats;
#endif /* HAVE_NETDEV_STATS_IN_NETDEV */
}
static int ngbe_fwbanner(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
return snprintf(page, count, "%s\n", adapter->eeprom_id);
}
static int ngbe_porttype(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
return snprintf(page, count, "%d\n",
test_bit(__NGBE_DOWN, &adapter->state));
}
static int ngbe_portspeed(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
int speed = 0;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
switch (adapter->link_speed) {
case NGBE_LINK_SPEED_100_FULL:
speed = 1;
break;
case NGBE_LINK_SPEED_1GB_FULL:
speed = 10;
break;
case NGBE_LINK_SPEED_10GB_FULL:
speed = 100;
break;
default:
break;
}
return snprintf(page, count, "%d\n", speed);
}
static int ngbe_wqlflag(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
return snprintf(page, count, "%d\n", adapter->wol);
}
static int ngbe_xflowctl(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
struct ngbe_hw *hw;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
hw = &adapter->hw;
if (hw == NULL)
return snprintf(page, count, "error: no hw data\n");
return snprintf(page, count, "%d\n", hw->fc.current_mode);
}
static int ngbe_rxdrops(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
struct net_device_stats *net_stats;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
net_stats = procfs_get_stats(adapter->netdev);
if (net_stats == NULL)
return snprintf(page, count, "error: no net stats\n");
return snprintf(page, count, "%lu\n",
net_stats->rx_dropped);
}
static int ngbe_rxerrors(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
struct net_device_stats *net_stats;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
net_stats = procfs_get_stats(adapter->netdev);
if (net_stats == NULL)
return snprintf(page, count, "error: no net stats\n");
return snprintf(page, count, "%lu\n", net_stats->rx_errors);
}
static int ngbe_rxupacks(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_hw *hw;
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
hw = &adapter->hw;
if (hw == NULL)
return snprintf(page, count, "error: no hw data\n");
return snprintf(page, count, "%d\n", rd32(hw, NGBE_TPR));
}
static int ngbe_rxmpacks(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_hw *hw;
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
int i, mprc = 0;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
hw = &adapter->hw;
if (hw == NULL)
return snprintf(page, count, "error: no hw data\n");
for (i = 0; i < 8; i++)
mprc += rd32(hw, NGBE_PX_MPRC(i));
return snprintf(page, count, "%d\n", mprc);
}
static int ngbe_rxbpacks(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_hw *hw;
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
hw = &adapter->hw;
if (hw == NULL)
return snprintf(page, count, "error: no hw data\n");
return snprintf(page, count, "%d\n",
rd32(hw, NGBE_RX_BC_FRAMES_GOOD_LOW));
}
static int ngbe_txupacks(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_hw *hw;
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
hw = &adapter->hw;
if (hw == NULL)
return snprintf(page, count, "error: no hw data\n");
return snprintf(page, count, "%d\n",
rd32(hw, NGBE_TX_FRAME_CNT_GOOD_BAD_LOW));
}
static int ngbe_txmpacks(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_hw *hw;
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
hw = &adapter->hw;
if (hw == NULL)
return snprintf(page, count, "error: no hw data\n");
return snprintf(page, count, "%d\n",
rd32(hw, NGBE_TX_MC_FRAMES_GOOD_LOW));
}
static int ngbe_txbpacks(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_hw *hw;
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
hw = &adapter->hw;
if (hw == NULL)
return snprintf(page, count, "error: no hw data\n");
return snprintf(page, count, "%d\n",
rd32(hw, NGBE_TX_BC_FRAMES_GOOD_LOW));
}
static int ngbe_txerrors(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
struct net_device_stats *net_stats;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
net_stats = procfs_get_stats(adapter->netdev);
if (net_stats == NULL)
return snprintf(page, count, "error: no net stats\n");
return snprintf(page, count, "%lu\n",
net_stats->tx_errors);
}
static int ngbe_txdrops(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
struct net_device_stats *net_stats;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
net_stats = procfs_get_stats(adapter->netdev);
if (net_stats == NULL)
return snprintf(page, count, "error: no net stats\n");
return snprintf(page, count, "%lu\n",
net_stats->tx_dropped);
}
static int ngbe_rxframes(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
struct net_device_stats *net_stats;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
net_stats = procfs_get_stats(adapter->netdev);
if (net_stats == NULL)
return snprintf(page, count, "error: no net stats\n");
return snprintf(page, count, "%lu\n",
net_stats->rx_packets);
}
static int ngbe_rxbytes(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
struct net_device_stats *net_stats;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
net_stats = procfs_get_stats(adapter->netdev);
if (net_stats == NULL)
return snprintf(page, count, "error: no net stats\n");
return snprintf(page, count, "%lu\n",
net_stats->rx_bytes);
}
static int ngbe_txframes(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
struct net_device_stats *net_stats;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
net_stats = procfs_get_stats(adapter->netdev);
if (net_stats == NULL)
return snprintf(page, count, "error: no net stats\n");
return snprintf(page, count, "%lu\n",
net_stats->tx_packets);
}
static int ngbe_txbytes(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
struct net_device_stats *net_stats;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
net_stats = procfs_get_stats(adapter->netdev);
if (net_stats == NULL)
return snprintf(page, count, "error: no net stats\n");
return snprintf(page, count, "%lu\n",
net_stats->tx_bytes);
}
static int ngbe_linkstat(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_hw *hw;
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
int bitmask = 0;
u32 link_speed;
bool link_up = false;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
hw = &adapter->hw;
if (hw == NULL)
return snprintf(page, count, "error: no hw data\n");
if (!test_bit(__NGBE_DOWN, &adapter->state))
bitmask |= 1;
/* always assume link is up, if no check link function */
link_up = true;
if (link_up)
bitmask |= 2;
if (adapter->old_lsc != adapter->lsc_int) {
bitmask |= 4;
adapter->old_lsc = adapter->lsc_int;
}
return snprintf(page, count, "0x%X\n", bitmask);
}
static int ngbe_funcid(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
struct ngbe_hw *hw;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
hw = &adapter->hw;
if (hw == NULL)
return snprintf(page, count, "error: no hw data\n");
return snprintf(page, count, "0x%X\n", hw->bus.func);
}
static int ngbe_funcvers(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void __always_unused *data)
{
return snprintf(page, count, "%s\n", ngbe_driver_version);
}
static int ngbe_macburn(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_hw *hw;
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
hw = &adapter->hw;
if (hw == NULL)
return snprintf(page, count, "error: no hw data\n");
return snprintf(page, count, "0x%02X%02X%02X%02X%02X%02X\n",
(unsigned int)hw->mac.perm_addr[0],
(unsigned int)hw->mac.perm_addr[1],
(unsigned int)hw->mac.perm_addr[2],
(unsigned int)hw->mac.perm_addr[3],
(unsigned int)hw->mac.perm_addr[4],
(unsigned int)hw->mac.perm_addr[5]);
}
static int ngbe_macadmn(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_hw *hw;
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
hw = &adapter->hw;
if (hw == NULL)
return snprintf(page, count, "error: no hw data\n");
return snprintf(page, count, "0x%02X%02X%02X%02X%02X%02X\n",
(unsigned int)hw->mac.addr[0],
(unsigned int)hw->mac.addr[1],
(unsigned int)hw->mac.addr[2],
(unsigned int)hw->mac.addr[3],
(unsigned int)hw->mac.addr[4],
(unsigned int)hw->mac.addr[5]);
}
static int ngbe_maclla1(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
struct ngbe_hw *hw;
int rc;
u16 eeprom_buff[6];
u16 first_word = 0x37;
const u16 word_count = ARRAY_SIZE(eeprom_buff);
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
hw = &adapter->hw;
if (hw == NULL)
return snprintf(page, count, "error: no hw data\n");
rc = hw->eeprom.ops.read_buffer(hw, first_word, 1, &first_word);
if (rc != 0)
return snprintf(page, count,
"error: reading pointer to the EEPROM\n");
if (first_word != 0x0000 && first_word != 0xFFFF) {
rc = hw->eeprom.ops.read_buffer(hw, first_word, word_count,
eeprom_buff);
if (rc != 0)
return snprintf(page, count, "error: reading buffer\n");
} else {
memset(eeprom_buff, 0, sizeof(eeprom_buff));
}
switch (hw->bus.func) {
case 0:
return snprintf(page, count, "0x%04X%04X%04X\n",
eeprom_buff[0],
eeprom_buff[1],
eeprom_buff[2]);
case 1:
return snprintf(page, count, "0x%04X%04X%04X\n",
eeprom_buff[3],
eeprom_buff[4],
eeprom_buff[5]);
default:
return snprintf(page, count, "unexpected port %d\n", hw->bus.func);
}
}
static int ngbe_mtusize(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
struct net_device *netdev;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
netdev = adapter->netdev;
if (netdev == NULL)
return snprintf(page, count, "error: no net device\n");
return snprintf(page, count, "%d\n", netdev->mtu);
}
static int ngbe_featflag(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
int bitmask = 0;
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
struct net_device *netdev;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
netdev = adapter->netdev;
if (netdev == NULL)
return snprintf(page, count, "error: no net device\n");
if (adapter->netdev->features & NETIF_F_RXCSUM)
bitmask |= 1;
return snprintf(page, count, "%d\n", bitmask);
}
static int ngbe_lsominct(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void __always_unused *data)
{
return snprintf(page, count, "%d\n", 1);
}
static int ngbe_prommode(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
struct net_device *netdev;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
netdev = adapter->netdev;
if (netdev == NULL)
return snprintf(page, count, "error: no net device\n");
return snprintf(page, count, "%d\n",
netdev->flags & IFF_PROMISC);
}
static int ngbe_txdscqsz(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
return snprintf(page, count, "%d\n", adapter->tx_ring[0]->count);
}
static int ngbe_rxdscqsz(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
return snprintf(page, count, "%d\n", adapter->rx_ring[0]->count);
}
static int ngbe_rxqavg(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
int index;
int diff = 0;
u16 ntc;
u16 ntu;
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
for (index = 0; index < adapter->num_rx_queues; index++) {
ntc = adapter->rx_ring[index]->next_to_clean;
ntu = adapter->rx_ring[index]->next_to_use;
if (ntc >= ntu)
diff += (ntc - ntu);
else
diff += (adapter->rx_ring[index]->count - ntu + ntc);
}
if (adapter->num_rx_queues <= 0)
return snprintf(page, count,
"can't calculate, number of queues %d\n",
adapter->num_rx_queues);
return snprintf(page, count, "%d\n", diff/adapter->num_rx_queues);
}
static int ngbe_txqavg(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
int index;
int diff = 0;
u16 ntc;
u16 ntu;
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
for (index = 0; index < adapter->num_tx_queues; index++) {
ntc = adapter->tx_ring[index]->next_to_clean;
ntu = adapter->tx_ring[index]->next_to_use;
if (ntc >= ntu)
diff += (ntc - ntu);
else
diff += (adapter->tx_ring[index]->count - ntu + ntc);
}
if (adapter->num_tx_queues <= 0)
return snprintf(page, count,
"can't calculate, number of queues %d\n",
adapter->num_tx_queues);
return snprintf(page, count, "%d\n",
diff/adapter->num_tx_queues);
}
static int ngbe_iovotype(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void __always_unused *data)
{
return snprintf(page, count, "2\n");
}
static int ngbe_funcnbr(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
return snprintf(page, count, "%d\n", adapter->num_vfs);
}
static int ngbe_pciebnbr(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_adapter *adapter = (struct ngbe_adapter *)data;
if (adapter == NULL)
return snprintf(page, count, "error: no adapter\n");
return snprintf(page, count, "%d\n", adapter->pdev->bus->number);
}
static int ngbe_therm_dealarmthresh(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_therm_proc_data *therm_data =
(struct ngbe_therm_proc_data *)data;
if (therm_data == NULL)
return snprintf(page, count, "error: no therm_data\n");
return snprintf(page, count, "%d\n",
therm_data->sensor_data->dalarm_thresh);
}
static int ngbe_therm_alarmthresh(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
struct ngbe_therm_proc_data *therm_data =
(struct ngbe_therm_proc_data *)data;
if (therm_data == NULL)
return snprintf(page, count, "error: no therm_data\n");
return snprintf(page, count, "%d\n",
therm_data->sensor_data->alarm_thresh);
}
static int ngbe_therm_temp(char *page, char __always_unused **start,
off_t __always_unused off, int count,
int __always_unused *eof, void *data)
{
int status;
struct ngbe_therm_proc_data *therm_data =
(struct ngbe_therm_proc_data *)data;
if (therm_data == NULL)
return snprintf(page, count, "error: no therm_data\n");
status = ngbe_get_thermal_sensor_data(therm_data->hw);
if (status != 0)
snprintf(page, count, "error: status %d returned\n", status);
return snprintf(page, count, "%d\n", therm_data->sensor_data->temp);
}
struct ngbe_proc_type {
char name[32];
int (*read)(char*, char**, off_t, int, int*, void*);
};
struct ngbe_proc_type ngbe_proc_entries[] = {
{"fwbanner", &ngbe_fwbanner},
{"porttype", &ngbe_porttype},
{"portspeed", &ngbe_portspeed},
{"wqlflag", &ngbe_wqlflag},
{"xflowctl", &ngbe_xflowctl},
{"rxdrops", &ngbe_rxdrops},
{"rxerrors", &ngbe_rxerrors},
{"rxupacks", &ngbe_rxupacks},
{"rxmpacks", &ngbe_rxmpacks},
{"rxbpacks", &ngbe_rxbpacks},
{"txdrops", &ngbe_txdrops},
{"txerrors", &ngbe_txerrors},
{"txupacks", &ngbe_txupacks},
{"txmpacks", &ngbe_txmpacks},
{"txbpacks", &ngbe_txbpacks},
{"rxframes", &ngbe_rxframes},
{"rxbytes", &ngbe_rxbytes},
{"txframes", &ngbe_txframes},
{"txbytes", &ngbe_txbytes},
{"linkstat", &ngbe_linkstat},
{"funcid", &ngbe_funcid},
{"funcvers", &ngbe_funcvers},
{"macburn", &ngbe_macburn},
{"macadmn", &ngbe_macadmn},
{"maclla1", &ngbe_maclla1},
{"mtusize", &ngbe_mtusize},
{"featflag", &ngbe_featflag},
{"lsominct", &ngbe_lsominct},
{"prommode", &ngbe_prommode},
{"txdscqsz", &ngbe_txdscqsz},
{"rxdscqsz", &ngbe_rxdscqsz},
{"txqavg", &ngbe_txqavg},
{"rxqavg", &ngbe_rxqavg},
{"iovotype", &ngbe_iovotype},
{"funcnbr", &ngbe_funcnbr},
{"pciebnbr", &ngbe_pciebnbr},
{"", NULL}
};
struct ngbe_proc_type ngbe_internal_entries[] = {
{"temp", &ngbe_therm_temp},
{"alarmthresh", &ngbe_therm_alarmthresh},
{"dealarmthresh", &ngbe_therm_dealarmthresh},
{"", NULL}
};
void ngbe_del_proc_entries(struct ngbe_adapter *adapter)
{
int index;
int i;
char buf[16]; /* much larger than the sensor number will ever be */
if (ngbe_top_dir == NULL)
return;
for (i = 0; i < NGBE_MAX_SENSORS; i++) {
if (adapter->therm_dir[i] == NULL)
continue;
for (index = 0; ; index++) {
if (ngbe_internal_entries[index].read == NULL)
break;
remove_proc_entry(ngbe_internal_entries[index].name,
adapter->therm_dir[i]);
}
snprintf(buf, sizeof(buf), "sensor_%d", i);
remove_proc_entry(buf, adapter->info_dir);
}
if (adapter->info_dir != NULL) {
for (index = 0; ; index++) {
if (ngbe_proc_entries[index].read == NULL)
break;
remove_proc_entry(ngbe_proc_entries[index].name,
adapter->info_dir);
}
remove_proc_entry("info", adapter->eth_dir);
}
if (adapter->eth_dir != NULL)
remove_proc_entry(pci_name(adapter->pdev), ngbe_top_dir);
}
/* called from ngbe_main.c */
void ngbe_procfs_exit(struct ngbe_adapter *adapter)
{
ngbe_del_proc_entries(adapter);
}
int ngbe_procfs_topdir_init(void)
{
ngbe_top_dir = proc_mkdir("driver/ngbe", NULL);
if (ngbe_top_dir == NULL)
return -ENOMEM;
return 0;
}
void ngbe_procfs_topdir_exit(void)
{
remove_proc_entry("driver/ngbe", NULL);
}
/* called from ngbe_main.c */
int ngbe_procfs_init(struct ngbe_adapter *adapter)
{
int rc = 0;
int index;
int i;
char buf[16]; /* much larger than the sensor number will ever be */
adapter->eth_dir = NULL;
adapter->info_dir = NULL;
adapter->therm_dir = NULL;
if (ngbe_top_dir == NULL) {
rc = -ENOMEM;
goto fail;
}
adapter->eth_dir = proc_mkdir(pci_name(adapter->pdev), ngbe_top_dir);
if (adapter->eth_dir == NULL) {
rc = -ENOMEM;
goto fail;
}
adapter->info_dir = proc_mkdir("info", adapter->eth_dir);
if (adapter->info_dir == NULL) {
rc = -ENOMEM;
goto fail;
}
for (index = 0; ; index++) {
if (ngbe_proc_entries[index].read == NULL)
break;
if (!(create_proc_read_entry(ngbe_proc_entries[index].name,
0444,
adapter->info_dir,
ngbe_proc_entries[index].read,
adapter))) {
rc = -ENOMEM;
goto fail;
}
}
if (!adapter->hw->ops.init_thermal_sensor_thresh(hw))
goto exit;
snprintf(buf, sizeof(buf), "sensor");
adapter->therm_dir = proc_mkdir(buf, adapter->info_dir);
if (adapter->therm_dir == NULL) {
rc = -ENOMEM;
goto fail;
}
for (index = 0; ; index++) {
if (ngbe_internal_entries[index].read == NULL)
break;
/*
* therm_data struct contains pointer the read func
* will be needing
*/
adapter->therm_data.hw = &adapter->hw;
adapter->therm_data.sensor_data =
&adapter->hw.mac.thermal_sensor_data.sensor;
if (!(create_proc_read_entry(
ngbe_internal_entries[index].name,
0444,
adapter->therm_dir,
ngbe_internal_entries[index].read,
&adapter->therm_data))) {
rc = -ENOMEM;
goto fail;
}
}
goto exit;
fail:
ngbe_del_proc_entries(adapter);
exit:
return rc;
}
#endif /* !NGBE_SYSFS */
#endif /* NGBE_PROCFS */

View File

@ -0,0 +1,887 @@
/*
* WangXun Gigabit PCI Express Linux driver
* Copyright (c) 2015 - 2017 Beijing WangXun Technology Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* The full GNU General Public License is included in this distribution in
* the file called "COPYING".
*/
#include "ngbe.h"
#include <linux/ptp_classify.h>
/*
* SYSTIME is defined by a fixed point system which allows the user to
* define the scale counter increment value at every level change of
* the oscillator driving SYSTIME value. The time unit is determined by
* the clock frequency of the oscillator and TIMINCA register.
* The cyclecounter and timecounter structures are used to to convert
* the scale counter into nanoseconds. SYSTIME registers need to be converted
* to ns values by use of only a right shift.
* The following math determines the largest incvalue that will fit into
* the available bits in the TIMINCA register:
* Period * [ 2 ^ ( MaxWidth - PeriodWidth ) ]
* PeriodWidth: Number of bits to store the clock period
* MaxWidth: The maximum width value of the TIMINCA register
* Period: The clock period for the oscillator, which changes based on the link
* speed:
* At 10Gb link or no link, the period is 6.4 ns.
* At 1Gb link, the period is multiplied by 10. (64ns)
* At 100Mb link, the period is multiplied by 100. (640ns)
* round(): discard the fractional portion of the calculation
*
* The calculated value allows us to right shift the SYSTIME register
* value in order to quickly convert it into a nanosecond clock,
* while allowing for the maximum possible adjustment value.
*
* LinkSpeed ClockFreq ClockPeriod TIMINCA:IV
* 10000Mbps 156.25MHz 6.4*10^-9 0xCCCCCC(0xFFFFF/ns)
* 1000 Mbps 62.5 MHz 16 *10^-9 0x800000(0x7FFFF/ns)
* 100 Mbps 6.25 MHz 160*10^-9 0xA00000(0xFFFF/ns)
* 10 Mbps 0.625 MHz 1600*10^-9 0xC7F380(0xFFF/ns)
* FPGA 31.25 MHz 32 *10^-9 0x800000(0x3FFFF/ns)
*
* These diagrams are only for the 10Gb link period
*
* +--------------+ +--------------+
* | 32 | | 8 | 3 | 20 |
* *--------------+ +--------------+
* \________ 43 bits ______/ fract
*
* The 43 bit SYSTIME overflows every
* 2^43 * 10^-9 / 3600 = 2.4 hours
*/
#define NGBE_INCVAL_10GB 0xCCCCCC
#define NGBE_INCVAL_1GB 0x2000000/*in Emerald all speed is same*/
#define NGBE_INCVAL_100 0xA00000
#define NGBE_INCVAL_10 0xC7F380
#define NGBE_INCVAL_FPGA 0x800000
#define NGBE_INCVAL_SHIFT_10GB 20
#define NGBE_INCVAL_SHIFT_1GB 22/*in Emerald all speed is same*/
#define NGBE_INCVAL_SHIFT_100 15
#define NGBE_INCVAL_SHIFT_10 12
#define NGBE_INCVAL_SHIFT_FPGA 17
#define NGBE_OVERFLOW_PERIOD (HZ * 30)
#define NGBE_PTP_TX_TIMEOUT (HZ)
/**
* ngbe_ptp_read - read raw cycle counter (to be used by time counter)
* @hw_cc: the cyclecounter structure
*
* this function reads the cyclecounter registers and is called by the
* cyclecounter structure used to construct a ns counter from the
* arbitrary fixed point registers
*/
static u64 ngbe_ptp_read(const struct cyclecounter *hw_cc)
{
struct ngbe_adapter *adapter =
container_of(hw_cc, struct ngbe_adapter, hw_cc);
struct ngbe_hw *hw = &adapter->hw;
u64 stamp = 0;
stamp |= (u64)rd32(hw, NGBE_TSEC_1588_SYSTIML);
stamp |= (u64)rd32(hw, NGBE_TSEC_1588_SYSTIMH) << 32;
return stamp;
}
/**
* ngbe_ptp_convert_to_hwtstamp - convert register value to hw timestamp
* @adapter: private adapter structure
* @hwtstamp: stack timestamp structure
* @systim: unsigned 64bit system time value
*
* We need to convert the adapter's RX/TXSTMP registers into a hwtstamp value
* which can be used by the stack's ptp functions.
*
* The lock is used to protect consistency of the cyclecounter and the SYSTIME
* registers. However, it does not need to protect against the Rx or Tx
* timestamp registers, as there can't be a new timestamp until the old one is
* unlatched by reading.
*
* In addition to the timestamp in hardware, some controllers need a software
* overflow cyclecounter, and this function takes this into account as well.
**/
static void ngbe_ptp_convert_to_hwtstamp(struct ngbe_adapter *adapter,
struct skb_shared_hwtstamps *hwtstamp,
u64 timestamp)
{
unsigned long flags;
u64 ns;
memset(hwtstamp, 0, sizeof(*hwtstamp));
spin_lock_irqsave(&adapter->tmreg_lock, flags);
ns = timecounter_cyc2time(&adapter->hw_tc, timestamp);
spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
hwtstamp->hwtstamp = ns_to_ktime(ns);
}
/**
* ngbe_ptp_adjfreq
* @ptp: the ptp clock structure
* @ppb: parts per billion adjustment from base
*
* adjust the frequency of the ptp cycle counter by the
* indicated ppb from the base frequency.
*/
#ifndef HAVE_NOT_PTT_ADJFREQ
static int ngbe_ptp_adjfreq(struct ptp_clock_info *ptp, int ppb)
{
struct ngbe_adapter *adapter =
container_of(ptp, struct ngbe_adapter, ptp_caps);
struct ngbe_hw *hw = &adapter->hw;
u64 freq, incval;
u32 diff;
int neg_adj = 0;
if (ppb < 0) {
neg_adj = 1;
ppb = -ppb;
}
smp_mb();
incval = READ_ONCE(adapter->base_incval);
freq = incval;
freq *= ppb;
diff = div_u64(freq, 1000000000ULL);
incval = neg_adj ? (incval - diff) : (incval + diff);
/* temp setting*/
if (incval > NGBE_TSEC_1588_INC_IV(~0))
e_dev_warn("PTP ppb adjusted SYSTIME rate overflowed!\n");
wr32(hw, NGBE_TSEC_1588_INC, NGBE_TSEC_1588_INC_IV(incval));
return 0;
}
#endif
/**
* ngbe_ptp_adjtime
* @ptp: the ptp clock structure
* @delta: offset to adjust the cycle counter by ns
*
* adjust the timer by resetting the timecounter structure.
*/
static int ngbe_ptp_adjtime(struct ptp_clock_info *ptp,
s64 delta)
{
struct ngbe_adapter *adapter =
container_of(ptp, struct ngbe_adapter, ptp_caps);
unsigned long flags;
spin_lock_irqsave(&adapter->tmreg_lock, flags);
timecounter_adjtime(&adapter->hw_tc, delta);
spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
return 0;
}
/**
* ngbe_ptp_gettime64
* @ptp: the ptp clock structure
* @ts: timespec64 structure to hold the current time value
*
* read the timecounter and return the correct value on ns,
* after converting it into a struct timespec64.
*/
static int ngbe_ptp_gettime64(struct ptp_clock_info *ptp,
struct timespec64 *ts)
{
struct ngbe_adapter *adapter =
container_of(ptp, struct ngbe_adapter, ptp_caps);
unsigned long flags;
u64 ns;
spin_lock_irqsave(&adapter->tmreg_lock, flags);
ns = timecounter_read(&adapter->hw_tc);
spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
*ts = ns_to_timespec64(ns);
return 0;
}
/**
* ngbe_ptp_settime64
* @ptp: the ptp clock structure
* @ts: the timespec64 containing the new time for the cycle counter
*
* reset the timecounter to use a new base value instead of the kernel
* wall timer value.
*/
static int ngbe_ptp_settime64(struct ptp_clock_info *ptp,
const struct timespec64 *ts)
{
struct ngbe_adapter *adapter =
container_of(ptp, struct ngbe_adapter, ptp_caps);
u64 ns;
unsigned long flags;
ns = timespec64_to_ns(ts);
/* reset the timecounter */
spin_lock_irqsave(&adapter->tmreg_lock, flags);
timecounter_init(&adapter->hw_tc, &adapter->hw_cc, ns);
spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
return 0;
}
#ifndef HAVE_PTP_CLOCK_INFO_GETTIME64
static int ngbe_ptp_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
{
struct timespec64 ts64;
int err;
err = ngbe_ptp_gettime64(ptp, &ts64);
if (err)
return err;
*ts = timespec64_to_timespec(ts64);
return 0;
}
static int ngbe_ptp_settime(struct ptp_clock_info *ptp,
const struct timespec *ts)
{
struct timespec64 ts64;
ts64 = timespec_to_timespec64(*ts);
return ngbe_ptp_settime64(ptp, &ts64);
}
#endif
/**
* ngbe_ptp_feature_enable
* @ptp: the ptp clock structure
* @rq: the requested feature to change
* @on: whether to enable or disable the feature
*
* enable (or disable) ancillary features of the phc subsystem.
* our driver only supports the PPS feature on the X540
*/
static int ngbe_ptp_feature_enable(struct ptp_clock_info *ptp,
struct ptp_clock_request *rq, int on)
{
return -ENOTSUPP;
}
/**
* ngbe_ptp_check_pps_event
* @adapter: the private adapter structure
* @eicr: the interrupt cause register value
*
* This function is called by the interrupt routine when checking for
* interrupts. It will check and handle a pps event.
*/
void ngbe_ptp_check_pps_event(struct ngbe_adapter *adapter)
{
/* this check is necessary in case the interrupt was enabled via some
* alternative means (ex. debug_fs). Better to check here than
* everywhere that calls this function.
*/
if (!adapter->ptp_clock)
return;
}
/**
* ngbe_ptp_overflow_check - watchdog task to detect SYSTIME overflow
* @adapter: private adapter struct
*
* this watchdog task periodically reads the timecounter
* in order to prevent missing when the system time registers wrap
* around. This needs to be run approximately twice a minute for the fastest
* overflowing hardware. We run it for all hardware since it shouldn't have a
* large impact.
*/
void ngbe_ptp_overflow_check(struct ngbe_adapter *adapter)
{
bool timeout = time_is_before_jiffies(adapter->last_overflow_check +
NGBE_OVERFLOW_PERIOD);
struct timespec64 ts;
if (timeout) {
ngbe_ptp_gettime64(&adapter->ptp_caps, &ts);
adapter->last_overflow_check = jiffies;
}
}
/**
* ngbe_ptp_rx_hang - detect error case when Rx timestamp registers latched
* @adapter: private network adapter structure
*
* this watchdog task is scheduled to detect error case where hardware has
* dropped an Rx packet that was timestamped when the ring is full. The
* particular error is rare but leaves the device in a state unable to timestamp
* any future packets.
*/
void ngbe_ptp_rx_hang(struct ngbe_adapter *adapter)
{
struct ngbe_hw *hw = &adapter->hw;
struct ngbe_ring *rx_ring;
u32 tsyncrxctl = rd32(hw, NGBE_PSR_1588_CTL);
unsigned long rx_event;
int n;
/* if we don't have a valid timestamp in the registers, just update the
* timeout counter and exit
*/
if (!(tsyncrxctl & NGBE_PSR_1588_CTL_VALID)) {
adapter->last_rx_ptp_check = jiffies;
return;
}
/* determine the most recent watchdog or rx_timestamp event */
rx_event = adapter->last_rx_ptp_check;
for (n = 0; n < adapter->num_rx_queues; n++) {
rx_ring = adapter->rx_ring[n];
if (time_after(rx_ring->last_rx_timestamp, rx_event))
rx_event = rx_ring->last_rx_timestamp;
}
/* only need to read the high RXSTMP register to clear the lock */
if (time_is_before_jiffies(rx_event + 5 * HZ)) {
rd32(hw, NGBE_PSR_1588_STMPH);
adapter->last_rx_ptp_check = jiffies;
adapter->rx_hwtstamp_cleared++;
e_warn(drv, "clearing RX Timestamp hang");
}
}
/**
* ngbe_ptp_clear_tx_timestamp - utility function to clear Tx timestamp state
* @adapter: the private adapter structure
*
* This function should be called whenever the state related to a Tx timestamp
* needs to be cleared. This helps ensure that all related bits are reset for
* the next Tx timestamp event.
*/
static void ngbe_ptp_clear_tx_timestamp(struct ngbe_adapter *adapter)
{
struct ngbe_hw *hw = &adapter->hw;
rd32(hw, NGBE_TSEC_1588_STMPH);
if (adapter->ptp_tx_skb) {
dev_kfree_skb_any(adapter->ptp_tx_skb);
adapter->ptp_tx_skb = NULL;
}
clear_bit_unlock(__NGBE_PTP_TX_IN_PROGRESS, &adapter->state);
}
/**
* ngbe_ptp_tx_hwtstamp - utility function which checks for TX time stamp
* @adapter: the private adapter struct
*
* if the timestamp is valid, we convert it into the timecounter ns
* value, then store that result into the shhwtstamps structure which
* is passed up the network stack
*/
static void ngbe_ptp_tx_hwtstamp(struct ngbe_adapter *adapter)
{
struct ngbe_hw *hw = &adapter->hw;
struct skb_shared_hwtstamps shhwtstamps;
u64 regval = 0;
regval |= (u64)rd32(hw, NGBE_TSEC_1588_STMPL);
regval |= (u64)rd32(hw, NGBE_TSEC_1588_STMPH) << 32;
ngbe_ptp_convert_to_hwtstamp(adapter, &shhwtstamps, regval);
skb_tstamp_tx(adapter->ptp_tx_skb, &shhwtstamps);
ngbe_ptp_clear_tx_timestamp(adapter);
}
/**
* ngbe_ptp_tx_hwtstamp_work
* @work: pointer to the work struct
*
* This work item polls TSYNCTXCTL valid bit to determine when a Tx hardware
* timestamp has been taken for the current skb. It is necesary, because the
* descriptor's "done" bit does not correlate with the timestamp event.
*/
static void ngbe_ptp_tx_hwtstamp_work(struct work_struct *work)
{
struct ngbe_adapter *adapter = container_of(work, struct ngbe_adapter,
ptp_tx_work);
struct ngbe_hw *hw = &adapter->hw;
bool timeout = time_is_before_jiffies(adapter->ptp_tx_start +
NGBE_PTP_TX_TIMEOUT);
u32 tsynctxctl;
/* we have to have a valid skb to poll for a timestamp */
if (!adapter->ptp_tx_skb) {
ngbe_ptp_clear_tx_timestamp(adapter);
return;
}
/* stop polling once we have a valid timestamp */
tsynctxctl = rd32(hw, NGBE_TSEC_1588_CTL);
if (tsynctxctl & NGBE_TSEC_1588_CTL_VALID) {
ngbe_ptp_tx_hwtstamp(adapter);
return;
}
/* check timeout last in case timestamp event just occurred */
if (timeout) {
ngbe_ptp_clear_tx_timestamp(adapter);
adapter->tx_hwtstamp_timeouts++;
e_warn(drv, "clearing Tx Timestamp hang");
} else {
/* reschedule to keep checking until we timeout */
schedule_work(&adapter->ptp_tx_work);
}
}
/**
* ngbe_ptp_rx_rgtstamp - utility function which checks for RX time stamp
* @q_vector: structure containing interrupt and ring information
* @skb: particular skb to send timestamp with
*
* if the timestamp is valid, we convert it into the timecounter ns
* value, then store that result into the shhwtstamps structure which
* is passed up the network stack
*/
void ngbe_ptp_rx_hwtstamp(struct ngbe_adapter *adapter, struct sk_buff *skb)
{
struct ngbe_hw *hw = &adapter->hw;
u64 regval = 0;
u32 tsyncrxctl;
/*
* Read the tsyncrxctl register afterwards in order to prevent taking an
* I/O hit on every packet.
*/
tsyncrxctl = rd32(hw, NGBE_PSR_1588_CTL);
if (!(tsyncrxctl & NGBE_PSR_1588_CTL_VALID))
return;
regval |= (u64)rd32(hw, NGBE_PSR_1588_STMPL);
regval |= (u64)rd32(hw, NGBE_PSR_1588_STMPH) << 32;
ngbe_ptp_convert_to_hwtstamp(adapter, skb_hwtstamps(skb), regval);
}
/**
* ngbe_ptp_get_ts_config - get current hardware timestamping configuration
* @adapter: pointer to adapter structure
* @ifreq: ioctl data
*
* This function returns the current timestamping settings. Rather than
* attempt to deconstruct registers to fill in the values, simply keep a copy
* of the old settings around, and return a copy when requested.
*/
int ngbe_ptp_get_ts_config(struct ngbe_adapter *adapter, struct ifreq *ifr)
{
struct hwtstamp_config *config = &adapter->tstamp_config;
return copy_to_user(ifr->ifr_data, config,
sizeof(*config)) ? -EFAULT : 0;
}
/**
* ngbe_ptp_set_timestamp_mode - setup the hardware for the requested mode
* @adapter: the private ngbe adapter structure
* @config: the hwtstamp configuration requested
*
* Outgoing time stamping can be enabled and disabled. Play nice and
* disable it when requested, although it shouldn't cause any overhead
* when no packet needs it. At most one packet in the queue may be
* marked for time stamping, otherwise it would be impossible to tell
* for sure to which packet the hardware time stamp belongs.
*
* Incoming time stamping has to be configured via the hardware
* filters. Not all combinations are supported, in particular event
* type has to be specified. Matching the kind of event packet is
* not supported, with the exception of "all V2 events regardless of
* level 2 or 4".
*
* Since hardware always timestamps Path delay packets when timestamping V2
* packets, regardless of the type specified in the register, only use V2
* Event mode. This more accurately tells the user what the hardware is going
* to do anyways.
*
* Note: this may modify the hwtstamp configuration towards a more general
* mode, if required to support the specifically requested mode.
*/
static int ngbe_ptp_set_timestamp_mode(struct ngbe_adapter *adapter,
struct hwtstamp_config *config)
{
struct ngbe_hw *hw = &adapter->hw;
u32 tsync_tx_ctl = NGBE_TSEC_1588_CTL_ENABLED;
u32 tsync_rx_ctl = NGBE_PSR_1588_CTL_ENABLED;
u32 tsync_rx_mtrl = PTP_EV_PORT << 16;
bool is_l2 = false;
u32 regval;
/* reserved for future extensions */
if (config->flags)
return -EINVAL;
switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
tsync_tx_ctl = 0;
case HWTSTAMP_TX_ON:
break;
default:
return -ERANGE;
}
switch (config->rx_filter) {
case HWTSTAMP_FILTER_NONE:
tsync_rx_ctl = 0;
tsync_rx_mtrl = 0;
adapter->flags &= ~(NGBE_FLAG_RX_HWTSTAMP_ENABLED |
NGBE_FLAG_RX_HWTSTAMP_IN_REGISTER);
break;
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
tsync_rx_ctl |= NGBE_PSR_1588_CTL_TYPE_L4_V1;
tsync_rx_mtrl |= NGBE_PSR_1588_MSGTYPE_V1_SYNC_MSG;
adapter->flags |= (NGBE_FLAG_RX_HWTSTAMP_ENABLED |
NGBE_FLAG_RX_HWTSTAMP_IN_REGISTER);
break;
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
tsync_rx_ctl |= NGBE_PSR_1588_CTL_TYPE_L4_V1;
tsync_rx_mtrl |= NGBE_PSR_1588_MSGTYPE_V1_DELAY_REQ_MSG;
adapter->flags |= (NGBE_FLAG_RX_HWTSTAMP_ENABLED |
NGBE_FLAG_RX_HWTSTAMP_IN_REGISTER);
break;
case HWTSTAMP_FILTER_PTP_V2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
tsync_rx_ctl |= NGBE_PSR_1588_CTL_TYPE_EVENT_V2;
is_l2 = true;
config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
adapter->flags |= (NGBE_FLAG_RX_HWTSTAMP_ENABLED |
NGBE_FLAG_RX_HWTSTAMP_IN_REGISTER);
break;
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
case HWTSTAMP_FILTER_ALL:
default:
/* register RXMTRL must be set in order to do V1 packets,
* therefore it is not possible to time stamp both V1 Sync and
* Delay_Req messages unless hardware supports timestamping all
* packets => return error
*/
adapter->flags &= ~(NGBE_FLAG_RX_HWTSTAMP_ENABLED |
NGBE_FLAG_RX_HWTSTAMP_IN_REGISTER);
config->rx_filter = HWTSTAMP_FILTER_NONE;
return -ERANGE;
}
/* define ethertype filter for timestamping L2 packets */
if (is_l2)
wr32(hw,
NGBE_PSR_ETYPE_SWC(NGBE_PSR_ETYPE_SWC_FILTER_1588),
(NGBE_PSR_ETYPE_SWC_FILTER_EN | /* enable filter */
NGBE_PSR_ETYPE_SWC_1588 | /* enable timestamping */
ETH_P_1588)); /* 1588 eth protocol type */
else
wr32(hw,
NGBE_PSR_ETYPE_SWC(NGBE_PSR_ETYPE_SWC_FILTER_1588),
0);
/* enable/disable TX */
regval = rd32(hw, NGBE_TSEC_1588_CTL);
regval &= ~NGBE_TSEC_1588_CTL_ENABLED;
regval |= tsync_tx_ctl;
wr32(hw, NGBE_TSEC_1588_CTL, regval);
/* enable/disable RX */
regval = rd32(hw, NGBE_PSR_1588_CTL);
regval &= ~(NGBE_PSR_1588_CTL_ENABLED | NGBE_PSR_1588_CTL_TYPE_MASK);
regval |= tsync_rx_ctl;
wr32(hw, NGBE_PSR_1588_CTL, regval);
/* define which PTP packets are time stamped */
wr32(hw, NGBE_PSR_1588_MSGTYPE, tsync_rx_mtrl);
NGBE_WRITE_FLUSH(hw);
/* clear TX/RX timestamp state, just to be sure */
ngbe_ptp_clear_tx_timestamp(adapter);
rd32(hw, NGBE_PSR_1588_STMPH);
return 0;
}
/**
* ngbe_ptp_set_ts_config - user entry point for timestamp mode
* @adapter: pointer to adapter struct
* @ifreq: ioctl data
*
* Set hardware to requested mode. If unsupported, return an error with no
* changes. Otherwise, store the mode for future reference.
*/
int ngbe_ptp_set_ts_config(struct ngbe_adapter *adapter, struct ifreq *ifr)
{
struct hwtstamp_config config;
int err;
if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
return -EFAULT;
err = ngbe_ptp_set_timestamp_mode(adapter, &config);
if (err)
return err;
/* save these settings for future reference */
memcpy(&adapter->tstamp_config, &config,
sizeof(adapter->tstamp_config));
return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
-EFAULT : 0;
}
static void ngbe_ptp_link_speed_adjust(struct ngbe_adapter *adapter,
u32 *shift, u32 *incval)
{
/**
* Scale the NIC cycle counter by a large factor so that
* relatively small corrections to the frequency can be added
* or subtracted. The drawbacks of a large factor include
* (a) the clock register overflows more quickly, (b) the cycle
* counter structure must be able to convert the systime value
* to nanoseconds using only a multiplier and a right-shift,
* and (c) the value must fit within the timinca register space
* => math based on internal DMA clock rate and available bits
*
* Note that when there is no link, internal DMA clock is same as when
* link speed is 10Gb. Set the registers correctly even when link is
* down to preserve the clock setting
*/
*shift = NGBE_INCVAL_SHIFT_1GB;
*incval = NGBE_INCVAL_1GB;
return;
}
/**
* ngbe_ptp_start_cyclecounter - create the cycle counter from hw
* @adapter: pointer to the adapter structure
*
* This function should be called to set the proper values for the TIMINCA
* register and tell the cyclecounter structure what the tick rate of SYSTIME
* is. It does not directly modify SYSTIME registers or the timecounter
* structure. It should be called whenever a new TIMINCA value is necessary,
* such as during initialization or when the link speed changes.
*/
void ngbe_ptp_start_cyclecounter(struct ngbe_adapter *adapter)
{
struct ngbe_hw *hw = &adapter->hw;
unsigned long flags;
struct cyclecounter cc;
u32 incval = 0;
/* For some of the boards below this mask is technically incorrect.
* The timestamp mask overflows at approximately 61bits. However the
* particular hardware does not overflow on an even bitmask value.
* Instead, it overflows due to conversion of upper 32bits billions of
* cycles. Timecounters are not really intended for this purpose so
* they do not properly function if the overflow point isn't 2^N-1.
* However, the actual SYSTIME values in question take ~138 years to
* overflow. In practice this means they won't actually overflow. A
* proper fix to this problem would require modification of the
* timecounter delta calculations.
*/
cc.mask = CLOCKSOURCE_MASK(64);
cc.mult = 1;
cc.shift = 0;
cc.read = ngbe_ptp_read;
ngbe_ptp_link_speed_adjust(adapter, &cc.shift, &incval);
wr32(hw, NGBE_TSEC_1588_INC, NGBE_TSEC_1588_INC_IV(incval));
/* update the base incval used to calculate frequency adjustment */
WRITE_ONCE(adapter->base_incval, incval);
smp_mb();
/* need lock to prevent incorrect read while modifying cyclecounter */
spin_lock_irqsave(&adapter->tmreg_lock, flags);
memcpy(&adapter->hw_cc, &cc, sizeof(adapter->hw_cc));
spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
}
/**
* ngbe_ptp_reset
* @adapter: the ngbe private board structure
*
* When the MAC resets, all of the hardware configuration for timesync is
* reset. This function should be called to re-enable the device for PTP,
* using the last known settings. However, we do lose the current clock time,
* so we fallback to resetting it based on the kernel's realtime clock.
*
* This function will maintain the hwtstamp_config settings, and it retriggers
* the SDP output if it's enabled.
*/
void ngbe_ptp_reset(struct ngbe_adapter *adapter)
{
unsigned long flags;
/* reset the hardware timestamping mode */
ngbe_ptp_set_timestamp_mode(adapter, &adapter->tstamp_config);
ngbe_ptp_start_cyclecounter(adapter);
spin_lock_irqsave(&adapter->tmreg_lock, flags);
timecounter_init(&adapter->hw_tc, &adapter->hw_cc,
ktime_to_ns(ktime_get_real()));
spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
adapter->last_overflow_check = jiffies;
}
/**
* ngbe_ptp_create_clock
* @adapter: the ngbe private adapter structure
*
* This function performs setup of the user entry point function table and
* initalizes the PTP clock device used by userspace to access the clock-like
* features of the PTP core. It will be called by ngbe_ptp_init, and may
* re-use a previously initialized clock (such as during a suspend/resume
* cycle).
*/
static long ngbe_ptp_create_clock(struct ngbe_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
long err;
/* do nothing if we already have a clock device */
if (!IS_ERR_OR_NULL(adapter->ptp_clock))
return 0;
snprintf(adapter->ptp_caps.name, sizeof(adapter->ptp_caps.name),
"%s", netdev->name);
adapter->ptp_caps.owner = THIS_MODULE;
adapter->ptp_caps.max_adj = 500000000; /* 10^-9s */
adapter->ptp_caps.n_alarm = 0;
adapter->ptp_caps.n_ext_ts = 0;
adapter->ptp_caps.n_per_out = 0;
adapter->ptp_caps.pps = 0;
#ifndef HAVE_NOT_PTT_ADJFREQ
adapter->ptp_caps.adjfreq = ngbe_ptp_adjfreq;
#endif
adapter->ptp_caps.adjtime = ngbe_ptp_adjtime;
#ifdef HAVE_PTP_CLOCK_INFO_GETTIME64
adapter->ptp_caps.gettime64 = ngbe_ptp_gettime64;
adapter->ptp_caps.settime64 = ngbe_ptp_settime64;
#else
adapter->ptp_caps.gettime = ngbe_ptp_gettime;
adapter->ptp_caps.settime = ngbe_ptp_settime;
#endif
adapter->ptp_caps.enable = ngbe_ptp_feature_enable;
adapter->ptp_clock = ptp_clock_register(&adapter->ptp_caps,
pci_dev_to_dev(adapter->pdev));
if (IS_ERR(adapter->ptp_clock)) {
err = PTR_ERR(adapter->ptp_clock);
adapter->ptp_clock = NULL;
e_dev_err("ptp_clock_register failed\n");
return err;
} else
e_dev_info("registered PHC device on %s\n", netdev->name);
/* Set the default timestamp mode to disabled here. We do this in
* create_clock instead of initialization, because we don't want to
* override the previous settings during a suspend/resume cycle.
*/
adapter->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;
adapter->tstamp_config.tx_type = HWTSTAMP_TX_OFF;
return 0;
}
/**
* ngbe_ptp_init
* @adapter: the ngbe private adapter structure
*
* This function performs the required steps for enabling ptp
* support. If ptp support has already been loaded it simply calls the
* cyclecounter init routine and exits.
*/
void ngbe_ptp_init(struct ngbe_adapter *adapter)
{
/* initialize the spin lock first, since the user might call the clock
* functions any time after we've initialized the ptp clock device.
*/
spin_lock_init(&adapter->tmreg_lock);
/* obtain a ptp clock device, or re-use an existing device */
if (ngbe_ptp_create_clock(adapter))
return;
/* we have a clock, so we can intialize work for timestamps now */
INIT_WORK(&adapter->ptp_tx_work, ngbe_ptp_tx_hwtstamp_work);
/* reset the ptp related hardware bits */
ngbe_ptp_reset(adapter);
/* enter the NGBE_PTP_RUNNING state */
set_bit(__NGBE_PTP_RUNNING, &adapter->state);
return;
}
/**
* ngbe_ptp_suspend - stop ptp work items
* @adapter: pointer to adapter struct
*
* This function suspends ptp activity, and prevents more work from being
* generated, but does not destroy the clock device.
*/
void ngbe_ptp_suspend(struct ngbe_adapter *adapter)
{
/* leave the NGBE_PTP_RUNNING STATE */
if (!test_and_clear_bit(__NGBE_PTP_RUNNING, &adapter->state))
return;
adapter->flags2 &= ~NGBE_FLAG2_PTP_PPS_ENABLED;
cancel_work_sync(&adapter->ptp_tx_work);
ngbe_ptp_clear_tx_timestamp(adapter);
}
/**
* ngbe_ptp_stop - destroy the ptp_clock device
* @adapter: pointer to adapter struct
*
* Completely destroy the ptp_clock device, and disable all PTP related
* features. Intended to be run when the device is being closed.
*/
void ngbe_ptp_stop(struct ngbe_adapter *adapter)
{
/* first, suspend ptp activity */
ngbe_ptp_suspend(adapter);
/* now destroy the ptp clock device */
if (adapter->ptp_clock) {
ptp_clock_unregister(adapter->ptp_clock);
adapter->ptp_clock = NULL;
e_dev_info("removed PHC on %s\n",
adapter->netdev->name);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,76 @@
/*
* WangXun Gigabit PCI Express Linux driver
* Copyright (c) 2015 - 2017 Beijing WangXun Technology Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* The full GNU General Public License is included in this distribution in
* the file called "COPYING".
*/
#ifndef _NGBE_SRIOV_H_
#define _NGBE_SRIOV_H_
/* ngbe driver limit the max number of VFs could be enabled to
* 7 (NGBE_MAX_VF_FUNCTIONS - 1)
*/
#define NGBE_MAX_VFS_DRV_LIMIT (NGBE_MAX_VF_FUNCTIONS - 1)
void ngbe_restore_vf_multicasts(struct ngbe_adapter *adapter);
int ngbe_set_vf_vlan(struct ngbe_adapter *adapter, int add, int vid, u16 vf);
void ngbe_set_vmolr(struct ngbe_hw *hw, u16 vf, bool aupe);
void ngbe_msg_task(struct ngbe_adapter *adapter);
int ngbe_set_vf_mac(struct ngbe_adapter *adapter,
u16 vf, unsigned char *mac_addr);
void ngbe_disable_tx_rx(struct ngbe_adapter *adapter);
void ngbe_ping_all_vfs(struct ngbe_adapter *adapter);
void ngbe_ping_all_vfs_with_link_status(struct ngbe_adapter *adapter, bool link_up);
#ifdef IFLA_VF_MAX
int ngbe_ndo_set_vf_mac(struct net_device *netdev, int queue, u8 *mac);
#ifdef IFLA_VF_VLAN_INFO_MAX
int ngbe_ndo_set_vf_vlan(struct net_device *netdev, int queue, u16 vlan,
u8 qos, __be16 vlan_proto);
#else
int ngbe_ndo_set_vf_vlan(struct net_device *netdev, int queue, u16 vlan,
u8 qos);
#endif
#ifdef HAVE_NDO_SET_VF_MIN_MAX_TX_RATE
int ngbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int min_tx_rate,
int max_tx_rate);
#else
int ngbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate);
#endif /* HAVE_NDO_SET_VF_MIN_MAX_TX_RATE */
#ifdef HAVE_VF_SPOOFCHK_CONFIGURE
int ngbe_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting);
#endif
#ifdef HAVE_NDO_SET_VF_TRUST
int ngbe_ndo_set_vf_trust(struct net_device *netdev, int vf, bool setting);
#endif
int ngbe_ndo_get_vf_config(struct net_device *netdev,
int vf, struct ifla_vf_info *ivi);
#endif /* IFLA_VF_MAX */
int ngbe_disable_sriov(struct ngbe_adapter *adapter);
#ifdef CONFIG_PCI_IOV
int ngbe_vf_configuration(struct pci_dev *pdev, unsigned int event_mask);
void ngbe_enable_sriov(struct ngbe_adapter *adapter);
#endif
int ngbe_pci_sriov_configure(struct pci_dev *dev, int num_vfs);
#define NGBE_VF_STATUS_LINKUP 0x1
/*
* These are defined in ngbe_type.h on behalf of the VF driver
* but we need them here unwrapped for the PF driver.
*/
//#define NGBE_DEV_ID_SP_VF 0x1000
#endif /* _NGBE_SRIOV_H_ */

View File

@ -0,0 +1,226 @@
/*
* WangXun Gigabit PCI Express Linux driver
* Copyright (c) 2015 - 2017 Beijing WangXun Technology Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* The full GNU General Public License is included in this distribution in
* the file called "COPYING".
*
*/
#include "ngbe.h"
#include "ngbe_hw.h"
#include "ngbe_type.h"
#ifdef NGBE_SYSFS
#include <linux/module.h>
#include <linux/types.h>
#include <linux/sysfs.h>
#include <linux/kobject.h>
#include <linux/device.h>
#include <linux/netdevice.h>
#include <linux/time.h>
#ifdef NGBE_HWMON
#include <linux/hwmon.h>
#endif
#ifdef NGBE_HWMON
/* hwmon callback functions */
static ssize_t ngbe_hwmon_show_temp(struct device __always_unused *dev,
struct device_attribute *attr,
char *buf)
{
struct hwmon_attr *ngbe_attr = container_of(attr, struct hwmon_attr,
dev_attr);
struct ngbe_hw *hw = ngbe_attr->hw;
unsigned int value;
/* reset the temp field */
hw->mac.ops.get_thermal_sensor_data(hw);
value = ngbe_attr->sensor->temp;
/* display millidegree */
value *= 1000;
return sprintf(buf, "%u\n", value);
}
static ssize_t ngbe_hwmon_show_alarmthresh(struct device __always_unused *dev,
struct device_attribute *attr,
char *buf)
{
struct hwmon_attr *ngbe_attr = container_of(attr, struct hwmon_attr,
dev_attr);
unsigned int value = ngbe_attr->sensor->alarm_thresh;
/* display millidegree */
value *= 1000;
return sprintf(buf, "%u\n", value);
}
static ssize_t ngbe_hwmon_show_dalarmthresh(struct device __always_unused *dev,
struct device_attribute *attr,
char *buf)
{
struct hwmon_attr *ngbe_attr = container_of(attr, struct hwmon_attr,
dev_attr);
unsigned int value = ngbe_attr->sensor->dalarm_thresh;
/* display millidegree */
value *= 1000;
return sprintf(buf, "%u\n", value);
}
/**
* ngbe_add_hwmon_attr - Create hwmon attr table for a hwmon sysfs file.
* @adapter: pointer to the adapter structure
* @type: type of sensor data to display
*
* For each file we want in hwmon's sysfs interface we need a device_attribute
* This is included in our hwmon_attr struct that contains the references to
* the data structures we need to get the data to display.
*/
static int ngbe_add_hwmon_attr(struct ngbe_adapter *adapter, int type)
{
int rc;
unsigned int n_attr;
struct hwmon_attr *ngbe_attr;
n_attr = adapter->ngbe_hwmon_buff.n_hwmon;
ngbe_attr = &adapter->ngbe_hwmon_buff.hwmon_list[n_attr];
switch (type) {
case NGBE_HWMON_TYPE_TEMP:
ngbe_attr->dev_attr.show = ngbe_hwmon_show_temp;
snprintf(ngbe_attr->name, sizeof(ngbe_attr->name),
"temp%u_input", 0);
break;
case NGBE_HWMON_TYPE_ALARMTHRESH:
ngbe_attr->dev_attr.show = ngbe_hwmon_show_alarmthresh;
snprintf(ngbe_attr->name, sizeof(ngbe_attr->name),
"temp%u_alarmthresh", 0);
break;
case NGBE_HWMON_TYPE_DALARMTHRESH:
ngbe_attr->dev_attr.show = ngbe_hwmon_show_dalarmthresh;
snprintf(ngbe_attr->name, sizeof(ngbe_attr->name),
"temp%u_dalarmthresh", 0);
break;
default:
rc = -EPERM;
return rc;
}
/* These always the same regardless of type */
ngbe_attr->sensor =
&adapter->hw.mac.thermal_sensor_data.sensor;
ngbe_attr->hw = &adapter->hw;
ngbe_attr->dev_attr.store = NULL;
ngbe_attr->dev_attr.attr.mode = S_IRUGO;
ngbe_attr->dev_attr.attr.name = ngbe_attr->name;
rc = device_create_file(pci_dev_to_dev(adapter->pdev),
&ngbe_attr->dev_attr);
if (rc == 0)
++adapter->ngbe_hwmon_buff.n_hwmon;
return rc;
}
#endif /* NGBE_HWMON */
static void ngbe_sysfs_del_adapter(
struct ngbe_adapter __maybe_unused *adapter)
{
#ifdef NGBE_HWMON
int i;
if (adapter == NULL)
return;
for (i = 0; i < adapter->ngbe_hwmon_buff.n_hwmon; i++) {
device_remove_file(pci_dev_to_dev(adapter->pdev),
&adapter->ngbe_hwmon_buff.hwmon_list[i].dev_attr);
}
kfree(adapter->ngbe_hwmon_buff.hwmon_list);
if (adapter->ngbe_hwmon_buff.device)
hwmon_device_unregister(adapter->ngbe_hwmon_buff.device);
#endif /* NGBE_HWMON */
}
/* called from ngbe_main.c */
void ngbe_sysfs_exit(struct ngbe_adapter *adapter)
{
ngbe_sysfs_del_adapter(adapter);
}
/* called from ngbe_main.c */
int ngbe_sysfs_init(struct ngbe_adapter *adapter)
{
struct ngbe_hw *hw = &adapter->hw;
int rc = 0;
#ifdef NGBE_HWMON
struct hwmon_buff *ngbe_hwmon = &adapter->ngbe_hwmon_buff;
int n_attrs;
#endif /* NGBE_HWMON */
if (adapter == NULL)
goto err;
#ifdef NGBE_HWMON
/* Don't create thermal hwmon interface if no sensors present */
if (hw->mac.ops.init_thermal_sensor_thresh(hw))
goto no_thermal;
/*
* Allocation space for max attributs
* max num sensors * values (temp, alamthresh, dalarmthresh)
*/
n_attrs = 3;
ngbe_hwmon->hwmon_list = kcalloc(n_attrs, sizeof(struct hwmon_attr),
GFP_KERNEL);
if (!ngbe_hwmon->hwmon_list) {
rc = -ENOMEM;
goto err;
}
ngbe_hwmon->device =
hwmon_device_register(pci_dev_to_dev(adapter->pdev));
if (IS_ERR(ngbe_hwmon->device)) {
rc = PTR_ERR(ngbe_hwmon->device);
goto err;
}
/* Bail if any hwmon attr struct fails to initialize */
rc = ngbe_add_hwmon_attr(adapter, NGBE_HWMON_TYPE_TEMP);
rc |= ngbe_add_hwmon_attr(adapter, NGBE_HWMON_TYPE_ALARMTHRESH);
rc |= ngbe_add_hwmon_attr(adapter, NGBE_HWMON_TYPE_DALARMTHRESH);
if (rc)
goto err;
no_thermal:
#endif /* NGBE_HWMON */
goto exit;
err:
ngbe_sysfs_del_adapter(adapter);
exit:
return rc;
}
#endif /* NGBE_SYSFS */

File diff suppressed because it is too large Load Diff