Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,15 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2015 - 2022 Beijing WangXun Technology Co., Ltd.
#
# Makefile for the Wangxun(R) 10GbE PCI Express ethernet driver
#
obj-$(CONFIG_TXGBE) += txgbe.o
txgbe-objs := txgbe_main.o \
txgbe_hw.o \
txgbe_phy.o \
txgbe_irq.o \
txgbe_fdir.o \
txgbe_ethtool.o \
txgbe_aml.o
@@ -0,0 +1,527 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2015 - 2025 Beijing WangXun Technology Co., Ltd. */
#include <linux/phylink.h>
#include <linux/iopoll.h>
#include <linux/pci.h>
#include <linux/phy.h>
#include "../libwx/wx_type.h"
#include "../libwx/wx_lib.h"
#include "../libwx/wx_ptp.h"
#include "../libwx/wx_hw.h"
#include "../libwx/wx_sriov.h"
#include "txgbe_type.h"
#include "txgbe_aml.h"
#include "txgbe_hw.h"
void txgbe_gpio_init_aml(struct wx *wx)
{
u32 status, mod_rst;
if (wx->mac.type == wx_mac_aml40)
mod_rst = TXGBE_GPIOBIT_4;
else
mod_rst = TXGBE_GPIOBIT_2;
wr32(wx, WX_GPIO_INTTYPE_LEVEL, mod_rst);
wr32(wx, WX_GPIO_INTEN, mod_rst);
status = rd32(wx, WX_GPIO_INTSTATUS);
for (int i = 0; i < 6; i++) {
if (status & BIT(i))
wr32(wx, WX_GPIO_EOI, BIT(i));
}
}
irqreturn_t txgbe_gpio_irq_handler_aml(int irq, void *data)
{
struct txgbe *txgbe = data;
struct wx *wx = txgbe->wx;
u32 status, mod_rst;
if (wx->mac.type == wx_mac_aml40)
mod_rst = TXGBE_GPIOBIT_4;
else
mod_rst = TXGBE_GPIOBIT_2;
wr32(wx, WX_GPIO_INTMASK, 0xFF);
status = rd32(wx, WX_GPIO_INTSTATUS);
if (status & mod_rst) {
set_bit(WX_FLAG_NEED_MODULE_RESET, wx->flags);
wr32(wx, WX_GPIO_EOI, mod_rst);
wx_service_event_schedule(wx);
}
wr32(wx, WX_GPIO_INTMASK, 0);
return IRQ_HANDLED;
}
int txgbe_test_hostif(struct wx *wx)
{
struct txgbe_hic_ephy_getlink buffer;
if (wx->mac.type == wx_mac_sp)
return 0;
buffer.hdr.cmd = FW_PHY_GET_LINK_CMD;
buffer.hdr.buf_len = sizeof(struct txgbe_hic_ephy_getlink) -
sizeof(struct wx_hic_hdr);
buffer.hdr.cmd_or_resp.cmd_resv = FW_CEM_CMD_RESERVED;
return wx_host_interface_command(wx, (u32 *)&buffer, sizeof(buffer),
WX_HI_COMMAND_TIMEOUT, false);
}
int txgbe_read_eeprom_hostif(struct wx *wx,
struct txgbe_hic_i2c_read *buffer,
u32 length, u8 *data)
{
u32 dword_len, offset, value, i;
int err;
buffer->hdr.cmd = FW_READ_EEPROM_CMD;
buffer->hdr.buf_len = sizeof(struct txgbe_hic_i2c_read) -
sizeof(struct wx_hic_hdr);
buffer->hdr.cmd_or_resp.cmd_resv = FW_CEM_CMD_RESERVED;
err = wx_host_interface_command(wx, (u32 *)buffer,
sizeof(struct txgbe_hic_i2c_read),
WX_HI_COMMAND_TIMEOUT, false);
if (err != 0)
return err;
/* buffer length offset to read return data */
offset = sizeof(struct txgbe_hic_i2c_read) >> 2;
dword_len = round_up(length, 4) >> 2;
for (i = 0; i < dword_len; i++) {
u32 copy_len = min_t(u32, 4, length - i * 4);
value = rd32a(wx, WX_FW2SW_MBOX, i + offset);
le32_to_cpus(&value);
memcpy(data, &value, copy_len);
data += copy_len;
}
return 0;
}
static int txgbe_identify_module_hostif(struct wx *wx,
struct txgbe_hic_get_module_info *buffer)
{
buffer->hdr.cmd = FW_GET_MODULE_INFO_CMD;
buffer->hdr.buf_len = sizeof(struct txgbe_hic_get_module_info) -
sizeof(struct wx_hic_hdr);
buffer->hdr.cmd_or_resp.cmd_resv = FW_CEM_CMD_RESERVED;
return wx_host_interface_command(wx, (u32 *)buffer,
sizeof(struct txgbe_hic_get_module_info),
WX_HI_COMMAND_TIMEOUT, true);
}
static int txgbe_set_phy_link_hostif(struct wx *wx, int speed, int autoneg, int duplex)
{
struct txgbe_hic_ephy_setlink buffer;
buffer.hdr.cmd = FW_PHY_SET_LINK_CMD;
buffer.hdr.buf_len = sizeof(struct txgbe_hic_ephy_setlink) -
sizeof(struct wx_hic_hdr);
buffer.hdr.cmd_or_resp.cmd_resv = FW_CEM_CMD_RESERVED;
switch (speed) {
case SPEED_40000:
buffer.speed = TXGBE_LINK_SPEED_40GB_FULL;
break;
case SPEED_25000:
buffer.speed = TXGBE_LINK_SPEED_25GB_FULL;
break;
case SPEED_10000:
buffer.speed = TXGBE_LINK_SPEED_10GB_FULL;
break;
default:
buffer.speed = TXGBE_LINK_SPEED_UNKNOWN;
break;
}
buffer.fec_mode = TXGBE_PHY_FEC_AUTO;
buffer.autoneg = autoneg;
buffer.duplex = duplex;
return wx_host_interface_command(wx, (u32 *)&buffer, sizeof(buffer),
WX_HI_COMMAND_TIMEOUT, false);
}
static void txgbe_get_link_capabilities(struct wx *wx, int *speed,
int *autoneg, int *duplex)
{
struct txgbe *txgbe = wx->priv;
if (test_bit(PHY_INTERFACE_MODE_XLGMII, txgbe->link_interfaces))
*speed = SPEED_40000;
else if (test_bit(PHY_INTERFACE_MODE_25GBASER, txgbe->link_interfaces))
*speed = SPEED_25000;
else if (test_bit(PHY_INTERFACE_MODE_10GBASER, txgbe->link_interfaces))
*speed = SPEED_10000;
else
*speed = SPEED_UNKNOWN;
*autoneg = phylink_test(txgbe->advertising, Autoneg);
*duplex = *speed == SPEED_UNKNOWN ? DUPLEX_HALF : DUPLEX_FULL;
}
static void txgbe_get_mac_link(struct wx *wx, int *speed)
{
u32 status;
status = rd32(wx, TXGBE_CFG_PORT_ST);
if (!(status & TXGBE_CFG_PORT_ST_LINK_UP))
*speed = SPEED_UNKNOWN;
else if (status & TXGBE_CFG_PORT_ST_LINK_AML_40G)
*speed = SPEED_40000;
else if (status & TXGBE_CFG_PORT_ST_LINK_AML_25G)
*speed = SPEED_25000;
else if (status & TXGBE_CFG_PORT_ST_LINK_AML_10G)
*speed = SPEED_10000;
else
*speed = SPEED_UNKNOWN;
}
void txgbe_set_phy_link(struct wx *wx)
{
int speed, autoneg, duplex, err;
txgbe_get_link_capabilities(wx, &speed, &autoneg, &duplex);
err = txgbe_set_phy_link_hostif(wx, speed, autoneg, duplex);
if (err)
wx_err(wx, "Failed to setup link\n");
}
static int txgbe_sfp_to_linkmodes(struct wx *wx, struct txgbe_sff_id *id)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, };
DECLARE_PHY_INTERFACE_MASK_ZERO(interfaces);
struct txgbe *txgbe = wx->priv;
if (id->cable_tech & TXGBE_SFF_DA_PASSIVE_CABLE) {
txgbe->link_port = PORT_DA;
phylink_set(modes, Autoneg);
if (id->com_25g_code == TXGBE_SFF_25GBASECR_91FEC ||
id->com_25g_code == TXGBE_SFF_25GBASECR_74FEC ||
id->com_25g_code == TXGBE_SFF_25GBASECR_NOFEC) {
phylink_set(modes, 25000baseCR_Full);
phylink_set(modes, 10000baseCR_Full);
__set_bit(PHY_INTERFACE_MODE_25GBASER, interfaces);
__set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
} else {
phylink_set(modes, 10000baseCR_Full);
__set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
}
} else if (id->cable_tech & TXGBE_SFF_DA_ACTIVE_CABLE) {
txgbe->link_port = PORT_DA;
phylink_set(modes, Autoneg);
phylink_set(modes, 25000baseCR_Full);
__set_bit(PHY_INTERFACE_MODE_25GBASER, interfaces);
} else {
if (id->com_25g_code == TXGBE_SFF_25GBASESR_CAPABLE ||
id->com_25g_code == TXGBE_SFF_25GBASEER_CAPABLE ||
id->com_25g_code == TXGBE_SFF_25GBASELR_CAPABLE) {
txgbe->link_port = PORT_FIBRE;
phylink_set(modes, 25000baseSR_Full);
__set_bit(PHY_INTERFACE_MODE_25GBASER, interfaces);
}
if (id->com_10g_code & TXGBE_SFF_10GBASESR_CAPABLE) {
txgbe->link_port = PORT_FIBRE;
phylink_set(modes, 10000baseSR_Full);
__set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
}
if (id->com_10g_code & TXGBE_SFF_10GBASELR_CAPABLE) {
txgbe->link_port = PORT_FIBRE;
phylink_set(modes, 10000baseLR_Full);
__set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
}
}
if (phy_interface_empty(interfaces)) {
wx_err(wx, "unsupported SFP module\n");
return -EINVAL;
}
phylink_set(modes, Pause);
phylink_set(modes, Asym_Pause);
phylink_set(modes, FIBRE);
if (!linkmode_equal(txgbe->link_support, modes)) {
linkmode_copy(txgbe->link_support, modes);
phy_interface_and(txgbe->link_interfaces,
wx->phylink_config.supported_interfaces,
interfaces);
linkmode_copy(txgbe->advertising, modes);
set_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags);
}
return 0;
}
static int txgbe_qsfp_to_linkmodes(struct wx *wx, struct txgbe_sff_id *id)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, };
DECLARE_PHY_INTERFACE_MASK_ZERO(interfaces);
struct txgbe *txgbe = wx->priv;
if (id->transceiver_type & TXGBE_SFF_ETHERNET_40G_CR4) {
txgbe->link_port = PORT_DA;
phylink_set(modes, Autoneg);
phylink_set(modes, 40000baseCR4_Full);
phylink_set(modes, 10000baseCR_Full);
__set_bit(PHY_INTERFACE_MODE_XLGMII, interfaces);
__set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
}
if (id->transceiver_type & TXGBE_SFF_ETHERNET_40G_SR4) {
txgbe->link_port = PORT_FIBRE;
phylink_set(modes, 40000baseSR4_Full);
__set_bit(PHY_INTERFACE_MODE_XLGMII, interfaces);
}
if (id->transceiver_type & TXGBE_SFF_ETHERNET_40G_LR4) {
txgbe->link_port = PORT_FIBRE;
phylink_set(modes, 40000baseLR4_Full);
__set_bit(PHY_INTERFACE_MODE_XLGMII, interfaces);
}
if (id->transceiver_type & TXGBE_SFF_ETHERNET_40G_ACTIVE) {
txgbe->link_port = PORT_DA;
phylink_set(modes, Autoneg);
phylink_set(modes, 40000baseCR4_Full);
__set_bit(PHY_INTERFACE_MODE_XLGMII, interfaces);
}
if (id->transceiver_type & TXGBE_SFF_ETHERNET_RSRVD) {
if (id->sff_opt1 & TXGBE_SFF_ETHERNET_100G_CR4) {
txgbe->link_port = PORT_DA;
phylink_set(modes, Autoneg);
phylink_set(modes, 40000baseCR4_Full);
phylink_set(modes, 25000baseCR_Full);
phylink_set(modes, 10000baseCR_Full);
__set_bit(PHY_INTERFACE_MODE_XLGMII, interfaces);
__set_bit(PHY_INTERFACE_MODE_25GBASER, interfaces);
__set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
}
}
if (phy_interface_empty(interfaces)) {
wx_err(wx, "unsupported QSFP module\n");
return -EINVAL;
}
phylink_set(modes, Pause);
phylink_set(modes, Asym_Pause);
phylink_set(modes, FIBRE);
if (!linkmode_equal(txgbe->link_support, modes)) {
linkmode_copy(txgbe->link_support, modes);
phy_interface_and(txgbe->link_interfaces,
wx->phylink_config.supported_interfaces,
interfaces);
linkmode_copy(txgbe->advertising, modes);
set_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags);
}
return 0;
}
int txgbe_identify_module(struct wx *wx)
{
struct txgbe_hic_get_module_info buffer = { 0 };
struct txgbe_sff_id *id;
int err = 0;
u32 mod_abs;
u32 gpio;
if (wx->mac.type == wx_mac_aml40)
mod_abs = TXGBE_GPIOBIT_4;
else
mod_abs = TXGBE_GPIOBIT_2;
gpio = rd32(wx, WX_GPIO_EXT);
if (gpio & mod_abs)
return -ENODEV;
err = txgbe_identify_module_hostif(wx, &buffer);
if (err) {
wx_err(wx, "Failed to identify module\n");
return err;
}
id = &buffer.id;
if (id->identifier == TXGBE_SFF_IDENTIFIER_SFP)
return txgbe_sfp_to_linkmodes(wx, id);
if (id->identifier == TXGBE_SFF_IDENTIFIER_QSFP ||
id->identifier == TXGBE_SFF_IDENTIFIER_QSFP_PLUS ||
id->identifier == TXGBE_SFF_IDENTIFIER_QSFP28)
return txgbe_qsfp_to_linkmodes(wx, id);
wx_err(wx, "Invalid module\n");
return -EINVAL;
}
void txgbe_setup_link(struct wx *wx)
{
struct txgbe *txgbe = wx->priv;
phy_interface_zero(txgbe->link_interfaces);
linkmode_zero(txgbe->link_support);
set_bit(WX_FLAG_NEED_MODULE_RESET, wx->flags);
wx_service_event_schedule(wx);
}
static void txgbe_get_link_state(struct phylink_config *config,
struct phylink_link_state *state)
{
struct wx *wx = phylink_to_wx(config);
int speed;
txgbe_get_mac_link(wx, &speed);
state->link = speed != SPEED_UNKNOWN;
state->speed = speed;
state->duplex = state->link ? DUPLEX_FULL : DUPLEX_UNKNOWN;
}
static void txgbe_reconfig_mac(struct wx *wx)
{
u32 wdg, fc;
wdg = rd32(wx, WX_MAC_WDG_TIMEOUT);
fc = rd32(wx, WX_MAC_RX_FLOW_CTRL);
wr32(wx, WX_MIS_RST, TXGBE_MIS_RST_MAC_RST(wx->bus.func));
/* wait for MAC reset complete */
usleep_range(1000, 1500);
wr32m(wx, TXGBE_MAC_MISC_CTL, TXGBE_MAC_MISC_CTL_LINK_STS_MOD,
TXGBE_MAC_MISC_CTL_LINK_BOTH);
wx_reset_mac(wx);
wr32(wx, WX_MAC_WDG_TIMEOUT, wdg);
wr32(wx, WX_MAC_RX_FLOW_CTRL, fc);
}
static void txgbe_mac_link_up_aml(struct phylink_config *config,
struct phy_device *phy,
unsigned int mode,
phy_interface_t interface,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
struct wx *wx = phylink_to_wx(config);
u32 txcfg;
wx_fc_enable(wx, tx_pause, rx_pause);
txgbe_reconfig_mac(wx);
txgbe_enable_sec_tx_path(wx);
txcfg = rd32(wx, TXGBE_AML_MAC_TX_CFG);
txcfg &= ~TXGBE_AML_MAC_TX_CFG_SPEED_MASK;
switch (speed) {
case SPEED_40000:
txcfg |= TXGBE_AML_MAC_TX_CFG_SPEED_40G;
break;
case SPEED_25000:
txcfg |= TXGBE_AML_MAC_TX_CFG_SPEED_25G;
break;
case SPEED_10000:
txcfg |= TXGBE_AML_MAC_TX_CFG_SPEED_10G;
break;
default:
break;
}
wr32m(wx, WX_MAC_RX_CFG, WX_MAC_RX_CFG_RE, WX_MAC_RX_CFG_RE);
wr32(wx, TXGBE_AML_MAC_TX_CFG, txcfg | TXGBE_AML_MAC_TX_CFG_TE);
wx->speed = speed;
wx->last_rx_ptp_check = jiffies;
if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
wx_ptp_reset_cyclecounter(wx);
/* ping all the active vfs to let them know we are going up */
wx_ping_all_vfs_with_link_status(wx, true);
}
static void txgbe_mac_link_down_aml(struct phylink_config *config,
unsigned int mode,
phy_interface_t interface)
{
struct wx *wx = phylink_to_wx(config);
wr32m(wx, TXGBE_AML_MAC_TX_CFG, TXGBE_AML_MAC_TX_CFG_TE, 0);
wr32m(wx, WX_MAC_RX_CFG, WX_MAC_RX_CFG_RE, 0);
wx->speed = SPEED_UNKNOWN;
if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
wx_ptp_reset_cyclecounter(wx);
/* ping all the active vfs to let them know we are going down */
wx_ping_all_vfs_with_link_status(wx, false);
}
static void txgbe_mac_config_aml(struct phylink_config *config, unsigned int mode,
const struct phylink_link_state *state)
{
}
static const struct phylink_mac_ops txgbe_mac_ops_aml = {
.mac_config = txgbe_mac_config_aml,
.mac_link_down = txgbe_mac_link_down_aml,
.mac_link_up = txgbe_mac_link_up_aml,
};
int txgbe_phylink_init_aml(struct txgbe *txgbe)
{
struct phylink_link_state state;
struct phylink_config *config;
struct wx *wx = txgbe->wx;
phy_interface_t phy_mode;
struct phylink *phylink;
int err;
config = &wx->phylink_config;
config->dev = &wx->netdev->dev;
config->type = PHYLINK_NETDEV;
config->mac_capabilities = MAC_25000FD | MAC_10000FD |
MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
config->get_fixed_state = txgbe_get_link_state;
if (wx->mac.type == wx_mac_aml40) {
config->mac_capabilities |= MAC_40000FD;
phy_mode = PHY_INTERFACE_MODE_XLGMII;
__set_bit(PHY_INTERFACE_MODE_XLGMII, config->supported_interfaces);
state.speed = SPEED_40000;
state.duplex = DUPLEX_FULL;
} else {
phy_mode = PHY_INTERFACE_MODE_25GBASER;
state.speed = SPEED_25000;
state.duplex = DUPLEX_FULL;
}
__set_bit(PHY_INTERFACE_MODE_25GBASER, config->supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_10GBASER, config->supported_interfaces);
phylink = phylink_create(config, NULL, phy_mode, &txgbe_mac_ops_aml);
if (IS_ERR(phylink))
return PTR_ERR(phylink);
err = phylink_set_fixed_link(phylink, &state);
if (err) {
wx_err(wx, "Failed to set fixed link\n");
phylink_destroy(phylink);
return err;
}
wx->phylink = phylink;
return 0;
}
@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2015 - 2025 Beijing WangXun Technology Co., Ltd. */
#ifndef _TXGBE_AML_H_
#define _TXGBE_AML_H_
void txgbe_gpio_init_aml(struct wx *wx);
irqreturn_t txgbe_gpio_irq_handler_aml(int irq, void *data);
int txgbe_test_hostif(struct wx *wx);
int txgbe_read_eeprom_hostif(struct wx *wx,
struct txgbe_hic_i2c_read *buffer,
u32 length, u8 *data);
void txgbe_set_phy_link(struct wx *wx);
int txgbe_identify_module(struct wx *wx);
void txgbe_setup_link(struct wx *wx);
int txgbe_phylink_init_aml(struct txgbe *txgbe);
#endif /* _TXGBE_AML_H_ */
@@ -0,0 +1,597 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2015 - 2023 Beijing WangXun Technology Co., Ltd. */
#include <linux/pci.h>
#include <linux/phylink.h>
#include <linux/netdevice.h>
#include "../libwx/wx_ethtool.h"
#include "../libwx/wx_type.h"
#include "../libwx/wx_lib.h"
#include "txgbe_type.h"
#include "txgbe_fdir.h"
#include "txgbe_aml.h"
#include "txgbe_ethtool.h"
int txgbe_get_link_ksettings(struct net_device *netdev,
struct ethtool_link_ksettings *cmd)
{
struct wx *wx = netdev_priv(netdev);
struct txgbe *txgbe = wx->priv;
int err;
err = wx_get_link_ksettings(netdev, cmd);
if (err)
return err;
if (wx->mac.type == wx_mac_sp)
return 0;
cmd->base.port = txgbe->link_port;
cmd->base.autoneg = phylink_test(txgbe->advertising, Autoneg) ?
AUTONEG_ENABLE : AUTONEG_DISABLE;
linkmode_copy(cmd->link_modes.supported, txgbe->link_support);
linkmode_copy(cmd->link_modes.advertising, txgbe->advertising);
return 0;
}
static int txgbe_set_ringparam(struct net_device *netdev,
struct ethtool_ringparam *ring,
struct kernel_ethtool_ringparam *kernel_ring,
struct netlink_ext_ack *extack)
{
struct wx *wx = netdev_priv(netdev);
u32 new_rx_count, new_tx_count;
struct wx_ring *temp_ring;
int i, err = 0;
new_tx_count = clamp_t(u32, ring->tx_pending, WX_MIN_TXD, WX_MAX_TXD);
new_tx_count = ALIGN(new_tx_count, WX_REQ_TX_DESCRIPTOR_MULTIPLE);
new_rx_count = clamp_t(u32, ring->rx_pending, WX_MIN_RXD, WX_MAX_RXD);
new_rx_count = ALIGN(new_rx_count, WX_REQ_RX_DESCRIPTOR_MULTIPLE);
if (new_tx_count == wx->tx_ring_count &&
new_rx_count == wx->rx_ring_count)
return 0;
mutex_lock(&wx->reset_lock);
set_bit(WX_STATE_RESETTING, wx->state);
if (!netif_running(wx->netdev)) {
for (i = 0; i < wx->num_tx_queues; i++)
wx->tx_ring[i]->count = new_tx_count;
for (i = 0; i < wx->num_rx_queues; i++)
wx->rx_ring[i]->count = new_rx_count;
wx->tx_ring_count = new_tx_count;
wx->rx_ring_count = new_rx_count;
goto clear_reset;
}
/* allocate temporary buffer to store rings in */
i = max_t(int, wx->num_tx_queues, wx->num_rx_queues);
temp_ring = kvmalloc_objs(struct wx_ring, i);
if (!temp_ring) {
err = -ENOMEM;
goto clear_reset;
}
txgbe_down(wx);
wx_set_ring(wx, new_tx_count, new_rx_count, temp_ring);
kvfree(temp_ring);
txgbe_up(wx);
clear_reset:
clear_bit(WX_STATE_RESETTING, wx->state);
mutex_unlock(&wx->reset_lock);
return err;
}
static int txgbe_get_ethtool_fdir_entry(struct txgbe *txgbe,
struct ethtool_rxnfc *cmd)
{
struct ethtool_rx_flow_spec *fsp =
(struct ethtool_rx_flow_spec *)&cmd->fs;
union txgbe_atr_input *mask = &txgbe->fdir_mask;
struct txgbe_fdir_filter *rule = NULL;
struct hlist_node *node;
/* report total rule count */
cmd->data = (1024 << TXGBE_FDIR_PBALLOC_64K) - 2;
hlist_for_each_entry_safe(rule, node, &txgbe->fdir_filter_list,
fdir_node) {
if (fsp->location <= rule->sw_idx)
break;
}
if (!rule || fsp->location != rule->sw_idx)
return -EINVAL;
/* set flow type field */
switch (rule->filter.formatted.flow_type) {
case TXGBE_ATR_FLOW_TYPE_TCPV4:
fsp->flow_type = TCP_V4_FLOW;
break;
case TXGBE_ATR_FLOW_TYPE_UDPV4:
fsp->flow_type = UDP_V4_FLOW;
break;
case TXGBE_ATR_FLOW_TYPE_SCTPV4:
fsp->flow_type = SCTP_V4_FLOW;
break;
case TXGBE_ATR_FLOW_TYPE_IPV4:
fsp->flow_type = IP_USER_FLOW;
fsp->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;
fsp->h_u.usr_ip4_spec.proto = 0;
fsp->m_u.usr_ip4_spec.proto = 0;
break;
default:
return -EINVAL;
}
fsp->h_u.tcp_ip4_spec.psrc = rule->filter.formatted.src_port;
fsp->m_u.tcp_ip4_spec.psrc = mask->formatted.src_port;
fsp->h_u.tcp_ip4_spec.pdst = rule->filter.formatted.dst_port;
fsp->m_u.tcp_ip4_spec.pdst = mask->formatted.dst_port;
fsp->h_u.tcp_ip4_spec.ip4src = rule->filter.formatted.src_ip[0];
fsp->m_u.tcp_ip4_spec.ip4src = mask->formatted.src_ip[0];
fsp->h_u.tcp_ip4_spec.ip4dst = rule->filter.formatted.dst_ip[0];
fsp->m_u.tcp_ip4_spec.ip4dst = mask->formatted.dst_ip[0];
fsp->h_ext.vlan_etype = rule->filter.formatted.flex_bytes;
fsp->m_ext.vlan_etype = mask->formatted.flex_bytes;
fsp->h_ext.data[1] = htonl(rule->filter.formatted.vm_pool);
fsp->m_ext.data[1] = htonl(mask->formatted.vm_pool);
fsp->flow_type |= FLOW_EXT;
/* record action */
if (rule->action == TXGBE_RDB_FDIR_DROP_QUEUE)
fsp->ring_cookie = RX_CLS_FLOW_DISC;
else
fsp->ring_cookie = rule->action;
return 0;
}
static int txgbe_get_ethtool_fdir_all(struct txgbe *txgbe,
struct ethtool_rxnfc *cmd,
u32 *rule_locs)
{
struct txgbe_fdir_filter *rule;
struct hlist_node *node;
int cnt = 0;
/* report total rule count */
cmd->data = (1024 << TXGBE_FDIR_PBALLOC_64K) - 2;
hlist_for_each_entry_safe(rule, node, &txgbe->fdir_filter_list,
fdir_node) {
if (cnt == cmd->rule_cnt)
return -EMSGSIZE;
rule_locs[cnt] = rule->sw_idx;
cnt++;
}
cmd->rule_cnt = cnt;
return 0;
}
static u32 txgbe_get_rx_ring_count(struct net_device *dev)
{
struct wx *wx = netdev_priv(dev);
return wx->num_rx_queues;
}
static int txgbe_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
u32 *rule_locs)
{
struct wx *wx = netdev_priv(dev);
struct txgbe *txgbe = wx->priv;
int ret = -EOPNOTSUPP;
switch (cmd->cmd) {
case ETHTOOL_GRXCLSRLCNT:
cmd->rule_cnt = txgbe->fdir_filter_count;
ret = 0;
break;
case ETHTOOL_GRXCLSRULE:
ret = txgbe_get_ethtool_fdir_entry(txgbe, cmd);
break;
case ETHTOOL_GRXCLSRLALL:
ret = txgbe_get_ethtool_fdir_all(txgbe, cmd, (u32 *)rule_locs);
break;
default:
break;
}
return ret;
}
static int txgbe_flowspec_to_flow_type(struct ethtool_rx_flow_spec *fsp,
u8 *flow_type)
{
switch (fsp->flow_type & ~FLOW_EXT) {
case TCP_V4_FLOW:
*flow_type = TXGBE_ATR_FLOW_TYPE_TCPV4;
break;
case UDP_V4_FLOW:
*flow_type = TXGBE_ATR_FLOW_TYPE_UDPV4;
break;
case SCTP_V4_FLOW:
*flow_type = TXGBE_ATR_FLOW_TYPE_SCTPV4;
break;
case IP_USER_FLOW:
switch (fsp->h_u.usr_ip4_spec.proto) {
case IPPROTO_TCP:
*flow_type = TXGBE_ATR_FLOW_TYPE_TCPV4;
break;
case IPPROTO_UDP:
*flow_type = TXGBE_ATR_FLOW_TYPE_UDPV4;
break;
case IPPROTO_SCTP:
*flow_type = TXGBE_ATR_FLOW_TYPE_SCTPV4;
break;
case 0:
if (!fsp->m_u.usr_ip4_spec.proto) {
*flow_type = TXGBE_ATR_FLOW_TYPE_IPV4;
break;
}
fallthrough;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
}
return 0;
}
static bool txgbe_match_ethtool_fdir_entry(struct txgbe *txgbe,
struct txgbe_fdir_filter *input)
{
struct txgbe_fdir_filter *rule = NULL;
struct hlist_node *node2;
hlist_for_each_entry_safe(rule, node2, &txgbe->fdir_filter_list,
fdir_node) {
if (rule->filter.formatted.bkt_hash ==
input->filter.formatted.bkt_hash &&
rule->action == input->action) {
wx_dbg(txgbe->wx, "FDIR entry already exist\n");
return true;
}
}
return false;
}
static int txgbe_update_ethtool_fdir_entry(struct txgbe *txgbe,
struct txgbe_fdir_filter *input,
u16 sw_idx)
{
struct hlist_node *node = NULL, *parent = NULL;
struct txgbe_fdir_filter *rule;
struct wx *wx = txgbe->wx;
bool deleted = false;
int err;
hlist_for_each_entry_safe(rule, node, &txgbe->fdir_filter_list,
fdir_node) {
/* hash found, or no matching entry */
if (rule->sw_idx >= sw_idx)
break;
parent = node;
}
/* if there is an old rule occupying our place remove it */
if (rule && rule->sw_idx == sw_idx) {
/* hardware filters are only configured when interface is up,
* and we should not issue filter commands while the interface
* is down
*/
if (netif_running(wx->netdev) &&
(!input || rule->filter.formatted.bkt_hash !=
input->filter.formatted.bkt_hash)) {
err = txgbe_fdir_erase_perfect_filter(wx,
&rule->filter,
sw_idx);
if (err)
return -EINVAL;
}
hlist_del(&rule->fdir_node);
kfree(rule);
txgbe->fdir_filter_count--;
deleted = true;
}
/* If we weren't given an input, then this was a request to delete a
* filter. We should return -EINVAL if the filter wasn't found, but
* return 0 if the rule was successfully deleted.
*/
if (!input)
return deleted ? 0 : -EINVAL;
/* initialize node and set software index */
INIT_HLIST_NODE(&input->fdir_node);
/* add filter to the list */
if (parent)
hlist_add_behind(&input->fdir_node, parent);
else
hlist_add_head(&input->fdir_node,
&txgbe->fdir_filter_list);
/* update counts */
txgbe->fdir_filter_count++;
return 0;
}
static int txgbe_add_ethtool_fdir_entry(struct txgbe *txgbe,
struct ethtool_rxnfc *cmd)
{
struct ethtool_rx_flow_spec *fsp =
(struct ethtool_rx_flow_spec *)&cmd->fs;
struct txgbe_fdir_filter *input;
union txgbe_atr_input mask;
struct wx *wx = txgbe->wx;
int err = -EINVAL;
u16 ptype = 0;
u8 queue;
if (!(test_bit(WX_FLAG_FDIR_PERFECT, wx->flags)))
return -EOPNOTSUPP;
/* ring_cookie is a masked into a set of queues and txgbe pools or
* we use drop index
*/
if (fsp->ring_cookie == RX_CLS_FLOW_DISC) {
queue = TXGBE_RDB_FDIR_DROP_QUEUE;
} else {
u32 ring = ethtool_get_flow_spec_ring(fsp->ring_cookie);
u8 vf = ethtool_get_flow_spec_ring_vf(fsp->ring_cookie);
if (!vf && ring >= wx->num_rx_queues)
return -EINVAL;
else if (vf && (vf > wx->num_vfs ||
ring >= wx->num_rx_queues_per_pool))
return -EINVAL;
/* Map the ring onto the absolute queue index */
if (!vf)
queue = wx->rx_ring[ring]->reg_idx;
else
queue = ((vf - 1) * wx->num_rx_queues_per_pool) + ring;
}
/* Don't allow indexes to exist outside of available space */
if (fsp->location >= ((1024 << TXGBE_FDIR_PBALLOC_64K) - 2)) {
wx_err(wx, "Location out of range\n");
return -EINVAL;
}
input = kzalloc_obj(*input, GFP_ATOMIC);
if (!input)
return -ENOMEM;
memset(&mask, 0, sizeof(union txgbe_atr_input));
/* set SW index */
input->sw_idx = fsp->location;
/* record flow type */
if (txgbe_flowspec_to_flow_type(fsp,
&input->filter.formatted.flow_type)) {
wx_err(wx, "Unrecognized flow type\n");
goto err_out;
}
mask.formatted.flow_type = TXGBE_ATR_L4TYPE_IPV6_MASK |
TXGBE_ATR_L4TYPE_MASK;
if (input->filter.formatted.flow_type == TXGBE_ATR_FLOW_TYPE_IPV4)
mask.formatted.flow_type &= TXGBE_ATR_L4TYPE_IPV6_MASK;
/* Copy input into formatted structures */
input->filter.formatted.src_ip[0] = fsp->h_u.tcp_ip4_spec.ip4src;
mask.formatted.src_ip[0] = fsp->m_u.tcp_ip4_spec.ip4src;
input->filter.formatted.dst_ip[0] = fsp->h_u.tcp_ip4_spec.ip4dst;
mask.formatted.dst_ip[0] = fsp->m_u.tcp_ip4_spec.ip4dst;
input->filter.formatted.src_port = fsp->h_u.tcp_ip4_spec.psrc;
mask.formatted.src_port = fsp->m_u.tcp_ip4_spec.psrc;
input->filter.formatted.dst_port = fsp->h_u.tcp_ip4_spec.pdst;
mask.formatted.dst_port = fsp->m_u.tcp_ip4_spec.pdst;
if (fsp->flow_type & FLOW_EXT) {
input->filter.formatted.vm_pool =
(unsigned char)ntohl(fsp->h_ext.data[1]);
mask.formatted.vm_pool =
(unsigned char)ntohl(fsp->m_ext.data[1]);
input->filter.formatted.flex_bytes =
fsp->h_ext.vlan_etype;
mask.formatted.flex_bytes = fsp->m_ext.vlan_etype;
}
switch (input->filter.formatted.flow_type) {
case TXGBE_ATR_FLOW_TYPE_TCPV4:
ptype = WX_PTYPE_L2_IPV4_TCP;
break;
case TXGBE_ATR_FLOW_TYPE_UDPV4:
ptype = WX_PTYPE_L2_IPV4_UDP;
break;
case TXGBE_ATR_FLOW_TYPE_SCTPV4:
ptype = WX_PTYPE_L2_IPV4_SCTP;
break;
case TXGBE_ATR_FLOW_TYPE_IPV4:
ptype = WX_PTYPE_L2_IPV4;
break;
default:
break;
}
input->filter.formatted.vlan_id = htons(ptype);
if (mask.formatted.flow_type & TXGBE_ATR_L4TYPE_MASK)
mask.formatted.vlan_id = htons(0xFFFF);
else
mask.formatted.vlan_id = htons(0xFFF8);
/* determine if we need to drop or route the packet */
if (fsp->ring_cookie == RX_CLS_FLOW_DISC)
input->action = TXGBE_RDB_FDIR_DROP_QUEUE;
else
input->action = fsp->ring_cookie;
spin_lock(&txgbe->fdir_perfect_lock);
if (hlist_empty(&txgbe->fdir_filter_list)) {
/* save mask and program input mask into HW */
memcpy(&txgbe->fdir_mask, &mask, sizeof(mask));
err = txgbe_fdir_set_input_mask(wx, &mask);
if (err)
goto err_unlock;
} else if (memcmp(&txgbe->fdir_mask, &mask, sizeof(mask))) {
wx_err(wx, "Hardware only supports one mask per port. To change the mask you must first delete all the rules.\n");
goto err_unlock;
}
/* apply mask and compute/store hash */
txgbe_atr_compute_perfect_hash(&input->filter, &mask);
/* check if new entry does not exist on filter list */
if (txgbe_match_ethtool_fdir_entry(txgbe, input))
goto err_unlock;
/* only program filters to hardware if the net device is running, as
* we store the filters in the Rx buffer which is not allocated when
* the device is down
*/
if (netif_running(wx->netdev)) {
err = txgbe_fdir_write_perfect_filter(wx, &input->filter,
input->sw_idx, queue);
if (err)
goto err_unlock;
}
txgbe_update_ethtool_fdir_entry(txgbe, input, input->sw_idx);
spin_unlock(&txgbe->fdir_perfect_lock);
return 0;
err_unlock:
spin_unlock(&txgbe->fdir_perfect_lock);
err_out:
kfree(input);
return err;
}
static int txgbe_del_ethtool_fdir_entry(struct txgbe *txgbe,
struct ethtool_rxnfc *cmd)
{
struct ethtool_rx_flow_spec *fsp =
(struct ethtool_rx_flow_spec *)&cmd->fs;
int err = 0;
spin_lock(&txgbe->fdir_perfect_lock);
err = txgbe_update_ethtool_fdir_entry(txgbe, NULL, fsp->location);
spin_unlock(&txgbe->fdir_perfect_lock);
return err;
}
static int txgbe_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
{
struct wx *wx = netdev_priv(dev);
struct txgbe *txgbe = wx->priv;
int ret = -EOPNOTSUPP;
switch (cmd->cmd) {
case ETHTOOL_SRXCLSRLINS:
ret = txgbe_add_ethtool_fdir_entry(txgbe, cmd);
break;
case ETHTOOL_SRXCLSRLDEL:
ret = txgbe_del_ethtool_fdir_entry(txgbe, cmd);
break;
default:
break;
}
return ret;
}
static int
txgbe_get_module_eeprom_by_page(struct net_device *netdev,
const struct ethtool_module_eeprom *page_data,
struct netlink_ext_ack *extack)
{
struct wx *wx = netdev_priv(netdev);
struct txgbe_hic_i2c_read buffer;
int err;
if (!test_bit(WX_FLAG_SWFW_RING, wx->flags))
return -EOPNOTSUPP;
buffer.length = cpu_to_be32(page_data->length);
buffer.offset = cpu_to_be32(page_data->offset);
buffer.page = page_data->page;
buffer.bank = page_data->bank;
buffer.i2c_address = page_data->i2c_address;
err = txgbe_read_eeprom_hostif(wx, &buffer, page_data->length,
page_data->data);
if (err) {
wx_err(wx, "Failed to read module EEPROM\n");
return err;
}
return page_data->length;
}
static const struct ethtool_ops txgbe_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ |
ETHTOOL_COALESCE_USE_ADAPTIVE,
.get_drvinfo = wx_get_drvinfo,
.nway_reset = wx_nway_reset,
.get_link = ethtool_op_get_link,
.get_link_ksettings = txgbe_get_link_ksettings,
.set_link_ksettings = wx_set_link_ksettings,
.get_sset_count = wx_get_sset_count,
.get_strings = wx_get_strings,
.get_ethtool_stats = wx_get_ethtool_stats,
.get_eth_mac_stats = wx_get_mac_stats,
.get_pause_stats = wx_get_pause_stats,
.get_pauseparam = wx_get_pauseparam,
.set_pauseparam = wx_set_pauseparam,
.get_ringparam = wx_get_ringparam,
.set_ringparam = txgbe_set_ringparam,
.get_coalesce = wx_get_coalesce,
.set_coalesce = wx_set_coalesce,
.get_channels = wx_get_channels,
.set_channels = wx_set_channels,
.get_rxnfc = txgbe_get_rxnfc,
.set_rxnfc = txgbe_set_rxnfc,
.get_rx_ring_count = txgbe_get_rx_ring_count,
.get_rxfh_fields = wx_get_rxfh_fields,
.set_rxfh_fields = wx_set_rxfh_fields,
.get_rxfh_indir_size = wx_rss_indir_size,
.get_rxfh_key_size = wx_get_rxfh_key_size,
.get_rxfh = wx_get_rxfh,
.set_rxfh = wx_set_rxfh,
.get_msglevel = wx_get_msglevel,
.set_msglevel = wx_set_msglevel,
.get_ts_info = wx_get_ts_info,
.get_ts_stats = wx_get_ptp_stats,
.get_module_eeprom_by_page = txgbe_get_module_eeprom_by_page,
};
void txgbe_set_ethtool_ops(struct net_device *netdev)
{
netdev->ethtool_ops = &txgbe_ethtool_ops;
}
@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2015 - 2023 Beijing WangXun Technology Co., Ltd. */
#ifndef _TXGBE_ETHTOOL_H_
#define _TXGBE_ETHTOOL_H_
int txgbe_get_link_ksettings(struct net_device *netdev,
struct ethtool_link_ksettings *cmd);
void txgbe_set_ethtool_ops(struct net_device *netdev);
#endif /* _TXGBE_ETHTOOL_H_ */
@@ -0,0 +1,648 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2015 - 2024 Beijing WangXun Technology Co., Ltd. */
#include <linux/string.h>
#include <linux/types.h>
#include <linux/pci.h>
#include "../libwx/wx_type.h"
#include "../libwx/wx_lib.h"
#include "../libwx/wx_hw.h"
#include "txgbe_type.h"
#include "txgbe_fdir.h"
/* These defines allow us to quickly generate all of the necessary instructions
* in the function below by simply calling out TXGBE_COMPUTE_SIG_HASH_ITERATION
* for values 0 through 15
*/
#define TXGBE_ATR_COMMON_HASH_KEY \
(TXGBE_ATR_BUCKET_HASH_KEY & TXGBE_ATR_SIGNATURE_HASH_KEY)
#define TXGBE_COMPUTE_SIG_HASH_ITERATION(_n) \
do { \
u32 n = (_n); \
if (TXGBE_ATR_COMMON_HASH_KEY & (0x01 << n)) \
common_hash ^= lo_hash_dword >> n; \
else if (TXGBE_ATR_BUCKET_HASH_KEY & (0x01 << n)) \
bucket_hash ^= lo_hash_dword >> n; \
else if (TXGBE_ATR_SIGNATURE_HASH_KEY & (0x01 << n)) \
sig_hash ^= lo_hash_dword << (16 - n); \
if (TXGBE_ATR_COMMON_HASH_KEY & (0x01 << (n + 16))) \
common_hash ^= hi_hash_dword >> n; \
else if (TXGBE_ATR_BUCKET_HASH_KEY & (0x01 << (n + 16))) \
bucket_hash ^= hi_hash_dword >> n; \
else if (TXGBE_ATR_SIGNATURE_HASH_KEY & (0x01 << (n + 16))) \
sig_hash ^= hi_hash_dword << (16 - n); \
} while (0)
/**
* txgbe_atr_compute_sig_hash - Compute the signature hash
* @input: input bitstream to compute the hash on
* @common: compressed common input dword
* @hash: pointer to the computed hash
*
* This function is almost identical to the function above but contains
* several optimizations such as unwinding all of the loops, letting the
* compiler work out all of the conditional ifs since the keys are static
* defines, and computing two keys at once since the hashed dword stream
* will be the same for both keys.
**/
static void txgbe_atr_compute_sig_hash(union txgbe_atr_hash_dword input,
union txgbe_atr_hash_dword common,
u32 *hash)
{
u32 sig_hash = 0, bucket_hash = 0, common_hash = 0;
u32 hi_hash_dword, lo_hash_dword, flow_vm_vlan;
u32 i;
/* record the flow_vm_vlan bits as they are a key part to the hash */
flow_vm_vlan = ntohl(input.dword);
/* generate common hash dword */
hi_hash_dword = ntohl(common.dword);
/* low dword is word swapped version of common */
lo_hash_dword = (hi_hash_dword >> 16) | (hi_hash_dword << 16);
/* apply flow ID/VM pool/VLAN ID bits to hash words */
hi_hash_dword ^= flow_vm_vlan ^ (flow_vm_vlan >> 16);
/* Process bits 0 and 16 */
TXGBE_COMPUTE_SIG_HASH_ITERATION(0);
/* apply flow ID/VM pool/VLAN ID bits to lo hash dword, we had to
* delay this because bit 0 of the stream should not be processed
* so we do not add the VLAN until after bit 0 was processed
*/
lo_hash_dword ^= flow_vm_vlan ^ (flow_vm_vlan << 16);
/* Process remaining 30 bit of the key */
for (i = 1; i <= 15; i++)
TXGBE_COMPUTE_SIG_HASH_ITERATION(i);
/* combine common_hash result with signature and bucket hashes */
bucket_hash ^= common_hash;
bucket_hash &= TXGBE_ATR_HASH_MASK;
sig_hash ^= common_hash << 16;
sig_hash &= TXGBE_ATR_HASH_MASK << 16;
/* return completed signature hash */
*hash = sig_hash ^ bucket_hash;
}
#define TXGBE_COMPUTE_BKT_HASH_ITERATION(_n) \
do { \
u32 n = (_n); \
if (TXGBE_ATR_BUCKET_HASH_KEY & (0x01 << n)) \
bucket_hash ^= lo_hash_dword >> n; \
if (TXGBE_ATR_BUCKET_HASH_KEY & (0x01 << (n + 16))) \
bucket_hash ^= hi_hash_dword >> n; \
} while (0)
/**
* txgbe_atr_compute_perfect_hash - Compute the perfect filter hash
* @input: input bitstream to compute the hash on
* @input_mask: mask for the input bitstream
*
* This function serves two main purposes. First it applies the input_mask
* to the atr_input resulting in a cleaned up atr_input data stream.
* Secondly it computes the hash and stores it in the bkt_hash field at
* the end of the input byte stream. This way it will be available for
* future use without needing to recompute the hash.
**/
void txgbe_atr_compute_perfect_hash(union txgbe_atr_input *input,
union txgbe_atr_input *input_mask)
{
u32 hi_hash_dword, lo_hash_dword, flow_vm_vlan;
u32 bucket_hash = 0;
__be32 hi_dword = 0;
u32 i = 0;
/* Apply masks to input data */
for (i = 0; i < 11; i++)
input->dword_stream[i] &= input_mask->dword_stream[i];
/* record the flow_vm_vlan bits as they are a key part to the hash */
flow_vm_vlan = ntohl(input->dword_stream[0]);
/* generate common hash dword */
for (i = 1; i <= 10; i++)
hi_dword ^= input->dword_stream[i];
hi_hash_dword = ntohl(hi_dword);
/* low dword is word swapped version of common */
lo_hash_dword = (hi_hash_dword >> 16) | (hi_hash_dword << 16);
/* apply flow ID/VM pool/VLAN ID bits to hash words */
hi_hash_dword ^= flow_vm_vlan ^ (flow_vm_vlan >> 16);
/* Process bits 0 and 16 */
TXGBE_COMPUTE_BKT_HASH_ITERATION(0);
/* apply flow ID/VM pool/VLAN ID bits to lo hash dword, we had to
* delay this because bit 0 of the stream should not be processed
* so we do not add the VLAN until after bit 0 was processed
*/
lo_hash_dword ^= flow_vm_vlan ^ (flow_vm_vlan << 16);
/* Process remaining 30 bit of the key */
for (i = 1; i <= 15; i++)
TXGBE_COMPUTE_BKT_HASH_ITERATION(i);
/* Limit hash to 13 bits since max bucket count is 8K.
* Store result at the end of the input stream.
*/
input->formatted.bkt_hash = (__force __be16)(bucket_hash & 0x1FFF);
}
static int txgbe_fdir_check_cmd_complete(struct wx *wx)
{
u32 val;
return read_poll_timeout_atomic(rd32, val,
!(val & TXGBE_RDB_FDIR_CMD_CMD_MASK),
10, 100, false,
wx, TXGBE_RDB_FDIR_CMD);
}
/**
* txgbe_fdir_add_signature_filter - Adds a signature hash filter
* @wx: pointer to hardware structure
* @input: unique input dword
* @common: compressed common input dword
* @queue: queue index to direct traffic to
*
* @return: 0 on success and negative on failure
**/
static int txgbe_fdir_add_signature_filter(struct wx *wx,
union txgbe_atr_hash_dword input,
union txgbe_atr_hash_dword common,
u8 queue)
{
u32 fdirhashcmd, fdircmd;
u8 flow_type;
int err;
/* Get the flow_type in order to program FDIRCMD properly
* lowest 2 bits are FDIRCMD.L4TYPE, third lowest bit is FDIRCMD.IPV6
* fifth is FDIRCMD.TUNNEL_FILTER
*/
flow_type = input.formatted.flow_type;
switch (flow_type) {
case TXGBE_ATR_FLOW_TYPE_TCPV4:
case TXGBE_ATR_FLOW_TYPE_UDPV4:
case TXGBE_ATR_FLOW_TYPE_SCTPV4:
case TXGBE_ATR_FLOW_TYPE_TCPV6:
case TXGBE_ATR_FLOW_TYPE_UDPV6:
case TXGBE_ATR_FLOW_TYPE_SCTPV6:
break;
default:
wx_err(wx, "Error on flow type input\n");
return -EINVAL;
}
/* configure FDIRCMD register */
fdircmd = TXGBE_RDB_FDIR_CMD_CMD_ADD_FLOW |
TXGBE_RDB_FDIR_CMD_FILTER_UPDATE |
TXGBE_RDB_FDIR_CMD_LAST | TXGBE_RDB_FDIR_CMD_QUEUE_EN;
fdircmd |= TXGBE_RDB_FDIR_CMD_FLOW_TYPE(flow_type);
fdircmd |= TXGBE_RDB_FDIR_CMD_RX_QUEUE(queue);
txgbe_atr_compute_sig_hash(input, common, &fdirhashcmd);
fdirhashcmd |= TXGBE_RDB_FDIR_HASH_BUCKET_VALID;
wr32(wx, TXGBE_RDB_FDIR_HASH, fdirhashcmd);
wr32(wx, TXGBE_RDB_FDIR_CMD, fdircmd);
wx_dbg(wx, "Tx Queue=%x hash=%x\n", queue, (u32)fdirhashcmd);
err = txgbe_fdir_check_cmd_complete(wx);
if (err)
wx_err(wx, "Flow Director command did not complete!\n");
return err;
}
void txgbe_atr(struct wx_ring *ring, struct wx_tx_buffer *first, u8 ptype)
{
union txgbe_atr_hash_dword common = { .dword = 0 };
union txgbe_atr_hash_dword input = { .dword = 0 };
struct wx_q_vector *q_vector = ring->q_vector;
struct wx_dec_ptype dptype;
union network_header {
struct ipv6hdr *ipv6;
struct iphdr *ipv4;
void *raw;
} hdr;
struct tcphdr *th;
/* if ring doesn't have a interrupt vector, cannot perform ATR */
if (!q_vector)
return;
ring->atr_count++;
dptype = wx_decode_ptype(ptype);
if (dptype.etype) {
if (WX_PTYPE_TYPL4(ptype) != WX_PTYPE_TYP_TCP)
return;
hdr.raw = (void *)skb_inner_network_header(first->skb);
th = inner_tcp_hdr(first->skb);
} else {
if (WX_PTYPE_PKT(ptype) != WX_PTYPE_PKT_IP ||
WX_PTYPE_TYPL4(ptype) != WX_PTYPE_TYP_TCP)
return;
hdr.raw = (void *)skb_network_header(first->skb);
th = tcp_hdr(first->skb);
}
/* skip this packet since it is invalid or the socket is closing */
if (!th || th->fin)
return;
/* sample on all syn packets or once every atr sample count */
if (!th->syn && ring->atr_count < ring->atr_sample_rate)
return;
/* reset sample count */
ring->atr_count = 0;
/* src and dst are inverted, think how the receiver sees them
*
* The input is broken into two sections, a non-compressed section
* containing vm_pool, vlan_id, and flow_type. The rest of the data
* is XORed together and stored in the compressed dword.
*/
input.formatted.vlan_id = htons((u16)ptype);
/* since src port and flex bytes occupy the same word XOR them together
* and write the value to source port portion of compressed dword
*/
if (first->tx_flags & WX_TX_FLAGS_SW_VLAN)
common.port.src ^= th->dest ^ first->skb->protocol;
else if (first->tx_flags & WX_TX_FLAGS_HW_VLAN)
common.port.src ^= th->dest ^ first->skb->vlan_proto;
else
common.port.src ^= th->dest ^ first->protocol;
common.port.dst ^= th->source;
if (WX_PTYPE_PKT_IPV6 & WX_PTYPE_PKT(ptype)) {
input.formatted.flow_type = TXGBE_ATR_FLOW_TYPE_TCPV6;
common.ip ^= hdr.ipv6->saddr.s6_addr32[0] ^
hdr.ipv6->saddr.s6_addr32[1] ^
hdr.ipv6->saddr.s6_addr32[2] ^
hdr.ipv6->saddr.s6_addr32[3] ^
hdr.ipv6->daddr.s6_addr32[0] ^
hdr.ipv6->daddr.s6_addr32[1] ^
hdr.ipv6->daddr.s6_addr32[2] ^
hdr.ipv6->daddr.s6_addr32[3];
} else {
input.formatted.flow_type = TXGBE_ATR_FLOW_TYPE_TCPV4;
common.ip ^= hdr.ipv4->saddr ^ hdr.ipv4->daddr;
}
/* This assumes the Rx queue and Tx queue are bound to the same CPU */
txgbe_fdir_add_signature_filter(q_vector->wx, input, common,
ring->queue_index);
}
int txgbe_fdir_set_input_mask(struct wx *wx, union txgbe_atr_input *input_mask)
{
u32 fdirm = 0, fdirtcpm = 0, flex = 0;
int index, offset;
/* Program the relevant mask registers. If src/dst_port or src/dst_addr
* are zero, then assume a full mask for that field. Also assume that
* a VLAN of 0 is unspecified, so mask that out as well. L4type
* cannot be masked out in this implementation.
*
* This also assumes IPv4 only. IPv6 masking isn't supported at this
* point in time.
*/
/* verify bucket hash is cleared on hash generation */
if (input_mask->formatted.bkt_hash)
wx_dbg(wx, "bucket hash should always be 0 in mask\n");
/* Program FDIRM and verify partial masks */
switch (input_mask->formatted.vm_pool & 0x7F) {
case 0x0:
fdirm |= TXGBE_RDB_FDIR_OTHER_MSK_POOL;
break;
case 0x7F:
break;
default:
wx_err(wx, "Error on vm pool mask\n");
return -EINVAL;
}
switch (input_mask->formatted.flow_type & TXGBE_ATR_L4TYPE_MASK) {
case 0x0:
fdirm |= TXGBE_RDB_FDIR_OTHER_MSK_L4P;
if (input_mask->formatted.dst_port ||
input_mask->formatted.src_port) {
wx_err(wx, "Error on src/dst port mask\n");
return -EINVAL;
}
break;
case TXGBE_ATR_L4TYPE_MASK:
break;
default:
wx_err(wx, "Error on flow type mask\n");
return -EINVAL;
}
/* Now mask VM pool and destination IPv6 - bits 5 and 2 */
wr32(wx, TXGBE_RDB_FDIR_OTHER_MSK, fdirm);
index = VMDQ_P(0) / 4;
offset = VMDQ_P(0) % 4;
flex = rd32(wx, TXGBE_RDB_FDIR_FLEX_CFG(index));
flex &= ~(TXGBE_RDB_FDIR_FLEX_CFG_FIELD0 << (offset * 8));
flex |= (TXGBE_RDB_FDIR_FLEX_CFG_BASE_MAC |
TXGBE_RDB_FDIR_FLEX_CFG_OFST(0x6)) << (offset * 8);
switch ((__force u16)input_mask->formatted.flex_bytes & 0xFFFF) {
case 0x0000:
/* Mask Flex Bytes */
flex |= TXGBE_RDB_FDIR_FLEX_CFG_MSK << (offset * 8);
break;
case 0xFFFF:
break;
default:
wx_err(wx, "Error on flexible byte mask\n");
return -EINVAL;
}
wr32(wx, TXGBE_RDB_FDIR_FLEX_CFG(index), flex);
/* store the TCP/UDP port masks, bit reversed from port layout */
fdirtcpm = ntohs(input_mask->formatted.dst_port);
fdirtcpm <<= TXGBE_RDB_FDIR_PORT_DESTINATION_SHIFT;
fdirtcpm |= ntohs(input_mask->formatted.src_port);
/* write both the same so that UDP and TCP use the same mask */
wr32(wx, TXGBE_RDB_FDIR_TCP_MSK, ~fdirtcpm);
wr32(wx, TXGBE_RDB_FDIR_UDP_MSK, ~fdirtcpm);
wr32(wx, TXGBE_RDB_FDIR_SCTP_MSK, ~fdirtcpm);
/* store source and destination IP masks (little-enian) */
wr32(wx, TXGBE_RDB_FDIR_SA4_MSK,
ntohl(~input_mask->formatted.src_ip[0]));
wr32(wx, TXGBE_RDB_FDIR_DA4_MSK,
ntohl(~input_mask->formatted.dst_ip[0]));
return 0;
}
int txgbe_fdir_write_perfect_filter(struct wx *wx,
union txgbe_atr_input *input,
u16 soft_id, u8 queue)
{
u32 fdirport, fdirvlan, fdirhash, fdircmd;
int err = 0;
/* currently IPv6 is not supported, must be programmed with 0 */
wr32(wx, TXGBE_RDB_FDIR_IP6(2), ntohl(input->formatted.src_ip[0]));
wr32(wx, TXGBE_RDB_FDIR_IP6(1), ntohl(input->formatted.src_ip[1]));
wr32(wx, TXGBE_RDB_FDIR_IP6(0), ntohl(input->formatted.src_ip[2]));
/* record the source address (little-endian) */
wr32(wx, TXGBE_RDB_FDIR_SA, ntohl(input->formatted.src_ip[0]));
/* record the first 32 bits of the destination address
* (little-endian)
*/
wr32(wx, TXGBE_RDB_FDIR_DA, ntohl(input->formatted.dst_ip[0]));
/* record source and destination port (little-endian)*/
fdirport = ntohs(input->formatted.dst_port);
fdirport <<= TXGBE_RDB_FDIR_PORT_DESTINATION_SHIFT;
fdirport |= ntohs(input->formatted.src_port);
wr32(wx, TXGBE_RDB_FDIR_PORT, fdirport);
/* record packet type and flex_bytes (little-endian) */
fdirvlan = ntohs(input->formatted.flex_bytes);
fdirvlan <<= TXGBE_RDB_FDIR_FLEX_FLEX_SHIFT;
fdirvlan |= ntohs(input->formatted.vlan_id);
wr32(wx, TXGBE_RDB_FDIR_FLEX, fdirvlan);
/* configure FDIRHASH register */
fdirhash = (__force u32)input->formatted.bkt_hash |
TXGBE_RDB_FDIR_HASH_BUCKET_VALID |
TXGBE_RDB_FDIR_HASH_SIG_SW_INDEX(soft_id);
wr32(wx, TXGBE_RDB_FDIR_HASH, fdirhash);
/* flush all previous writes to make certain registers are
* programmed prior to issuing the command
*/
WX_WRITE_FLUSH(wx);
/* configure FDIRCMD register */
fdircmd = TXGBE_RDB_FDIR_CMD_CMD_ADD_FLOW |
TXGBE_RDB_FDIR_CMD_FILTER_UPDATE |
TXGBE_RDB_FDIR_CMD_LAST | TXGBE_RDB_FDIR_CMD_QUEUE_EN;
if (queue == TXGBE_RDB_FDIR_DROP_QUEUE)
fdircmd |= TXGBE_RDB_FDIR_CMD_DROP;
fdircmd |= TXGBE_RDB_FDIR_CMD_FLOW_TYPE(input->formatted.flow_type);
fdircmd |= TXGBE_RDB_FDIR_CMD_RX_QUEUE(queue);
fdircmd |= TXGBE_RDB_FDIR_CMD_VT_POOL(input->formatted.vm_pool);
wr32(wx, TXGBE_RDB_FDIR_CMD, fdircmd);
err = txgbe_fdir_check_cmd_complete(wx);
if (err)
wx_err(wx, "Flow Director command did not complete!\n");
return err;
}
int txgbe_fdir_erase_perfect_filter(struct wx *wx,
union txgbe_atr_input *input,
u16 soft_id)
{
u32 fdirhash, fdircmd;
int err = 0;
/* configure FDIRHASH register */
fdirhash = (__force u32)input->formatted.bkt_hash;
fdirhash |= TXGBE_RDB_FDIR_HASH_SIG_SW_INDEX(soft_id);
wr32(wx, TXGBE_RDB_FDIR_HASH, fdirhash);
/* flush hash to HW */
WX_WRITE_FLUSH(wx);
/* Query if filter is present */
wr32(wx, TXGBE_RDB_FDIR_CMD, TXGBE_RDB_FDIR_CMD_CMD_QUERY_REM_FILT);
err = txgbe_fdir_check_cmd_complete(wx);
if (err) {
wx_err(wx, "Flow Director command did not complete!\n");
return err;
}
fdircmd = rd32(wx, TXGBE_RDB_FDIR_CMD);
/* if filter exists in hardware then remove it */
if (fdircmd & TXGBE_RDB_FDIR_CMD_FILTER_VALID) {
wr32(wx, TXGBE_RDB_FDIR_HASH, fdirhash);
WX_WRITE_FLUSH(wx);
wr32(wx, TXGBE_RDB_FDIR_CMD,
TXGBE_RDB_FDIR_CMD_CMD_REMOVE_FLOW);
}
return 0;
}
/**
* txgbe_fdir_enable - Initialize Flow Director control registers
* @wx: pointer to hardware structure
* @fdirctrl: value to write to flow director control register
**/
static void txgbe_fdir_enable(struct wx *wx, u32 fdirctrl)
{
u32 val;
int ret;
/* Prime the keys for hashing */
wr32(wx, TXGBE_RDB_FDIR_HKEY, TXGBE_ATR_BUCKET_HASH_KEY);
wr32(wx, TXGBE_RDB_FDIR_SKEY, TXGBE_ATR_SIGNATURE_HASH_KEY);
wr32(wx, TXGBE_RDB_FDIR_CTL, fdirctrl);
WX_WRITE_FLUSH(wx);
ret = read_poll_timeout(rd32, val, val & TXGBE_RDB_FDIR_CTL_INIT_DONE,
1000, 10000, false, wx, TXGBE_RDB_FDIR_CTL);
if (ret < 0)
wx_dbg(wx, "Flow Director poll time exceeded!\n");
}
/**
* txgbe_init_fdir_signature -Initialize Flow Director sig filters
* @wx: pointer to hardware structure
**/
static void txgbe_init_fdir_signature(struct wx *wx)
{
u32 fdirctrl = TXGBE_FDIR_PBALLOC_64K;
int index = VMDQ_P(0) / 4;
int offset = VMDQ_P(0) % 4;
u32 flex = 0;
flex = rd32(wx, TXGBE_RDB_FDIR_FLEX_CFG(index));
flex &= ~(TXGBE_RDB_FDIR_FLEX_CFG_FIELD0 << (offset * 8));
flex |= (TXGBE_RDB_FDIR_FLEX_CFG_BASE_MAC |
TXGBE_RDB_FDIR_FLEX_CFG_OFST(0x6)) << (offset * 8);
wr32(wx, TXGBE_RDB_FDIR_FLEX_CFG(index), flex);
/* Continue setup of fdirctrl register bits:
* Move the flexible bytes to use the ethertype - shift 6 words
* Set the maximum length per hash bucket to 0xA filters
* Send interrupt when 64 filters are left
*/
fdirctrl |= TXGBE_RDB_FDIR_CTL_HASH_BITS(0xF) |
TXGBE_RDB_FDIR_CTL_MAX_LENGTH(0xA) |
TXGBE_RDB_FDIR_CTL_FULL_THRESH(4);
/* write hashes and fdirctrl register, poll for completion */
txgbe_fdir_enable(wx, fdirctrl);
}
/**
* txgbe_init_fdir_perfect - Initialize Flow Director perfect filters
* @wx: pointer to hardware structure
**/
static void txgbe_init_fdir_perfect(struct wx *wx)
{
u32 fdirctrl = TXGBE_FDIR_PBALLOC_64K;
/* Continue setup of fdirctrl register bits:
* Turn perfect match filtering on
* Report hash in RSS field of Rx wb descriptor
* Initialize the drop queue
* Move the flexible bytes to use the ethertype - shift 6 words
* Set the maximum length per hash bucket to 0xA filters
* Send interrupt when 64 (0x4 * 16) filters are left
*/
fdirctrl |= TXGBE_RDB_FDIR_CTL_PERFECT_MATCH |
TXGBE_RDB_FDIR_CTL_DROP_Q(TXGBE_RDB_FDIR_DROP_QUEUE) |
TXGBE_RDB_FDIR_CTL_HASH_BITS(0xF) |
TXGBE_RDB_FDIR_CTL_MAX_LENGTH(0xA) |
TXGBE_RDB_FDIR_CTL_FULL_THRESH(4);
/* write hashes and fdirctrl register, poll for completion */
txgbe_fdir_enable(wx, fdirctrl);
}
static void txgbe_fdir_filter_restore(struct wx *wx)
{
struct txgbe_fdir_filter *filter;
struct txgbe *txgbe = wx->priv;
struct hlist_node *node;
u8 queue = 0;
int ret = 0;
spin_lock(&txgbe->fdir_perfect_lock);
if (!hlist_empty(&txgbe->fdir_filter_list))
ret = txgbe_fdir_set_input_mask(wx, &txgbe->fdir_mask);
if (ret)
goto unlock;
hlist_for_each_entry_safe(filter, node,
&txgbe->fdir_filter_list, fdir_node) {
if (filter->action == TXGBE_RDB_FDIR_DROP_QUEUE) {
queue = TXGBE_RDB_FDIR_DROP_QUEUE;
} else {
u32 ring = ethtool_get_flow_spec_ring(filter->action);
if (ring >= wx->num_rx_queues) {
wx_err(wx, "FDIR restore failed, ring:%u\n",
ring);
continue;
}
/* Map the ring onto the absolute queue index */
queue = wx->rx_ring[ring]->reg_idx;
}
ret = txgbe_fdir_write_perfect_filter(wx,
&filter->filter,
filter->sw_idx,
queue);
if (ret)
wx_err(wx, "FDIR restore failed, index:%u\n",
filter->sw_idx);
}
unlock:
spin_unlock(&txgbe->fdir_perfect_lock);
}
void txgbe_configure_fdir(struct wx *wx)
{
wx_disable_sec_rx_path(wx);
if (test_bit(WX_FLAG_FDIR_HASH, wx->flags)) {
txgbe_init_fdir_signature(wx);
} else if (test_bit(WX_FLAG_FDIR_PERFECT, wx->flags)) {
txgbe_init_fdir_perfect(wx);
txgbe_fdir_filter_restore(wx);
}
wx_enable_sec_rx_path(wx);
}
void txgbe_fdir_filter_exit(struct wx *wx)
{
struct txgbe_fdir_filter *filter;
struct txgbe *txgbe = wx->priv;
struct hlist_node *node;
spin_lock(&txgbe->fdir_perfect_lock);
hlist_for_each_entry_safe(filter, node,
&txgbe->fdir_filter_list, fdir_node) {
hlist_del(&filter->fdir_node);
kfree(filter);
}
txgbe->fdir_filter_count = 0;
spin_unlock(&txgbe->fdir_perfect_lock);
}
@@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2015 - 2024 Beijing WangXun Technology Co., Ltd. */
#ifndef _TXGBE_FDIR_H_
#define _TXGBE_FDIR_H_
void txgbe_atr_compute_perfect_hash(union txgbe_atr_input *input,
union txgbe_atr_input *input_mask);
void txgbe_atr(struct wx_ring *ring, struct wx_tx_buffer *first, u8 ptype);
int txgbe_fdir_set_input_mask(struct wx *wx, union txgbe_atr_input *input_mask);
int txgbe_fdir_write_perfect_filter(struct wx *wx,
union txgbe_atr_input *input,
u16 soft_id, u8 queue);
int txgbe_fdir_erase_perfect_filter(struct wx *wx,
union txgbe_atr_input *input,
u16 soft_id);
void txgbe_configure_fdir(struct wx *wx);
void txgbe_fdir_filter_exit(struct wx *wx);
#endif /* _TXGBE_FDIR_H_ */
@@ -0,0 +1,227 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2015 - 2022 Beijing WangXun Technology Co., Ltd. */
#include <linux/etherdevice.h>
#include <linux/if_ether.h>
#include <linux/string.h>
#include <linux/iopoll.h>
#include <linux/types.h>
#include <linux/pci.h>
#include "../libwx/wx_type.h"
#include "../libwx/wx_hw.h"
#include "txgbe_type.h"
#include "txgbe_hw.h"
/**
* txgbe_disable_sec_tx_path - Stops the transmit data path
* @wx: pointer to hardware structure
*
* Stops the transmit data path and waits for the HW to internally empty
* the tx security block
**/
int txgbe_disable_sec_tx_path(struct wx *wx)
{
int val;
wr32m(wx, WX_TSC_CTL, WX_TSC_CTL_TX_DIS, WX_TSC_CTL_TX_DIS);
return read_poll_timeout(rd32, val, val & WX_TSC_ST_SECTX_RDY,
1000, 20000, false, wx, WX_TSC_ST);
}
/**
* txgbe_enable_sec_tx_path - Enables the transmit data path
* @wx: pointer to hardware structure
*
* Enables the transmit data path.
**/
void txgbe_enable_sec_tx_path(struct wx *wx)
{
wr32m(wx, WX_TSC_CTL, WX_TSC_CTL_TX_DIS, 0);
WX_WRITE_FLUSH(wx);
}
/**
* txgbe_init_thermal_sensor_thresh - Inits thermal sensor thresholds
* @wx: pointer to hardware structure
*
* Inits the thermal sensor thresholds according to the NVM map
* and save off the threshold and location values into mac.thermal_sensor_data
**/
static void txgbe_init_thermal_sensor_thresh(struct wx *wx)
{
struct wx_thermal_sensor_data *data = &wx->mac.sensor;
memset(data, 0, sizeof(struct wx_thermal_sensor_data));
/* Only support thermal sensors attached to SP physical port 0 */
if (wx->bus.func)
return;
wr32(wx, TXGBE_TS_CTL, TXGBE_TS_CTL_EVAL_MD);
wr32(wx, WX_TS_INT_EN,
WX_TS_INT_EN_ALARM_INT_EN | WX_TS_INT_EN_DALARM_INT_EN);
wr32(wx, WX_TS_EN, WX_TS_EN_ENA);
data->alarm_thresh = 100;
wr32(wx, WX_TS_ALARM_THRE, 677);
data->dalarm_thresh = 90;
wr32(wx, WX_TS_DALARM_THRE, 614);
}
/**
* txgbe_calc_eeprom_checksum - Calculates and returns the checksum
* @wx: pointer to hardware structure
* @checksum: pointer to cheksum
*
* Returns a negative error code on error
**/
static int txgbe_calc_eeprom_checksum(struct wx *wx, u16 *checksum)
{
u16 *eeprom_ptrs = NULL;
u16 *local_buffer;
int status;
u16 i;
wx_init_eeprom_params(wx);
eeprom_ptrs = kvmalloc_array(TXGBE_EEPROM_LAST_WORD, sizeof(u16),
GFP_KERNEL);
if (!eeprom_ptrs)
return -ENOMEM;
/* Read pointer area */
status = wx_read_ee_hostif_buffer(wx, 0, TXGBE_EEPROM_LAST_WORD, eeprom_ptrs);
if (status != 0) {
wx_err(wx, "Failed to read EEPROM image\n");
kvfree(eeprom_ptrs);
return status;
}
local_buffer = eeprom_ptrs;
for (i = 0; i < TXGBE_EEPROM_LAST_WORD; i++) {
if (wx->mac.type == wx_mac_aml) {
if (i >= TXGBE_EEPROM_I2C_SRART_PTR &&
i < TXGBE_EEPROM_I2C_END_PTR)
local_buffer[i] = 0xffff;
}
if (i != wx->eeprom.sw_region_offset + TXGBE_EEPROM_CHECKSUM)
*checksum += local_buffer[i];
}
kvfree(eeprom_ptrs);
*checksum = TXGBE_EEPROM_SUM - *checksum;
return 0;
}
/**
* txgbe_validate_eeprom_checksum - Validate EEPROM checksum
* @wx: pointer to hardware structure
* @checksum_val: calculated checksum
*
* Performs checksum calculation and validates the EEPROM checksum. If the
* caller does not need checksum_val, the value can be NULL.
**/
int txgbe_validate_eeprom_checksum(struct wx *wx, u16 *checksum_val)
{
u16 read_checksum = 0;
u16 checksum;
int status;
/* Read the first word from the EEPROM. If this times out or fails, do
* not continue or we could be in for a very long wait while every
* EEPROM read fails
*/
status = wx_read_ee_hostif(wx, 0, &checksum);
if (status) {
wx_err(wx, "EEPROM read failed\n");
return status;
}
checksum = 0;
status = txgbe_calc_eeprom_checksum(wx, &checksum);
if (status != 0)
return status;
status = wx_read_ee_hostif(wx, wx->eeprom.sw_region_offset +
TXGBE_EEPROM_CHECKSUM, &read_checksum);
if (status != 0)
return status;
/* Verify read checksum from EEPROM is the same as
* calculated checksum
*/
if (read_checksum != checksum) {
status = -EIO;
wx_err(wx, "Invalid EEPROM checksum\n");
}
/* If the user cares, return the calculated checksum */
if (checksum_val)
*checksum_val = checksum;
return status;
}
static void txgbe_reset_misc(struct wx *wx)
{
wx_reset_misc(wx);
txgbe_init_thermal_sensor_thresh(wx);
}
/**
* txgbe_reset_hw - Perform hardware reset
* @wx: pointer to wx structure
*
* Resets the hardware by resetting the transmit and receive units, masks
* and clears all interrupts, perform a PHY reset, and perform a link (MAC)
* reset.
**/
int txgbe_reset_hw(struct wx *wx)
{
int status;
/* Call adapter stop to disable tx/rx and clear interrupts */
status = wx_stop_adapter(wx);
if (status != 0)
return status;
if (wx->media_type != wx_media_copper) {
u32 val;
val = WX_MIS_RST_LAN_RST(wx->bus.func);
wr32(wx, WX_MIS_RST, val | rd32(wx, WX_MIS_RST));
WX_WRITE_FLUSH(wx);
usleep_range(10, 100);
}
status = wx_check_flash_load(wx, TXGBE_SPI_ILDR_STATUS_LAN_SW_RST(wx->bus.func));
if (status != 0)
return status;
txgbe_reset_misc(wx);
if (wx->mac.type != wx_mac_sp) {
wr32(wx, TXGBE_PX_PF_BME, 0x1);
wr32m(wx, TXGBE_RDM_RSC_CTL, TXGBE_RDM_RSC_CTL_FREE_CTL,
TXGBE_RDM_RSC_CTL_FREE_CTL);
}
wx_clear_hw_cntrs(wx);
/* Store the permanent mac address */
wx_get_mac_addr(wx, wx->mac.perm_addr);
/* Store MAC address from RAR0, clear receive address registers, and
* clear the multicast table. Also reset num_rar_entries to 128,
* since we modify this value when programming the SAN MAC address.
*/
wx->mac.num_rar_entries = TXGBE_RAR_ENTRIES;
wx_init_rx_addrs(wx);
pci_set_master(wx->pdev);
return 0;
}
@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2015 - 2022 Beijing WangXun Technology Co., Ltd. */
#ifndef _TXGBE_HW_H_
#define _TXGBE_HW_H_
int txgbe_disable_sec_tx_path(struct wx *wx);
void txgbe_enable_sec_tx_path(struct wx *wx);
int txgbe_validate_eeprom_checksum(struct wx *wx, u16 *checksum_val);
int txgbe_reset_hw(struct wx *wx);
#endif /* _TXGBE_HW_H_ */
@@ -0,0 +1,268 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2015 - 2024 Beijing WangXun Technology Co., Ltd. */
#include <linux/irqdomain.h>
#include <linux/pci.h>
#include "../libwx/wx_type.h"
#include "../libwx/wx_lib.h"
#include "../libwx/wx_ptp.h"
#include "../libwx/wx_hw.h"
#include "../libwx/wx_sriov.h"
#include "txgbe_type.h"
#include "txgbe_phy.h"
#include "txgbe_irq.h"
#include "txgbe_aml.h"
/**
* txgbe_irq_enable - Enable default interrupt generation settings
* @wx: pointer to private structure
* @queues: enable irqs for queues
**/
void txgbe_irq_enable(struct wx *wx, bool queues)
{
u32 misc_ien = TXGBE_PX_MISC_IEN_MASK;
if (wx->mac.type != wx_mac_sp) {
misc_ien |= TXGBE_PX_MISC_GPIO;
txgbe_gpio_init_aml(wx);
}
wr32(wx, WX_PX_MISC_IEN, misc_ien);
/* unmask interrupt */
wx_intr_enable(wx, TXGBE_INTR_MISC(wx));
if (queues)
wx_intr_enable(wx, TXGBE_INTR_QALL(wx));
}
/**
* txgbe_request_queue_irqs - Initialize MSI-X queue interrupts
* @wx: board private structure
*
* Allocate MSI-X queue vectors and request interrupts from the kernel.
**/
int txgbe_request_queue_irqs(struct wx *wx)
{
struct net_device *netdev = wx->netdev;
int vector, err;
if (!wx->pdev->msix_enabled)
return 0;
for (vector = 0; vector < wx->num_q_vectors; vector++) {
struct wx_q_vector *q_vector = wx->q_vector[vector];
struct msix_entry *entry = &wx->msix_q_entries[vector];
if (q_vector->tx.ring && q_vector->rx.ring)
snprintf(q_vector->name, sizeof(q_vector->name) - 1,
"%s-TxRx-%d", netdev->name, entry->entry);
else
/* skip this unused q_vector */
continue;
err = request_irq(entry->vector, wx_msix_clean_rings, 0,
q_vector->name, q_vector);
if (err) {
wx_err(wx, "request_irq failed for MSIX interrupt %s Error: %d\n",
q_vector->name, err);
goto free_queue_irqs;
}
}
return 0;
free_queue_irqs:
while (vector) {
vector--;
free_irq(wx->msix_q_entries[vector].vector,
wx->q_vector[vector]);
}
return err;
}
static int txgbe_request_link_irq(struct txgbe *txgbe)
{
txgbe->link_irq = irq_find_mapping(txgbe->misc.domain, TXGBE_IRQ_LINK);
return request_threaded_irq(txgbe->link_irq, NULL,
txgbe_link_irq_handler,
IRQF_ONESHOT, "txgbe-link-irq", txgbe);
}
static int txgbe_request_gpio_irq(struct txgbe *txgbe)
{
txgbe->gpio_irq = irq_find_mapping(txgbe->misc.domain, TXGBE_IRQ_GPIO);
return request_threaded_irq(txgbe->gpio_irq, NULL,
txgbe_gpio_irq_handler_aml,
IRQF_ONESHOT, "txgbe-gpio-irq", txgbe);
}
static const struct irq_chip txgbe_irq_chip = {
.name = "txgbe-misc-irq",
};
static int txgbe_misc_irq_domain_map(struct irq_domain *d,
unsigned int irq,
irq_hw_number_t hwirq)
{
struct txgbe *txgbe = d->host_data;
irq_set_chip_data(irq, txgbe);
irq_set_chip(irq, &txgbe->misc.chip);
irq_set_nested_thread(irq, true);
irq_set_noprobe(irq);
return 0;
}
static const struct irq_domain_ops txgbe_misc_irq_domain_ops = {
.map = txgbe_misc_irq_domain_map,
};
static irqreturn_t txgbe_misc_irq_handle(int irq, void *data)
{
struct wx_q_vector *q_vector;
struct txgbe *txgbe = data;
struct wx *wx = txgbe->wx;
u32 eicr;
if (wx->pdev->msix_enabled) {
eicr = wx_misc_isb(wx, WX_ISB_MISC);
txgbe->eicr = eicr;
if (eicr & TXGBE_PX_MISC_IC_VF_MBOX) {
wx_msg_task(txgbe->wx);
wx_intr_enable(wx, TXGBE_INTR_MISC(wx));
}
return IRQ_WAKE_THREAD;
}
eicr = wx_misc_isb(wx, WX_ISB_VEC0);
if (!eicr) {
/* shared interrupt alert!
* the interrupt that we masked before the ICR read.
*/
if (!test_bit(WX_STATE_DOWN, wx->state))
txgbe_irq_enable(wx, true);
return IRQ_NONE; /* Not our interrupt */
}
wx->isb_mem[WX_ISB_VEC0] = 0;
if (!(wx->pdev->msi_enabled))
wr32(wx, WX_PX_INTA, 1);
/* would disable interrupts here but it is auto disabled */
q_vector = wx->q_vector[0];
napi_schedule_irqoff(&q_vector->napi);
txgbe->eicr = wx_misc_isb(wx, WX_ISB_MISC);
return IRQ_WAKE_THREAD;
}
static irqreturn_t txgbe_misc_irq_thread_fn(int irq, void *data)
{
struct txgbe *txgbe = data;
struct wx *wx = txgbe->wx;
unsigned int nhandled = 0;
unsigned int sub_irq;
u32 eicr;
eicr = txgbe->eicr;
if (eicr & (TXGBE_PX_MISC_ETH_LK | TXGBE_PX_MISC_ETH_LKDN |
TXGBE_PX_MISC_ETH_AN)) {
sub_irq = irq_find_mapping(txgbe->misc.domain, TXGBE_IRQ_LINK);
handle_nested_irq(sub_irq);
nhandled++;
}
if (eicr & TXGBE_PX_MISC_GPIO) {
sub_irq = irq_find_mapping(txgbe->misc.domain, TXGBE_IRQ_GPIO);
handle_nested_irq(sub_irq);
nhandled++;
}
if (unlikely(eicr & TXGBE_PX_MISC_IC_TIMESYNC)) {
wx_ptp_check_pps_event(wx);
nhandled++;
}
wx_intr_enable(wx, TXGBE_INTR_MISC(wx));
return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
}
static void txgbe_del_irq_domain(struct txgbe *txgbe)
{
int hwirq, virq;
for (hwirq = 0; hwirq < txgbe->misc.nirqs; hwirq++) {
virq = irq_find_mapping(txgbe->misc.domain, hwirq);
irq_dispose_mapping(virq);
}
irq_domain_remove(txgbe->misc.domain);
}
void txgbe_free_misc_irq(struct txgbe *txgbe)
{
if (txgbe->wx->mac.type != wx_mac_sp)
free_irq(txgbe->gpio_irq, txgbe);
free_irq(txgbe->link_irq, txgbe);
free_irq(txgbe->misc.irq, txgbe);
txgbe_del_irq_domain(txgbe);
txgbe->wx->misc_irq_domain = false;
}
int txgbe_setup_misc_irq(struct txgbe *txgbe)
{
unsigned long flags = IRQF_ONESHOT;
struct wx *wx = txgbe->wx;
int hwirq, err;
txgbe->misc.nirqs = TXGBE_IRQ_MAX;
txgbe->misc.domain = irq_domain_create_simple(NULL, txgbe->misc.nirqs, 0,
&txgbe_misc_irq_domain_ops, txgbe);
if (!txgbe->misc.domain)
return -ENOMEM;
for (hwirq = 0; hwirq < txgbe->misc.nirqs; hwirq++)
irq_create_mapping(txgbe->misc.domain, hwirq);
txgbe->misc.chip = txgbe_irq_chip;
if (wx->pdev->msix_enabled) {
txgbe->misc.irq = wx->msix_entry->vector;
} else {
txgbe->misc.irq = wx->pdev->irq;
if (!wx->pdev->msi_enabled)
flags |= IRQF_SHARED;
}
err = request_threaded_irq(txgbe->misc.irq, txgbe_misc_irq_handle,
txgbe_misc_irq_thread_fn,
flags,
wx->netdev->name, txgbe);
if (err)
goto del_misc_irq;
err = txgbe_request_link_irq(txgbe);
if (err)
goto free_msic_irq;
if (wx->mac.type == wx_mac_sp)
goto skip_sp_irq;
err = txgbe_request_gpio_irq(txgbe);
if (err)
goto free_link_irq;
skip_sp_irq:
wx->misc_irq_domain = true;
return 0;
free_link_irq:
free_irq(txgbe->link_irq, txgbe);
free_msic_irq:
free_irq(txgbe->misc.irq, txgbe);
del_misc_irq:
txgbe_del_irq_domain(txgbe);
return err;
}
@@ -0,0 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2015 - 2024 Beijing WangXun Technology Co., Ltd. */
void txgbe_irq_enable(struct wx *wx, bool queues);
int txgbe_request_queue_irqs(struct wx *wx);
void txgbe_free_misc_irq(struct txgbe *txgbe);
int txgbe_setup_misc_irq(struct txgbe *txgbe);
@@ -0,0 +1,980 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2015 - 2022 Beijing WangXun Technology Co., Ltd. */
#include <linux/types.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/string.h>
#include <linux/etherdevice.h>
#include <linux/phylink.h>
#include <net/udp_tunnel.h>
#include <net/ip.h>
#include <linux/if_vlan.h>
#include "../libwx/wx_type.h"
#include "../libwx/wx_lib.h"
#include "../libwx/wx_ptp.h"
#include "../libwx/wx_hw.h"
#include "../libwx/wx_mbx.h"
#include "../libwx/wx_sriov.h"
#include "txgbe_type.h"
#include "txgbe_hw.h"
#include "txgbe_phy.h"
#include "txgbe_aml.h"
#include "txgbe_irq.h"
#include "txgbe_fdir.h"
#include "txgbe_ethtool.h"
char txgbe_driver_name[] = "txgbe";
/* txgbe_pci_tbl - PCI Device ID Table
*
* Wildcard entries (PCI_ANY_ID) should come last
* Last entry must be all 0s
*
* { Vendor ID, Device ID, SubVendor ID, SubDevice ID,
* Class, Class Mask, private data (not used) }
*/
static const struct pci_device_id txgbe_pci_tbl[] = {
{ PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_SP1000) },
{ PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_WX1820) },
{ PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_AML5010) },
{ PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_AML5110) },
{ PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_AML5025) },
{ PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_AML5125) },
{ PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_AML5040) },
{ PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_AML5140) },
/* required last entry */
{ }
};
#define DEFAULT_DEBUG_LEVEL_SHIFT 3
static void txgbe_check_minimum_link(struct wx *wx)
{
struct pci_dev *pdev;
pdev = wx->pdev;
pcie_print_link_status(pdev);
}
/**
* txgbe_enumerate_functions - Get the number of ports this device has
* @wx: wx structure
*
* This function enumerates the phsyical functions co-located on a single slot,
* in order to determine how many ports a device has. This is most useful in
* determining the required GT/s of PCIe bandwidth necessary for optimal
* performance.
**/
static int txgbe_enumerate_functions(struct wx *wx)
{
struct pci_dev *entry, *pdev = wx->pdev;
int physfns = 0;
list_for_each_entry(entry, &pdev->bus->devices, bus_list) {
/* When the devices on the bus don't all match our device ID,
* we can't reliably determine the correct number of
* functions. This can occur if a function has been direct
* attached to a virtual machine using VT-d.
*/
if (entry->vendor != pdev->vendor ||
entry->device != pdev->device)
return -EINVAL;
physfns++;
}
return physfns;
}
static void txgbe_module_detection_subtask(struct wx *wx)
{
int err;
if (!test_and_clear_bit(WX_FLAG_NEED_MODULE_RESET, wx->flags))
return;
/* wait for SFF module ready */
msleep(200);
err = txgbe_identify_module(wx);
if (err == -ENODEV)
set_bit(WX_FLAG_NEED_MODULE_RESET, wx->flags);
}
static void txgbe_link_config_subtask(struct wx *wx)
{
if (!test_and_clear_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags))
return;
txgbe_set_phy_link(wx);
}
/**
* txgbe_service_task - manages and runs subtasks
* @work: pointer to work_struct containing our data
**/
static void txgbe_service_task(struct work_struct *work)
{
struct wx *wx = container_of(work, struct wx, service_task);
txgbe_module_detection_subtask(wx);
txgbe_link_config_subtask(wx);
wx_update_stats(wx);
wx_service_event_complete(wx);
}
static void txgbe_init_service(struct wx *wx)
{
timer_setup(&wx->service_timer, wx_service_timer, 0);
INIT_WORK(&wx->service_task, txgbe_service_task);
clear_bit(WX_STATE_SERVICE_SCHED, wx->state);
}
static void txgbe_up_complete(struct wx *wx)
{
struct net_device *netdev = wx->netdev;
wx_control_hw(wx, true);
wx_configure_vectors(wx);
/* make sure to complete pre-operations */
smp_mb__before_atomic();
clear_bit(WX_STATE_DOWN, wx->state);
wx_napi_enable_all(wx);
switch (wx->mac.type) {
case wx_mac_aml40:
txgbe_setup_link(wx);
phylink_start(wx->phylink);
break;
case wx_mac_aml:
/* Enable TX laser */
wr32m(wx, WX_GPIO_DR, TXGBE_GPIOBIT_1, 0);
txgbe_setup_link(wx);
phylink_start(wx->phylink);
break;
case wx_mac_sp:
phylink_start(wx->phylink);
break;
default:
break;
}
/* clear any pending interrupts, may auto mask */
rd32(wx, WX_PX_IC(0));
rd32(wx, WX_PX_IC(1));
rd32(wx, WX_PX_MISC_IC);
txgbe_irq_enable(wx, true);
/* enable transmits */
netif_tx_start_all_queues(netdev);
mod_timer(&wx->service_timer, jiffies);
/* Set PF Reset Done bit so PF/VF Mail Ops can work */
wr32m(wx, WX_CFG_PORT_CTL, WX_CFG_PORT_CTL_PFRSTD,
WX_CFG_PORT_CTL_PFRSTD);
/* update setting rx tx for all active vfs */
wx_set_all_vfs(wx);
}
static void txgbe_reset(struct wx *wx)
{
struct net_device *netdev = wx->netdev;
u8 old_addr[ETH_ALEN];
int err;
err = txgbe_reset_hw(wx);
if (err != 0)
wx_err(wx, "Hardware Error: %d\n", err);
wx_start_hw(wx);
/* do not flush user set addresses */
memcpy(old_addr, &wx->mac_table[0].addr, netdev->addr_len);
wx_flush_sw_mac_table(wx);
wx_mac_set_default_filter(wx, old_addr);
if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
wx_ptp_reset(wx);
}
static void txgbe_disable_device(struct wx *wx)
{
struct net_device *netdev = wx->netdev;
u32 i;
if (test_and_set_bit(WX_STATE_DOWN, wx->state))
return;
wx_disable_pcie_master(wx);
/* disable receives */
wx_disable_rx(wx);
/* disable all enabled rx queues */
for (i = 0; i < wx->num_rx_queues; i++)
/* this call also flushes the previous write */
wx_disable_rx_queue(wx, wx->rx_ring[i]);
netif_tx_stop_all_queues(netdev);
netif_tx_disable(netdev);
wx_irq_disable(wx);
wx_napi_disable_all(wx);
timer_delete_sync(&wx->service_timer);
cancel_work_sync(&wx->service_task);
if (wx->bus.func < 2)
wr32m(wx, TXGBE_MIS_PRB_CTL, TXGBE_MIS_PRB_CTL_LAN_UP(wx->bus.func), 0);
else
wx_err(wx, "%s: invalid bus lan id %d\n",
__func__, wx->bus.func);
if (wx->num_vfs) {
/* Clear EITR Select mapping */
wr32(wx, WX_PX_ITRSEL, 0);
/* Mark all the VFs as inactive */
for (i = 0; i < wx->num_vfs; i++)
wx->vfinfo[i].clear_to_send = 0;
/* update setting rx tx for all active vfs */
wx_set_all_vfs(wx);
}
if (!(((wx->subsystem_device_id & WX_NCSI_MASK) == WX_NCSI_SUP) ||
((wx->subsystem_device_id & WX_WOL_MASK) == WX_WOL_SUP))) {
/* disable mac transmiter */
wr32m(wx, WX_MAC_TX_CFG, WX_MAC_TX_CFG_TE, 0);
}
/* disable transmits in the hardware now that interrupts are off */
for (i = 0; i < wx->num_tx_queues; i++) {
u8 reg_idx = wx->tx_ring[i]->reg_idx;
wr32(wx, WX_PX_TR_CFG(reg_idx), WX_PX_TR_CFG_SWFLSH);
}
/* Disable the Tx DMA engine */
wr32m(wx, WX_TDM_CTL, WX_TDM_CTL_TE, 0);
}
void txgbe_down(struct wx *wx)
{
txgbe_disable_device(wx);
txgbe_reset(wx);
switch (wx->mac.type) {
case wx_mac_aml40:
phylink_stop(wx->phylink);
break;
case wx_mac_aml:
phylink_stop(wx->phylink);
/* Disable TX laser */
wr32m(wx, WX_GPIO_DR, TXGBE_GPIOBIT_1, TXGBE_GPIOBIT_1);
break;
case wx_mac_sp:
phylink_stop(wx->phylink);
break;
default:
break;
}
wx_clean_all_tx_rings(wx);
wx_clean_all_rx_rings(wx);
}
void txgbe_up(struct wx *wx)
{
wx_configure(wx);
wx_ptp_init(wx);
txgbe_up_complete(wx);
}
/**
* txgbe_init_type_code - Initialize the shared code
* @wx: pointer to hardware structure
**/
static void txgbe_init_type_code(struct wx *wx)
{
u8 device_type = wx->subsystem_device_id & 0xF0;
switch (wx->device_id) {
case TXGBE_DEV_ID_SP1000:
case TXGBE_DEV_ID_WX1820:
wx->mac.type = wx_mac_sp;
break;
case TXGBE_DEV_ID_AML5010:
case TXGBE_DEV_ID_AML5110:
case TXGBE_DEV_ID_AML5025:
case TXGBE_DEV_ID_AML5125:
wx->mac.type = wx_mac_aml;
break;
case TXGBE_DEV_ID_AML5040:
case TXGBE_DEV_ID_AML5140:
wx->mac.type = wx_mac_aml40;
break;
default:
wx->mac.type = wx_mac_unknown;
break;
}
switch (device_type) {
case TXGBE_ID_SFP:
wx->media_type = wx_media_fiber;
break;
case TXGBE_ID_XAUI:
case TXGBE_ID_SGMII:
wx->media_type = wx_media_copper;
break;
case TXGBE_ID_KR_KX_KX4:
case TXGBE_ID_MAC_XAUI:
case TXGBE_ID_MAC_SGMII:
wx->media_type = wx_media_backplane;
break;
case TXGBE_ID_SFI_XAUI:
if (wx->bus.func == 0)
wx->media_type = wx_media_fiber;
else
wx->media_type = wx_media_copper;
break;
default:
wx->media_type = wx_media_unknown;
break;
}
}
/**
* txgbe_sw_init - Initialize general software structures (struct wx)
* @wx: board private structure to initialize
**/
static int txgbe_sw_init(struct wx *wx)
{
u16 msix_count = 0;
int err;
wx->mac.num_rar_entries = TXGBE_RAR_ENTRIES;
wx->mac.max_tx_queues = TXGBE_MAX_TXQ;
wx->mac.max_rx_queues = TXGBE_MAX_RXQ;
wx->mac.mcft_size = TXGBE_MC_TBL_SIZE;
wx->mac.vft_size = TXGBE_VFT_TBL_SIZE;
wx->mac.rx_pb_size = TXGBE_RX_PB_SIZE;
wx->mac.tx_pb_size = TXGBE_TDB_PB_SZ;
/* PCI config space info */
err = wx_sw_init(wx);
if (err < 0)
return err;
txgbe_init_type_code(wx);
/* Set common capability flags and settings */
wx->max_q_vectors = TXGBE_MAX_MSIX_VECTORS;
err = wx_get_pcie_msix_counts(wx, &msix_count, TXGBE_MAX_MSIX_VECTORS);
if (err)
wx_err(wx, "Do not support MSI-X\n");
wx->mac.max_msix_vectors = msix_count;
wx->ring_feature[RING_F_RSS].limit = min_t(int, TXGBE_MAX_RSS_INDICES,
num_online_cpus());
wx->rss_enabled = true;
wx->ring_feature[RING_F_FDIR].limit = min_t(int, TXGBE_MAX_FDIR_INDICES,
num_online_cpus());
set_bit(WX_FLAG_FDIR_CAPABLE, wx->flags);
set_bit(WX_FLAG_FDIR_HASH, wx->flags);
wx->atr_sample_rate = TXGBE_DEFAULT_ATR_SAMPLE_RATE;
wx->atr = txgbe_atr;
wx->configure_fdir = txgbe_configure_fdir;
set_bit(WX_FLAG_RSC_CAPABLE, wx->flags);
set_bit(WX_FLAG_RSC_ENABLED, wx->flags);
set_bit(WX_FLAG_MULTI_64_FUNC, wx->flags);
/* enable itr by default in dynamic mode */
wx->adaptive_itr = true;
wx->rx_itr_setting = 1;
wx->tx_itr_setting = 1;
/* set default ring sizes */
wx->tx_ring_count = TXGBE_DEFAULT_TXD;
wx->rx_ring_count = TXGBE_DEFAULT_RXD;
wx->mbx.size = WX_VXMAILBOX_SIZE;
/* set default work limits */
wx->tx_work_limit = TXGBE_DEFAULT_TX_WORK;
wx->rx_work_limit = TXGBE_DEFAULT_RX_WORK;
wx->setup_tc = txgbe_setup_tc;
wx->do_reset = txgbe_do_reset;
set_bit(0, &wx->fwd_bitmask);
switch (wx->mac.type) {
case wx_mac_sp:
break;
case wx_mac_aml:
case wx_mac_aml40:
set_bit(WX_FLAG_RX_MERGE_ENABLED, wx->flags);
set_bit(WX_FLAG_TXHEAD_WB_ENABLED, wx->flags);
set_bit(WX_FLAG_SWFW_RING, wx->flags);
wx->swfw_index = 0;
break;
default:
break;
}
return 0;
}
static void txgbe_init_fdir(struct txgbe *txgbe)
{
txgbe->fdir_filter_count = 0;
spin_lock_init(&txgbe->fdir_perfect_lock);
}
/**
* txgbe_open - Called when a network interface is made active
* @netdev: network interface device structure
*
* Returns 0 on success, negative value on failure
*
* The open entry point is called when a network interface is made
* active by the system (IFF_UP).
**/
static int txgbe_open(struct net_device *netdev)
{
struct wx *wx = netdev_priv(netdev);
int err;
err = wx_setup_resources(wx);
if (err)
goto err_reset;
wx_configure(wx);
err = txgbe_setup_misc_irq(wx->priv);
if (err)
goto err_free_resources;
err = txgbe_request_queue_irqs(wx);
if (err)
goto err_free_misc_irq;
/* Notify the stack of the actual queue counts. */
err = netif_set_real_num_tx_queues(netdev, wx->num_tx_queues);
if (err)
goto err_free_irq;
err = netif_set_real_num_rx_queues(netdev, wx->num_rx_queues);
if (err)
goto err_free_irq;
wx_ptp_init(wx);
txgbe_up_complete(wx);
return 0;
err_free_irq:
wx_free_irq(wx);
err_free_misc_irq:
txgbe_free_misc_irq(wx->priv);
wx_reset_interrupt_capability(wx);
err_free_resources:
wx_free_resources(wx);
err_reset:
txgbe_reset(wx);
return err;
}
/**
* txgbe_close_suspend - actions necessary to both suspend and close flows
* @wx: the private wx struct
*
* This function should contain the necessary work common to both suspending
* and closing of the device.
*/
static void txgbe_close_suspend(struct wx *wx)
{
wx_ptp_suspend(wx);
txgbe_disable_device(wx);
wx_free_resources(wx);
}
/**
* txgbe_close - Disables a network interface
* @netdev: network interface device structure
*
* Returns 0, this is not allowed to fail
*
* The close entry point is called when an interface is de-activated
* by the OS. The hardware is still under the drivers control, but
* needs to be disabled. A global MAC reset is issued to stop the
* hardware, and all transmit and receive resources are freed.
**/
static int txgbe_close(struct net_device *netdev)
{
struct wx *wx = netdev_priv(netdev);
wx_ptp_stop(wx);
txgbe_down(wx);
wx_free_irq(wx);
txgbe_free_misc_irq(wx->priv);
wx_free_resources(wx);
txgbe_fdir_filter_exit(wx);
wx_control_hw(wx, false);
return 0;
}
static void txgbe_dev_shutdown(struct pci_dev *pdev)
{
struct wx *wx = pci_get_drvdata(pdev);
struct net_device *netdev;
netdev = wx->netdev;
netif_device_detach(netdev);
rtnl_lock();
if (netif_running(netdev))
txgbe_close_suspend(wx);
rtnl_unlock();
wx_control_hw(wx, false);
pci_disable_device(pdev);
}
static void txgbe_shutdown(struct pci_dev *pdev)
{
txgbe_dev_shutdown(pdev);
if (system_state == SYSTEM_POWER_OFF) {
pci_wake_from_d3(pdev, false);
pci_set_power_state(pdev, PCI_D3hot);
}
}
/**
* txgbe_setup_tc - routine to configure net_device for multiple traffic
* classes.
*
* @dev: net device to configure
* @tc: number of traffic classes to enable
*/
int txgbe_setup_tc(struct net_device *dev, u8 tc)
{
struct wx *wx = netdev_priv(dev);
/* Hardware has to reinitialize queues and interrupts to
* match packet buffer alignment. Unfortunately, the
* hardware is not flexible enough to do this dynamically.
*/
if (netif_running(dev))
txgbe_close(dev);
else
txgbe_reset(wx);
wx_clear_interrupt_scheme(wx);
if (tc)
netdev_set_num_tc(dev, tc);
else
netdev_reset_tc(dev);
wx_init_interrupt_scheme(wx);
if (netif_running(dev))
txgbe_open(dev);
return 0;
}
static void txgbe_reinit_locked(struct wx *wx)
{
netif_trans_update(wx->netdev);
mutex_lock(&wx->reset_lock);
set_bit(WX_STATE_RESETTING, wx->state);
txgbe_down(wx);
txgbe_up(wx);
clear_bit(WX_STATE_RESETTING, wx->state);
mutex_unlock(&wx->reset_lock);
}
void txgbe_do_reset(struct net_device *netdev)
{
struct wx *wx = netdev_priv(netdev);
if (netif_running(netdev))
txgbe_reinit_locked(wx);
else
txgbe_reset(wx);
}
static int txgbe_udp_tunnel_sync(struct net_device *dev, unsigned int table)
{
struct wx *wx = netdev_priv(dev);
struct udp_tunnel_info ti;
udp_tunnel_nic_get_port(dev, table, 0, &ti);
switch (ti.type) {
case UDP_TUNNEL_TYPE_VXLAN:
wr32(wx, TXGBE_CFG_VXLAN, ntohs(ti.port));
break;
case UDP_TUNNEL_TYPE_VXLAN_GPE:
wr32(wx, TXGBE_CFG_VXLAN_GPE, ntohs(ti.port));
break;
case UDP_TUNNEL_TYPE_GENEVE:
wr32(wx, TXGBE_CFG_GENEVE, ntohs(ti.port));
break;
default:
break;
}
return 0;
}
static const struct udp_tunnel_nic_info txgbe_udp_tunnels = {
.sync_table = txgbe_udp_tunnel_sync,
.flags = UDP_TUNNEL_NIC_INFO_OPEN_ONLY,
.tables = {
{ .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, },
{ .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN_GPE, },
{ .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_GENEVE, },
},
};
static const struct net_device_ops txgbe_netdev_ops = {
.ndo_open = txgbe_open,
.ndo_stop = txgbe_close,
.ndo_change_mtu = wx_change_mtu,
.ndo_start_xmit = wx_xmit_frame,
.ndo_set_rx_mode = wx_set_rx_mode,
.ndo_set_features = wx_set_features,
.ndo_fix_features = wx_fix_features,
.ndo_features_check = wx_features_check,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = wx_set_mac,
.ndo_get_stats64 = wx_get_stats64,
.ndo_vlan_rx_add_vid = wx_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = wx_vlan_rx_kill_vid,
.ndo_hwtstamp_set = wx_hwtstamp_set,
.ndo_hwtstamp_get = wx_hwtstamp_get,
};
/**
* txgbe_probe - Device Initialization Routine
* @pdev: PCI device information struct
* @ent: entry in txgbe_pci_tbl
*
* Returns 0 on success, negative on failure
*
* txgbe_probe initializes an adapter identified by a pci_dev structure.
* The OS initialization, configuring of the wx private structure,
* and a hardware reset occur.
**/
static int txgbe_probe(struct pci_dev *pdev,
const struct pci_device_id __always_unused *ent)
{
struct net_device *netdev;
int err, expected_gts;
struct wx *wx = NULL;
struct txgbe *txgbe;
u16 eeprom_verh = 0, eeprom_verl = 0, offset = 0;
u16 eeprom_cfg_blkh = 0, eeprom_cfg_blkl = 0;
u16 build = 0, major = 0, patch = 0;
u32 etrack_id = 0;
err = pci_enable_device_mem(pdev);
if (err)
return err;
err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (err) {
dev_err(&pdev->dev,
"No usable DMA configuration, aborting\n");
goto err_pci_disable_dev;
}
err = pci_request_selected_regions(pdev,
pci_select_bars(pdev, IORESOURCE_MEM),
txgbe_driver_name);
if (err) {
dev_err(&pdev->dev,
"pci_request_selected_regions failed 0x%x\n", err);
goto err_pci_disable_dev;
}
pci_set_master(pdev);
netdev = devm_alloc_etherdev_mqs(&pdev->dev,
sizeof(struct wx),
TXGBE_MAX_TX_QUEUES,
TXGBE_MAX_RX_QUEUES);
if (!netdev) {
err = -ENOMEM;
goto err_pci_release_regions;
}
SET_NETDEV_DEV(netdev, &pdev->dev);
wx = netdev_priv(netdev);
wx->netdev = netdev;
wx->pdev = pdev;
wx->msg_enable = (1 << DEFAULT_DEBUG_LEVEL_SHIFT) - 1;
wx->hw_addr = devm_ioremap(&pdev->dev,
pci_resource_start(pdev, 0),
pci_resource_len(pdev, 0));
if (!wx->hw_addr) {
err = -EIO;
goto err_pci_release_regions;
}
/* The sapphire supports up to 63 VFs per pf, but physical
* function also need one pool for basic networking.
*/
pci_sriov_set_totalvfs(pdev, TXGBE_MAX_VFS_DRV_LIMIT);
wx->driver_name = txgbe_driver_name;
txgbe_set_ethtool_ops(netdev);
netdev->netdev_ops = &txgbe_netdev_ops;
netdev->udp_tunnel_nic_info = &txgbe_udp_tunnels;
/* setup the private structure */
err = txgbe_sw_init(wx);
if (err)
goto err_pci_release_regions;
/* check if flash load is done after hw power up */
err = wx_check_flash_load(wx, TXGBE_SPI_ILDR_STATUS_PERST);
if (err)
goto err_free_mac_table;
err = wx_check_flash_load(wx, TXGBE_SPI_ILDR_STATUS_PWRRST);
if (err)
goto err_free_mac_table;
err = wx_mng_present(wx);
if (err) {
dev_err(&pdev->dev, "Management capability is not present\n");
goto err_free_mac_table;
}
err = txgbe_reset_hw(wx);
if (err) {
dev_err(&pdev->dev, "HW Init failed: %d\n", err);
goto err_free_mac_table;
}
netdev->features = NETIF_F_SG |
NETIF_F_TSO |
NETIF_F_TSO6 |
NETIF_F_RXHASH |
NETIF_F_RXCSUM |
NETIF_F_HW_CSUM;
netdev->gso_partial_features = NETIF_F_GSO_ENCAP_ALL;
netdev->features |= netdev->gso_partial_features;
netdev->features |= NETIF_F_SCTP_CRC;
netdev->vlan_features |= netdev->features | NETIF_F_TSO_MANGLEID;
netdev->hw_enc_features |= netdev->vlan_features;
netdev->features |= NETIF_F_VLAN_FEATURES;
/* copy netdev features into list of user selectable features */
netdev->hw_features |= netdev->features | NETIF_F_RXALL;
netdev->hw_features |= NETIF_F_NTUPLE | NETIF_F_HW_TC;
netdev->features |= NETIF_F_HIGHDMA;
netdev->hw_features |= NETIF_F_GRO;
netdev->features |= NETIF_F_GRO;
netdev->hw_features |= NETIF_F_LRO;
netdev->features |= NETIF_F_LRO;
netdev->features |= NETIF_F_RX_UDP_TUNNEL_PORT;
netdev->priv_flags |= IFF_UNICAST_FLT;
netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
netdev->min_mtu = ETH_MIN_MTU;
netdev->max_mtu = WX_MAX_JUMBO_FRAME_SIZE -
(ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN);
/* make sure the EEPROM is good */
err = txgbe_validate_eeprom_checksum(wx, NULL);
if (err != 0) {
dev_err(&pdev->dev, "The EEPROM Checksum Is Not Valid\n");
wr32(wx, WX_MIS_RST, WX_MIS_RST_SW_RST);
err = -EIO;
goto err_free_mac_table;
}
eth_hw_addr_set(netdev, wx->mac.perm_addr);
wx_mac_set_default_filter(wx, wx->mac.perm_addr);
txgbe_init_service(wx);
err = wx_init_interrupt_scheme(wx);
if (err)
goto err_cancel_service;
/* Save off EEPROM version number and Option Rom version which
* together make a unique identify for the eeprom
*/
wx_read_ee_hostif(wx,
wx->eeprom.sw_region_offset + TXGBE_EEPROM_VERSION_H,
&eeprom_verh);
wx_read_ee_hostif(wx,
wx->eeprom.sw_region_offset + TXGBE_EEPROM_VERSION_L,
&eeprom_verl);
etrack_id = (eeprom_verh << 16) | eeprom_verl;
wx_read_ee_hostif(wx,
wx->eeprom.sw_region_offset + TXGBE_ISCSI_BOOT_CONFIG,
&offset);
/* Make sure offset to SCSI block is valid */
if (!(offset == 0x0) && !(offset == 0xffff)) {
wx_read_ee_hostif(wx, offset + 0x84, &eeprom_cfg_blkh);
wx_read_ee_hostif(wx, offset + 0x83, &eeprom_cfg_blkl);
/* Only display Option Rom if exist */
if (eeprom_cfg_blkl && eeprom_cfg_blkh) {
major = eeprom_cfg_blkl >> 8;
build = (eeprom_cfg_blkl << 8) | (eeprom_cfg_blkh >> 8);
patch = eeprom_cfg_blkh & 0x00ff;
snprintf(wx->eeprom_id, sizeof(wx->eeprom_id),
"0x%08x, %d.%d.%d", etrack_id, major, build,
patch);
} else {
snprintf(wx->eeprom_id, sizeof(wx->eeprom_id),
"0x%08x", etrack_id);
}
} else {
snprintf(wx->eeprom_id, sizeof(wx->eeprom_id),
"0x%08x", etrack_id);
}
if (wx->mac.type == wx_mac_sp &&
((etrack_id & 0xfffff) < 0x20010))
dev_warn(&pdev->dev, "Please upgrade the firmware to 0x20010 or above.\n");
err = txgbe_test_hostif(wx);
if (err != 0) {
dev_err(&pdev->dev, "Mismatched Firmware version\n");
err = -EIO;
goto err_release_hw;
}
txgbe = devm_kzalloc(&pdev->dev, sizeof(*txgbe), GFP_KERNEL);
if (!txgbe) {
err = -ENOMEM;
goto err_release_hw;
}
txgbe->wx = wx;
wx->priv = txgbe;
txgbe_init_fdir(txgbe);
err = txgbe_init_phy(txgbe);
if (err)
goto err_release_hw;
err = register_netdev(netdev);
if (err)
goto err_remove_phy;
pci_set_drvdata(pdev, wx);
netif_tx_stop_all_queues(netdev);
/* calculate the expected PCIe bandwidth required for optimal
* performance. Note that some older parts will never have enough
* bandwidth due to being older generation PCIe parts. We clamp these
* parts to ensure that no warning is displayed, as this could confuse
* users otherwise.
*/
expected_gts = txgbe_enumerate_functions(wx) * 10;
/* don't check link if we failed to enumerate functions */
if (expected_gts > 0)
txgbe_check_minimum_link(wx);
else
dev_warn(&pdev->dev, "Failed to enumerate PF devices.\n");
return 0;
err_remove_phy:
txgbe_remove_phy(txgbe);
err_release_hw:
wx_clear_interrupt_scheme(wx);
wx_control_hw(wx, false);
err_cancel_service:
timer_delete_sync(&wx->service_timer);
cancel_work_sync(&wx->service_task);
err_free_mac_table:
kfree(wx->rss_key);
kfree(wx->mac_table);
err_pci_release_regions:
pci_release_selected_regions(pdev,
pci_select_bars(pdev, IORESOURCE_MEM));
err_pci_disable_dev:
pci_disable_device(pdev);
return err;
}
/**
* txgbe_remove - Device Removal Routine
* @pdev: PCI device information struct
*
* txgbe_remove is called by the PCI subsystem to alert the driver
* that it should release a PCI device. The could be caused by a
* Hot-Plug event, or because the driver is going to be removed from
* memory.
**/
static void txgbe_remove(struct pci_dev *pdev)
{
struct wx *wx = pci_get_drvdata(pdev);
struct txgbe *txgbe = wx->priv;
struct net_device *netdev;
netdev = wx->netdev;
wx_disable_sriov(wx);
unregister_netdev(netdev);
txgbe_fdir_filter_exit(wx);
timer_shutdown_sync(&wx->service_timer);
cancel_work_sync(&wx->service_task);
txgbe_remove_phy(txgbe);
wx_free_isb_resources(wx);
pci_release_selected_regions(pdev,
pci_select_bars(pdev, IORESOURCE_MEM));
kfree(wx->rss_key);
kfree(wx->mac_table);
wx_clear_interrupt_scheme(wx);
pci_disable_device(pdev);
}
static struct pci_driver txgbe_driver = {
.name = txgbe_driver_name,
.id_table = txgbe_pci_tbl,
.probe = txgbe_probe,
.remove = txgbe_remove,
.shutdown = txgbe_shutdown,
.sriov_configure = wx_pci_sriov_configure,
};
module_pci_driver(txgbe_driver);
MODULE_DEVICE_TABLE(pci, txgbe_pci_tbl);
MODULE_AUTHOR("Beijing WangXun Technology Co., Ltd, <software@trustnetic.com>");
MODULE_DESCRIPTION("WangXun(R) 10/25/40 Gigabit PCI Express Network Driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,678 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2015 - 2023 Beijing WangXun Technology Co., Ltd. */
#include <linux/gpio/machine.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/property.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/i2c.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/pcs/pcs-xpcs.h>
#include <linux/phylink.h>
#include "../libwx/wx_type.h"
#include "../libwx/wx_lib.h"
#include "../libwx/wx_ptp.h"
#include "../libwx/wx_sriov.h"
#include "../libwx/wx_mbx.h"
#include "../libwx/wx_hw.h"
#include "txgbe_type.h"
#include "txgbe_aml.h"
#include "txgbe_phy.h"
#include "txgbe_hw.h"
static int txgbe_swnodes_register(struct txgbe *txgbe)
{
struct txgbe_nodes *nodes = &txgbe->nodes;
struct pci_dev *pdev = txgbe->wx->pdev;
struct software_node *swnodes;
u32 id;
id = pci_dev_id(pdev);
snprintf(nodes->gpio_name, sizeof(nodes->gpio_name), "txgbe_gpio-%x", id);
snprintf(nodes->i2c_name, sizeof(nodes->i2c_name), "txgbe_i2c-%x", id);
snprintf(nodes->sfp_name, sizeof(nodes->sfp_name), "txgbe_sfp-%x", id);
snprintf(nodes->phylink_name, sizeof(nodes->phylink_name), "txgbe_phylink-%x", id);
swnodes = nodes->swnodes;
/* GPIO 0: tx fault
* GPIO 1: tx disable
* GPIO 2: sfp module absent
* GPIO 3: rx signal lost
* GPIO 4: rate select, 1G(0) 10G(1)
* GPIO 5: rate select, 1G(0) 10G(1)
*/
nodes->gpio_props[0] = PROPERTY_ENTRY_STRING("pinctrl-names", "default");
swnodes[SWNODE_GPIO] = NODE_PROP(nodes->gpio_name, nodes->gpio_props);
nodes->gpio0_ref[0] = SOFTWARE_NODE_REFERENCE(&swnodes[SWNODE_GPIO], 0, GPIO_ACTIVE_HIGH);
nodes->gpio1_ref[0] = SOFTWARE_NODE_REFERENCE(&swnodes[SWNODE_GPIO], 1, GPIO_ACTIVE_HIGH);
nodes->gpio2_ref[0] = SOFTWARE_NODE_REFERENCE(&swnodes[SWNODE_GPIO], 2, GPIO_ACTIVE_LOW);
nodes->gpio3_ref[0] = SOFTWARE_NODE_REFERENCE(&swnodes[SWNODE_GPIO], 3, GPIO_ACTIVE_HIGH);
nodes->gpio4_ref[0] = SOFTWARE_NODE_REFERENCE(&swnodes[SWNODE_GPIO], 4, GPIO_ACTIVE_HIGH);
nodes->gpio5_ref[0] = SOFTWARE_NODE_REFERENCE(&swnodes[SWNODE_GPIO], 5, GPIO_ACTIVE_HIGH);
nodes->i2c_props[0] = PROPERTY_ENTRY_STRING("compatible", "snps,designware-i2c");
nodes->i2c_props[1] = PROPERTY_ENTRY_BOOL("wx,i2c-snps-model");
nodes->i2c_props[2] = PROPERTY_ENTRY_U32("clock-frequency", I2C_MAX_STANDARD_MODE_FREQ);
swnodes[SWNODE_I2C] = NODE_PROP(nodes->i2c_name, nodes->i2c_props);
nodes->i2c_ref[0] = SOFTWARE_NODE_REFERENCE(&swnodes[SWNODE_I2C]);
nodes->sfp_props[0] = PROPERTY_ENTRY_STRING("compatible", "sff,sfp");
nodes->sfp_props[1] = PROPERTY_ENTRY_REF_ARRAY("i2c-bus", nodes->i2c_ref);
nodes->sfp_props[2] = PROPERTY_ENTRY_REF_ARRAY("tx-fault-gpios", nodes->gpio0_ref);
nodes->sfp_props[3] = PROPERTY_ENTRY_REF_ARRAY("tx-disable-gpios", nodes->gpio1_ref);
nodes->sfp_props[4] = PROPERTY_ENTRY_REF_ARRAY("mod-def0-gpios", nodes->gpio2_ref);
nodes->sfp_props[5] = PROPERTY_ENTRY_REF_ARRAY("los-gpios", nodes->gpio3_ref);
nodes->sfp_props[6] = PROPERTY_ENTRY_REF_ARRAY("rate-select1-gpios", nodes->gpio4_ref);
nodes->sfp_props[7] = PROPERTY_ENTRY_REF_ARRAY("rate-select0-gpios", nodes->gpio5_ref);
swnodes[SWNODE_SFP] = NODE_PROP(nodes->sfp_name, nodes->sfp_props);
nodes->sfp_ref[0] = SOFTWARE_NODE_REFERENCE(&swnodes[SWNODE_SFP]);
nodes->phylink_props[0] = PROPERTY_ENTRY_STRING("managed", "in-band-status");
nodes->phylink_props[1] = PROPERTY_ENTRY_REF_ARRAY("sfp", nodes->sfp_ref);
swnodes[SWNODE_PHYLINK] = NODE_PROP(nodes->phylink_name, nodes->phylink_props);
nodes->group[SWNODE_GPIO] = &swnodes[SWNODE_GPIO];
nodes->group[SWNODE_I2C] = &swnodes[SWNODE_I2C];
nodes->group[SWNODE_SFP] = &swnodes[SWNODE_SFP];
nodes->group[SWNODE_PHYLINK] = &swnodes[SWNODE_PHYLINK];
return software_node_register_node_group(nodes->group);
}
static int txgbe_pcs_read(struct mii_bus *bus, int addr, int devnum, int regnum)
{
struct wx *wx = bus->priv;
u32 offset, val;
if (addr)
return -EOPNOTSUPP;
offset = devnum << 16 | regnum;
/* Set the LAN port indicator to IDA_ADDR */
wr32(wx, TXGBE_XPCS_IDA_ADDR, offset);
/* Read the data from IDA_DATA register */
val = rd32(wx, TXGBE_XPCS_IDA_DATA);
return (u16)val;
}
static int txgbe_pcs_write(struct mii_bus *bus, int addr, int devnum, int regnum, u16 val)
{
struct wx *wx = bus->priv;
u32 offset;
if (addr)
return -EOPNOTSUPP;
offset = devnum << 16 | regnum;
/* Set the LAN port indicator to IDA_ADDR */
wr32(wx, TXGBE_XPCS_IDA_ADDR, offset);
/* Write the data to IDA_DATA register */
wr32(wx, TXGBE_XPCS_IDA_DATA, val);
return 0;
}
static int txgbe_mdio_pcs_init(struct txgbe *txgbe)
{
struct mii_bus *mii_bus;
struct phylink_pcs *pcs;
struct pci_dev *pdev;
struct wx *wx;
int ret = 0;
wx = txgbe->wx;
pdev = wx->pdev;
mii_bus = devm_mdiobus_alloc(&pdev->dev);
if (!mii_bus)
return -ENOMEM;
mii_bus->name = "txgbe_pcs_mdio_bus";
mii_bus->read_c45 = &txgbe_pcs_read;
mii_bus->write_c45 = &txgbe_pcs_write;
mii_bus->parent = &pdev->dev;
mii_bus->phy_mask = ~0;
mii_bus->priv = wx;
snprintf(mii_bus->id, MII_BUS_ID_SIZE, "txgbe_pcs-%x",
pci_dev_id(pdev));
ret = devm_mdiobus_register(&pdev->dev, mii_bus);
if (ret)
return ret;
pcs = xpcs_create_pcs_mdiodev(mii_bus, 0);
if (IS_ERR(pcs))
return PTR_ERR(pcs);
txgbe->pcs = pcs;
return 0;
}
static struct phylink_pcs *txgbe_phylink_mac_select(struct phylink_config *config,
phy_interface_t interface)
{
struct wx *wx = phylink_to_wx(config);
struct txgbe *txgbe = wx->priv;
if (wx->media_type != wx_media_copper)
return txgbe->pcs;
return NULL;
}
static void txgbe_mac_config(struct phylink_config *config, unsigned int mode,
const struct phylink_link_state *state)
{
}
static void txgbe_mac_link_down(struct phylink_config *config,
unsigned int mode, phy_interface_t interface)
{
struct wx *wx = phylink_to_wx(config);
wr32m(wx, WX_MAC_TX_CFG, WX_MAC_TX_CFG_TE, 0);
wx->speed = SPEED_UNKNOWN;
if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
wx_ptp_reset_cyclecounter(wx);
/* ping all the active vfs to let them know we are going down */
wx_ping_all_vfs_with_link_status(wx, false);
}
static void txgbe_mac_link_up(struct phylink_config *config,
struct phy_device *phy,
unsigned int mode, phy_interface_t interface,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
struct wx *wx = phylink_to_wx(config);
u32 txcfg, wdg;
wx_fc_enable(wx, tx_pause, rx_pause);
txcfg = rd32(wx, WX_MAC_TX_CFG);
txcfg &= ~WX_MAC_TX_CFG_SPEED_MASK;
switch (speed) {
case SPEED_10000:
txcfg |= WX_MAC_TX_CFG_SPEED_10G;
break;
case SPEED_1000:
case SPEED_100:
case SPEED_10:
txcfg |= WX_MAC_TX_CFG_SPEED_1G;
break;
default:
break;
}
wr32(wx, WX_MAC_TX_CFG, txcfg | WX_MAC_TX_CFG_TE);
/* Re configure MAC Rx */
wr32m(wx, WX_MAC_RX_CFG, WX_MAC_RX_CFG_RE, WX_MAC_RX_CFG_RE);
wr32(wx, WX_MAC_PKT_FLT, WX_MAC_PKT_FLT_PR);
wdg = rd32(wx, WX_MAC_WDG_TIMEOUT);
wr32(wx, WX_MAC_WDG_TIMEOUT, wdg);
wx->speed = speed;
wx->last_rx_ptp_check = jiffies;
if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
wx_ptp_reset_cyclecounter(wx);
/* ping all the active vfs to let them know we are going up */
wx_ping_all_vfs_with_link_status(wx, true);
}
static int txgbe_mac_prepare(struct phylink_config *config, unsigned int mode,
phy_interface_t interface)
{
struct wx *wx = phylink_to_wx(config);
wr32m(wx, WX_MAC_TX_CFG, WX_MAC_TX_CFG_TE, 0);
wr32m(wx, WX_MAC_RX_CFG, WX_MAC_RX_CFG_RE, 0);
return txgbe_disable_sec_tx_path(wx);
}
static int txgbe_mac_finish(struct phylink_config *config, unsigned int mode,
phy_interface_t interface)
{
struct wx *wx = phylink_to_wx(config);
txgbe_enable_sec_tx_path(wx);
wr32m(wx, WX_MAC_RX_CFG, WX_MAC_RX_CFG_RE, WX_MAC_RX_CFG_RE);
return 0;
}
static const struct phylink_mac_ops txgbe_mac_ops = {
.mac_select_pcs = txgbe_phylink_mac_select,
.mac_prepare = txgbe_mac_prepare,
.mac_finish = txgbe_mac_finish,
.mac_config = txgbe_mac_config,
.mac_link_down = txgbe_mac_link_down,
.mac_link_up = txgbe_mac_link_up,
};
static int txgbe_phylink_init(struct txgbe *txgbe)
{
struct fwnode_handle *fwnode = NULL;
struct phylink_config *config;
struct wx *wx = txgbe->wx;
phy_interface_t phy_mode;
struct phylink *phylink;
config = &wx->phylink_config;
config->dev = &wx->netdev->dev;
config->type = PHYLINK_NETDEV;
config->mac_capabilities = MAC_10000FD | MAC_1000FD | MAC_100FD |
MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
if (wx->media_type == wx_media_copper) {
phy_mode = PHY_INTERFACE_MODE_XAUI;
__set_bit(PHY_INTERFACE_MODE_XAUI, config->supported_interfaces);
} else {
phy_mode = PHY_INTERFACE_MODE_10GBASER;
fwnode = software_node_fwnode(txgbe->nodes.group[SWNODE_PHYLINK]);
__set_bit(PHY_INTERFACE_MODE_10GBASER, config->supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_1000BASEX, config->supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_SGMII, config->supported_interfaces);
}
phylink = phylink_create(config, fwnode, phy_mode, &txgbe_mac_ops);
if (IS_ERR(phylink))
return PTR_ERR(phylink);
if (wx->phydev) {
int ret;
ret = phylink_connect_phy(phylink, wx->phydev);
if (ret) {
phylink_destroy(phylink);
return ret;
}
}
wx->phylink = phylink;
return 0;
}
irqreturn_t txgbe_link_irq_handler(int irq, void *data)
{
struct txgbe *txgbe = data;
struct wx *wx = txgbe->wx;
u32 status;
bool up;
status = rd32(wx, TXGBE_CFG_PORT_ST);
up = !!(status & TXGBE_CFG_PORT_ST_LINK_UP);
if (txgbe->pcs)
phylink_pcs_change(txgbe->pcs, up);
else
phylink_mac_change(wx->phylink, up);
return IRQ_HANDLED;
}
static int txgbe_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
struct wx *wx = gpiochip_get_data(chip);
int val;
val = rd32m(wx, WX_GPIO_EXT, BIT(offset));
return !!(val & BIT(offset));
}
static int txgbe_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
{
struct wx *wx = gpiochip_get_data(chip);
u32 val;
val = rd32(wx, WX_GPIO_DDR);
if (BIT(offset) & val)
return GPIO_LINE_DIRECTION_OUT;
return GPIO_LINE_DIRECTION_IN;
}
static int txgbe_gpio_direction_in(struct gpio_chip *chip, unsigned int offset)
{
struct wx *wx = gpiochip_get_data(chip);
unsigned long flags;
raw_spin_lock_irqsave(&wx->gpio_lock, flags);
wr32m(wx, WX_GPIO_DDR, BIT(offset), 0);
raw_spin_unlock_irqrestore(&wx->gpio_lock, flags);
return 0;
}
static int txgbe_gpio_direction_out(struct gpio_chip *chip, unsigned int offset,
int val)
{
struct wx *wx = gpiochip_get_data(chip);
unsigned long flags;
u32 set;
set = val ? BIT(offset) : 0;
raw_spin_lock_irqsave(&wx->gpio_lock, flags);
wr32m(wx, WX_GPIO_DR, BIT(offset), set);
wr32m(wx, WX_GPIO_DDR, BIT(offset), BIT(offset));
raw_spin_unlock_irqrestore(&wx->gpio_lock, flags);
return 0;
}
static int txgbe_gpio_init(struct txgbe *txgbe)
{
struct gpio_chip *gc;
struct device *dev;
struct wx *wx;
int ret;
wx = txgbe->wx;
dev = &wx->pdev->dev;
raw_spin_lock_init(&wx->gpio_lock);
gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
if (!gc)
return -ENOMEM;
gc->label = devm_kasprintf(dev, GFP_KERNEL, "txgbe_gpio-%x",
pci_dev_id(wx->pdev));
if (!gc->label)
return -ENOMEM;
gc->base = -1;
gc->ngpio = 6;
gc->owner = THIS_MODULE;
gc->parent = dev;
gc->fwnode = software_node_fwnode(txgbe->nodes.group[SWNODE_GPIO]);
gc->get = txgbe_gpio_get;
gc->get_direction = txgbe_gpio_get_direction;
gc->direction_input = txgbe_gpio_direction_in;
gc->direction_output = txgbe_gpio_direction_out;
ret = devm_gpiochip_add_data(dev, gc, wx);
if (ret)
return ret;
txgbe->gpio = gc;
return 0;
}
static int txgbe_clock_register(struct txgbe *txgbe)
{
struct pci_dev *pdev = txgbe->wx->pdev;
struct clk_lookup *clock;
char clk_name[32];
struct clk *clk;
snprintf(clk_name, sizeof(clk_name), "i2c_designware.%d",
pci_dev_id(pdev));
clk = clk_register_fixed_rate(NULL, clk_name, NULL, 0, 156250000);
if (IS_ERR(clk))
return PTR_ERR(clk);
clock = clkdev_create(clk, NULL, "%s", clk_name);
if (!clock) {
clk_unregister(clk);
return -ENOMEM;
}
txgbe->clk = clk;
txgbe->clock = clock;
return 0;
}
static int txgbe_i2c_read(void *context, unsigned int reg, unsigned int *val)
{
struct wx *wx = context;
*val = rd32(wx, reg + TXGBE_I2C_BASE);
return 0;
}
static int txgbe_i2c_write(void *context, unsigned int reg, unsigned int val)
{
struct wx *wx = context;
wr32(wx, reg + TXGBE_I2C_BASE, val);
return 0;
}
static const struct regmap_config i2c_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_read = txgbe_i2c_read,
.reg_write = txgbe_i2c_write,
.fast_io = true,
};
static int txgbe_i2c_register(struct txgbe *txgbe)
{
struct platform_device_info info = {};
struct platform_device *i2c_dev;
struct regmap *i2c_regmap;
struct pci_dev *pdev;
struct wx *wx;
wx = txgbe->wx;
pdev = wx->pdev;
i2c_regmap = devm_regmap_init(&pdev->dev, NULL, wx, &i2c_regmap_config);
if (IS_ERR(i2c_regmap)) {
wx_err(wx, "failed to init I2C regmap\n");
return PTR_ERR(i2c_regmap);
}
info.parent = &pdev->dev;
info.fwnode = software_node_fwnode(txgbe->nodes.group[SWNODE_I2C]);
info.name = "i2c_designware";
info.id = pci_dev_id(pdev);
info.res = &DEFINE_RES_IRQ(pdev->irq);
info.num_res = 1;
i2c_dev = platform_device_register_full(&info);
if (IS_ERR(i2c_dev))
return PTR_ERR(i2c_dev);
txgbe->i2c_dev = i2c_dev;
return 0;
}
static int txgbe_sfp_register(struct txgbe *txgbe)
{
struct pci_dev *pdev = txgbe->wx->pdev;
struct platform_device_info info = {};
struct platform_device *sfp_dev;
info.parent = &pdev->dev;
info.fwnode = software_node_fwnode(txgbe->nodes.group[SWNODE_SFP]);
info.name = "sfp";
info.id = pci_dev_id(pdev);
sfp_dev = platform_device_register_full(&info);
if (IS_ERR(sfp_dev))
return PTR_ERR(sfp_dev);
txgbe->sfp_dev = sfp_dev;
return 0;
}
static int txgbe_ext_phy_init(struct txgbe *txgbe)
{
struct phy_device *phydev;
struct mii_bus *mii_bus;
struct pci_dev *pdev;
struct wx *wx;
int ret = 0;
wx = txgbe->wx;
pdev = wx->pdev;
mii_bus = devm_mdiobus_alloc(&pdev->dev);
if (!mii_bus)
return -ENOMEM;
mii_bus->name = "txgbe_mii_bus";
mii_bus->read_c45 = &wx_phy_read_reg_mdi_c45;
mii_bus->write_c45 = &wx_phy_write_reg_mdi_c45;
mii_bus->parent = &pdev->dev;
mii_bus->phy_mask = GENMASK(31, 1);
mii_bus->priv = wx;
snprintf(mii_bus->id, MII_BUS_ID_SIZE, "txgbe-%x", pci_dev_id(pdev));
ret = devm_mdiobus_register(&pdev->dev, mii_bus);
if (ret) {
wx_err(wx, "failed to register MDIO bus: %d\n", ret);
return ret;
}
phydev = phy_find_first(mii_bus);
if (!phydev) {
wx_err(wx, "no PHY found\n");
return -ENODEV;
}
phy_attached_info(phydev);
wx->link = 0;
wx->speed = 0;
wx->duplex = 0;
wx->phydev = phydev;
ret = txgbe_phylink_init(txgbe);
if (ret) {
wx_err(wx, "failed to init phylink: %d\n", ret);
return ret;
}
return 0;
}
int txgbe_init_phy(struct txgbe *txgbe)
{
struct wx *wx = txgbe->wx;
int ret;
switch (wx->mac.type) {
case wx_mac_aml40:
case wx_mac_aml:
return txgbe_phylink_init_aml(txgbe);
case wx_mac_sp:
if (wx->media_type == wx_media_copper)
return txgbe_ext_phy_init(txgbe);
break;
default:
break;
}
ret = txgbe_swnodes_register(txgbe);
if (ret) {
wx_err(wx, "failed to register software nodes\n");
return ret;
}
ret = txgbe_mdio_pcs_init(txgbe);
if (ret) {
wx_err(wx, "failed to init mdio pcs: %d\n", ret);
goto err_unregister_swnode;
}
ret = txgbe_phylink_init(txgbe);
if (ret) {
wx_err(wx, "failed to init phylink\n");
goto err_destroy_xpcs;
}
ret = txgbe_gpio_init(txgbe);
if (ret) {
wx_err(wx, "failed to init gpio\n");
goto err_destroy_phylink;
}
ret = txgbe_clock_register(txgbe);
if (ret) {
wx_err(wx, "failed to register clock: %d\n", ret);
goto err_destroy_phylink;
}
ret = txgbe_i2c_register(txgbe);
if (ret) {
wx_err(wx, "failed to init i2c interface: %d\n", ret);
goto err_unregister_clk;
}
ret = txgbe_sfp_register(txgbe);
if (ret) {
wx_err(wx, "failed to register sfp\n");
goto err_unregister_i2c;
}
return 0;
err_unregister_i2c:
platform_device_unregister(txgbe->i2c_dev);
err_unregister_clk:
clkdev_drop(txgbe->clock);
clk_unregister(txgbe->clk);
err_destroy_phylink:
phylink_destroy(wx->phylink);
err_destroy_xpcs:
xpcs_destroy_pcs(txgbe->pcs);
err_unregister_swnode:
software_node_unregister_node_group(txgbe->nodes.group);
return ret;
}
void txgbe_remove_phy(struct txgbe *txgbe)
{
switch (txgbe->wx->mac.type) {
case wx_mac_aml40:
case wx_mac_aml:
phylink_destroy(txgbe->wx->phylink);
return;
case wx_mac_sp:
if (txgbe->wx->media_type == wx_media_copper) {
rtnl_lock();
phylink_disconnect_phy(txgbe->wx->phylink);
rtnl_unlock();
phylink_destroy(txgbe->wx->phylink);
return;
}
break;
default:
break;
}
platform_device_unregister(txgbe->sfp_dev);
platform_device_unregister(txgbe->i2c_dev);
clkdev_drop(txgbe->clock);
clk_unregister(txgbe->clk);
phylink_destroy(txgbe->wx->phylink);
xpcs_destroy_pcs(txgbe->pcs);
software_node_unregister_node_group(txgbe->nodes.group);
}
@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2015 - 2023 Beijing WangXun Technology Co., Ltd. */
#ifndef _TXGBE_PHY_H_
#define _TXGBE_PHY_H_
irqreturn_t txgbe_link_irq_handler(int irq, void *data);
int txgbe_init_phy(struct txgbe *txgbe);
void txgbe_remove_phy(struct txgbe *txgbe);
#endif /* _TXGBE_PHY_H_ */
@@ -0,0 +1,485 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2015 - 2022 Beijing WangXun Technology Co., Ltd. */
#ifndef _TXGBE_TYPE_H_
#define _TXGBE_TYPE_H_
#include <linux/property.h>
#include <linux/irq.h>
#include <linux/phy.h>
#include "../libwx/wx_type.h"
/* Device IDs */
#define TXGBE_DEV_ID_SP1000 0x1001
#define TXGBE_DEV_ID_WX1820 0x2001
#define TXGBE_DEV_ID_AML5010 0x5010
#define TXGBE_DEV_ID_AML5110 0x5110
#define TXGBE_DEV_ID_AML5025 0x5025
#define TXGBE_DEV_ID_AML5125 0x5125
#define TXGBE_DEV_ID_AML5040 0x5040
#define TXGBE_DEV_ID_AML5140 0x5140
/* Subsystem IDs */
/* SFP */
#define TXGBE_ID_SP1000_SFP 0x0000
#define TXGBE_ID_WX1820_SFP 0x2000
#define TXGBE_ID_SFP 0x00
/* copper */
#define TXGBE_ID_SP1000_XAUI 0x1010
#define TXGBE_ID_WX1820_XAUI 0x2010
#define TXGBE_ID_XAUI 0x10
#define TXGBE_ID_SP1000_SGMII 0x1020
#define TXGBE_ID_WX1820_SGMII 0x2020
#define TXGBE_ID_SGMII 0x20
/* backplane */
#define TXGBE_ID_SP1000_KR_KX_KX4 0x1030
#define TXGBE_ID_WX1820_KR_KX_KX4 0x2030
#define TXGBE_ID_KR_KX_KX4 0x30
/* MAC Interface */
#define TXGBE_ID_SP1000_MAC_XAUI 0x1040
#define TXGBE_ID_WX1820_MAC_XAUI 0x2040
#define TXGBE_ID_MAC_XAUI 0x40
#define TXGBE_ID_SP1000_MAC_SGMII 0x1060
#define TXGBE_ID_WX1820_MAC_SGMII 0x2060
#define TXGBE_ID_MAC_SGMII 0x60
/* Combined interface*/
#define TXGBE_ID_SFI_XAUI 0x50
/* Revision ID */
#define TXGBE_SP_MPW 1
/**************** SP Registers ****************************/
/* chip control Registers */
#define TXGBE_MIS_RST 0x1000C
#define TXGBE_MIS_RST_MAC_RST(_i) BIT(20 - (_i) * 3)
#define TXGBE_MIS_PRB_CTL 0x10010
#define TXGBE_MIS_PRB_CTL_LAN_UP(_i) BIT(1 - (_i))
/* FMGR Registers */
#define TXGBE_SPI_ILDR_STATUS 0x10120
#define TXGBE_SPI_ILDR_STATUS_PERST BIT(0) /* PCIE_PERST is done */
#define TXGBE_SPI_ILDR_STATUS_PWRRST BIT(1) /* Power on reset is done */
#define TXGBE_SPI_ILDR_STATUS_LAN_SW_RST(_i) BIT((_i) + 9) /* lan soft reset done */
/* Sensors for PVT(Process Voltage Temperature) */
#define TXGBE_TS_CTL 0x10300
#define TXGBE_TS_CTL_EVAL_MD BIT(31)
/* MAC Misc Registers */
#define TXGBE_MAC_MISC_CTL 0x11F00
#define TXGBE_MAC_MISC_CTL_LINK_STS_MOD BIT(0)
#define TXGBE_MAC_MISC_CTL_LINK_PCS FIELD_PREP(BIT(0), 0)
#define TXGBE_MAC_MISC_CTL_LINK_BOTH FIELD_PREP(BIT(0), 1)
/* GPIO register bit */
#define TXGBE_GPIOBIT_0 BIT(0) /* I:tx fault */
#define TXGBE_GPIOBIT_1 BIT(1) /* O:tx disabled */
#define TXGBE_GPIOBIT_2 BIT(2) /* I:sfp module absent */
#define TXGBE_GPIOBIT_3 BIT(3) /* I:rx signal lost */
#define TXGBE_GPIOBIT_4 BIT(4) /* O:rate select, 1G(0) 10G(1) */
#define TXGBE_GPIOBIT_5 BIT(5) /* O:rate select, 1G(0) 10G(1) */
/* Extended Interrupt Enable Set */
#define TXGBE_PX_MISC_ETH_LKDN BIT(8)
#define TXGBE_PX_MISC_DEV_RST BIT(10)
#define TXGBE_PX_MISC_IC_TIMESYNC BIT(11)
#define TXGBE_PX_MISC_ETH_EVENT BIT(17)
#define TXGBE_PX_MISC_ETH_LK BIT(18)
#define TXGBE_PX_MISC_ETH_AN BIT(19)
#define TXGBE_PX_MISC_INT_ERR BIT(20)
#define TXGBE_PX_MISC_IC_VF_MBOX BIT(23)
#define TXGBE_PX_MISC_GPIO BIT(26)
#define TXGBE_PX_MISC_IEN_MASK \
(TXGBE_PX_MISC_ETH_LKDN | TXGBE_PX_MISC_DEV_RST | \
TXGBE_PX_MISC_ETH_EVENT | TXGBE_PX_MISC_ETH_LK | \
TXGBE_PX_MISC_ETH_AN | TXGBE_PX_MISC_INT_ERR | \
TXGBE_PX_MISC_IC_VF_MBOX | TXGBE_PX_MISC_IC_TIMESYNC)
/* Port cfg registers */
#define TXGBE_CFG_PORT_ST 0x14404
#define TXGBE_CFG_PORT_ST_LINK_UP BIT(0)
#define TXGBE_CFG_PORT_ST_LINK_AML_40G BIT(2)
#define TXGBE_CFG_PORT_ST_LINK_AML_25G BIT(3)
#define TXGBE_CFG_PORT_ST_LINK_AML_10G BIT(4)
#define TXGBE_CFG_VXLAN 0x14410
#define TXGBE_CFG_VXLAN_GPE 0x14414
#define TXGBE_CFG_GENEVE 0x14418
/* I2C registers */
#define TXGBE_I2C_BASE 0x14900
/************************************** ETH PHY ******************************/
#define TXGBE_XPCS_IDA_ADDR 0x13000
#define TXGBE_XPCS_IDA_DATA 0x13004
/********************************* Flow Director *****************************/
#define TXGBE_RDB_FDIR_DROP_QUEUE 127
#define TXGBE_RDB_FDIR_CTL 0x19500
#define TXGBE_RDB_FDIR_CTL_INIT_DONE BIT(3)
#define TXGBE_RDB_FDIR_CTL_PERFECT_MATCH BIT(4)
#define TXGBE_RDB_FDIR_CTL_DROP_Q(v) FIELD_PREP(GENMASK(14, 8), v)
#define TXGBE_RDB_FDIR_CTL_HASH_BITS(v) FIELD_PREP(GENMASK(23, 20), v)
#define TXGBE_RDB_FDIR_CTL_MAX_LENGTH(v) FIELD_PREP(GENMASK(27, 24), v)
#define TXGBE_RDB_FDIR_CTL_FULL_THRESH(v) FIELD_PREP(GENMASK(31, 28), v)
#define TXGBE_RDB_FDIR_IP6(_i) (0x1950C + ((_i) * 4)) /* 0-2 */
#define TXGBE_RDB_FDIR_SA 0x19518
#define TXGBE_RDB_FDIR_DA 0x1951C
#define TXGBE_RDB_FDIR_PORT 0x19520
#define TXGBE_RDB_FDIR_PORT_DESTINATION_SHIFT 16
#define TXGBE_RDB_FDIR_FLEX 0x19524
#define TXGBE_RDB_FDIR_FLEX_FLEX_SHIFT 16
#define TXGBE_RDB_FDIR_HASH 0x19528
#define TXGBE_RDB_FDIR_HASH_SIG_SW_INDEX(v) FIELD_PREP(GENMASK(31, 16), v)
#define TXGBE_RDB_FDIR_HASH_BUCKET_VALID BIT(15)
#define TXGBE_RDB_FDIR_CMD 0x1952C
#define TXGBE_RDB_FDIR_CMD_CMD_MASK GENMASK(1, 0)
#define TXGBE_RDB_FDIR_CMD_CMD(v) FIELD_PREP(GENMASK(1, 0), v)
#define TXGBE_RDB_FDIR_CMD_CMD_ADD_FLOW TXGBE_RDB_FDIR_CMD_CMD(1)
#define TXGBE_RDB_FDIR_CMD_CMD_REMOVE_FLOW TXGBE_RDB_FDIR_CMD_CMD(2)
#define TXGBE_RDB_FDIR_CMD_CMD_QUERY_REM_FILT TXGBE_RDB_FDIR_CMD_CMD(3)
#define TXGBE_RDB_FDIR_CMD_FILTER_VALID BIT(2)
#define TXGBE_RDB_FDIR_CMD_FILTER_UPDATE BIT(3)
#define TXGBE_RDB_FDIR_CMD_FLOW_TYPE(v) FIELD_PREP(GENMASK(6, 5), v)
#define TXGBE_RDB_FDIR_CMD_DROP BIT(9)
#define TXGBE_RDB_FDIR_CMD_LAST BIT(11)
#define TXGBE_RDB_FDIR_CMD_QUEUE_EN BIT(15)
#define TXGBE_RDB_FDIR_CMD_RX_QUEUE(v) FIELD_PREP(GENMASK(22, 16), v)
#define TXGBE_RDB_FDIR_CMD_VT_POOL(v) FIELD_PREP(GENMASK(29, 24), v)
#define TXGBE_RDB_FDIR_DA4_MSK 0x1953C
#define TXGBE_RDB_FDIR_SA4_MSK 0x19540
#define TXGBE_RDB_FDIR_TCP_MSK 0x19544
#define TXGBE_RDB_FDIR_UDP_MSK 0x19548
#define TXGBE_RDB_FDIR_SCTP_MSK 0x19560
#define TXGBE_RDB_FDIR_HKEY 0x19568
#define TXGBE_RDB_FDIR_SKEY 0x1956C
#define TXGBE_RDB_FDIR_OTHER_MSK 0x19570
#define TXGBE_RDB_FDIR_OTHER_MSK_POOL BIT(2)
#define TXGBE_RDB_FDIR_OTHER_MSK_L4P BIT(3)
#define TXGBE_RDB_FDIR_FLEX_CFG(_i) (0x19580 + ((_i) * 4))
#define TXGBE_RDB_FDIR_FLEX_CFG_FIELD0 GENMASK(7, 0)
#define TXGBE_RDB_FDIR_FLEX_CFG_BASE_MAC FIELD_PREP(GENMASK(1, 0), 0)
#define TXGBE_RDB_FDIR_FLEX_CFG_MSK BIT(2)
#define TXGBE_RDB_FDIR_FLEX_CFG_OFST(v) FIELD_PREP(GENMASK(7, 3), v)
/*************************** Amber Lite Registers ****************************/
#define TXGBE_PX_PF_BME 0x4B8
#define TXGBE_AML_MAC_TX_CFG 0x11000
#define TXGBE_AML_MAC_TX_CFG_TE BIT(0)
#define TXGBE_AML_MAC_TX_CFG_SPEED_MASK GENMASK(30, 27)
#define TXGBE_AML_MAC_TX_CFG_SPEED_40G FIELD_PREP(GENMASK(30, 27), 0)
#define TXGBE_AML_MAC_TX_CFG_SPEED_25G FIELD_PREP(GENMASK(30, 27), 2)
#define TXGBE_AML_MAC_TX_CFG_SPEED_10G FIELD_PREP(GENMASK(30, 27), 8)
#define TXGBE_RDM_RSC_CTL 0x1200C
#define TXGBE_RDM_RSC_CTL_FREE_CTL BIT(7)
/* Checksum and EEPROM pointers */
#define TXGBE_EEPROM_LAST_WORD 0x800
#define TXGBE_EEPROM_CHECKSUM 0x2F
#define TXGBE_EEPROM_SUM 0xBABA
#define TXGBE_EEPROM_VERSION_L 0x1D
#define TXGBE_EEPROM_VERSION_H 0x1E
#define TXGBE_ISCSI_BOOT_CONFIG 0x07
#define TXGBE_EEPROM_I2C_SRART_PTR 0x580
#define TXGBE_EEPROM_I2C_END_PTR 0x800
#define TXGBE_MAX_MSIX_VECTORS 64
#define TXGBE_MAX_FDIR_INDICES 63
#define TXGBE_MAX_RSS_INDICES 63
#define TXGBE_MAX_RX_QUEUES (TXGBE_MAX_FDIR_INDICES + 1)
#define TXGBE_MAX_TX_QUEUES (TXGBE_MAX_FDIR_INDICES + 1)
#define TXGBE_MAX_TXQ 128
#define TXGBE_MAX_RXQ 128
#define TXGBE_RAR_ENTRIES 128
#define TXGBE_MC_TBL_SIZE 128
#define TXGBE_VFT_TBL_SIZE 128
#define TXGBE_RX_PB_SIZE 512
#define TXGBE_TDB_PB_SZ (160 * 1024) /* 160KB Packet Buffer */
#define TXGBE_MAX_VFS_DRV_LIMIT 63
#define TXGBE_DEFAULT_ATR_SAMPLE_RATE 20
/* Software ATR hash keys */
#define TXGBE_ATR_BUCKET_HASH_KEY 0x3DAD14E2
#define TXGBE_ATR_SIGNATURE_HASH_KEY 0x174D3614
/* Software ATR input stream values and masks */
#define TXGBE_ATR_HASH_MASK 0x7fff
#define TXGBE_ATR_L4TYPE_MASK 0x3
#define TXGBE_ATR_L4TYPE_UDP 0x1
#define TXGBE_ATR_L4TYPE_TCP 0x2
#define TXGBE_ATR_L4TYPE_SCTP 0x3
#define TXGBE_ATR_L4TYPE_IPV6_MASK 0x4
#define TXGBE_ATR_L4TYPE_TUNNEL_MASK 0x10
enum txgbe_atr_flow_type {
TXGBE_ATR_FLOW_TYPE_IPV4 = 0x0,
TXGBE_ATR_FLOW_TYPE_UDPV4 = 0x1,
TXGBE_ATR_FLOW_TYPE_TCPV4 = 0x2,
TXGBE_ATR_FLOW_TYPE_SCTPV4 = 0x3,
TXGBE_ATR_FLOW_TYPE_IPV6 = 0x4,
TXGBE_ATR_FLOW_TYPE_UDPV6 = 0x5,
TXGBE_ATR_FLOW_TYPE_TCPV6 = 0x6,
TXGBE_ATR_FLOW_TYPE_SCTPV6 = 0x7,
TXGBE_ATR_FLOW_TYPE_TUNNELED_IPV4 = 0x10,
TXGBE_ATR_FLOW_TYPE_TUNNELED_UDPV4 = 0x11,
TXGBE_ATR_FLOW_TYPE_TUNNELED_TCPV4 = 0x12,
TXGBE_ATR_FLOW_TYPE_TUNNELED_SCTPV4 = 0x13,
TXGBE_ATR_FLOW_TYPE_TUNNELED_IPV6 = 0x14,
TXGBE_ATR_FLOW_TYPE_TUNNELED_UDPV6 = 0x15,
TXGBE_ATR_FLOW_TYPE_TUNNELED_TCPV6 = 0x16,
TXGBE_ATR_FLOW_TYPE_TUNNELED_SCTPV6 = 0x17,
};
/* Flow Director ATR input struct. */
union txgbe_atr_input {
/* Byte layout in order, all values with MSB first:
*
* vm_pool - 1 byte
* flow_type - 1 byte
* vlan_id - 2 bytes
* dst_ip - 16 bytes
* src_ip - 16 bytes
* src_port - 2 bytes
* dst_port - 2 bytes
* flex_bytes - 2 bytes
* bkt_hash - 2 bytes
*/
struct {
u8 vm_pool;
u8 flow_type;
__be16 vlan_id;
__be32 dst_ip[4];
__be32 src_ip[4];
__be16 src_port;
__be16 dst_port;
__be16 flex_bytes;
__be16 bkt_hash;
} formatted;
__be32 dword_stream[11];
};
/* Flow Director compressed ATR hash input struct */
union txgbe_atr_hash_dword {
struct {
u8 vm_pool;
u8 flow_type;
__be16 vlan_id;
} formatted;
__be32 ip;
struct {
__be16 src;
__be16 dst;
} port;
__be16 flex_bytes;
__be32 dword;
};
enum txgbe_fdir_pballoc_type {
TXGBE_FDIR_PBALLOC_NONE = 0,
TXGBE_FDIR_PBALLOC_64K = 1,
TXGBE_FDIR_PBALLOC_128K = 2,
TXGBE_FDIR_PBALLOC_256K = 3,
};
struct txgbe_fdir_filter {
struct hlist_node fdir_node;
union txgbe_atr_input filter;
u16 sw_idx;
u64 action;
};
/* TX/RX descriptor defines */
#define TXGBE_DEFAULT_TXD 512
#define TXGBE_DEFAULT_TX_WORK 256
#if (PAGE_SIZE < 8192)
#define TXGBE_DEFAULT_RXD 512
#define TXGBE_DEFAULT_RX_WORK 256
#else
#define TXGBE_DEFAULT_RXD 256
#define TXGBE_DEFAULT_RX_WORK 128
#endif
#define TXGBE_INTR_MISC(A) BIT((A)->num_q_vectors)
#define TXGBE_INTR_QALL(A) (TXGBE_INTR_MISC(A) - 1)
#define TXGBE_MAX_EITR GENMASK(11, 3)
extern char txgbe_driver_name[];
void txgbe_down(struct wx *wx);
void txgbe_up(struct wx *wx);
int txgbe_setup_tc(struct net_device *dev, u8 tc);
void txgbe_do_reset(struct net_device *netdev);
#define DECLARE_PHY_INTERFACE_MASK_ZERO(name) \
unsigned long name[PHY_INTERFACE_MODE_MAX] = { 0, }
#define TXGBE_LINK_SPEED_UNKNOWN 0
#define TXGBE_LINK_SPEED_10GB_FULL 4
#define TXGBE_LINK_SPEED_25GB_FULL 0x10
#define TXGBE_LINK_SPEED_40GB_FULL 0x20
#define TXGBE_SFF_IDENTIFIER_SFP 0x3
#define TXGBE_SFF_IDENTIFIER_QSFP 0xC
#define TXGBE_SFF_IDENTIFIER_QSFP_PLUS 0xD
#define TXGBE_SFF_IDENTIFIER_QSFP28 0x11
#define TXGBE_SFF_DA_PASSIVE_CABLE 0x4
#define TXGBE_SFF_DA_ACTIVE_CABLE 0x8
#define TXGBE_SFF_DA_SPEC_ACTIVE_LIMIT 0x4
#define TXGBE_SFF_FCPI4_LIMITING 0x3
#define TXGBE_SFF_10GBASESR_CAPABLE 0x10
#define TXGBE_SFF_10GBASELR_CAPABLE 0x20
#define TXGBE_SFF_25GBASESR_CAPABLE 0x2
#define TXGBE_SFF_25GBASELR_CAPABLE 0x3
#define TXGBE_SFF_25GBASEER_CAPABLE 0x4
#define TXGBE_SFF_25GBASECR_91FEC 0xB
#define TXGBE_SFF_25GBASECR_74FEC 0xC
#define TXGBE_SFF_25GBASECR_NOFEC 0xD
#define TXGBE_SFF_ETHERNET_RSRVD BIT(7)
#define TXGBE_SFF_ETHERNET_40G_CR4 BIT(3)
#define TXGBE_SFF_ETHERNET_40G_SR4 BIT(2)
#define TXGBE_SFF_ETHERNET_40G_LR4 BIT(1)
#define TXGBE_SFF_ETHERNET_40G_ACTIVE BIT(0)
#define TXGBE_SFF_ETHERNET_100G_CR4 0xB
#define TXGBE_PHY_FEC_RS BIT(0)
#define TXGBE_PHY_FEC_BASER BIT(1)
#define TXGBE_PHY_FEC_OFF BIT(2)
#define TXGBE_PHY_FEC_AUTO (TXGBE_PHY_FEC_OFF | \
TXGBE_PHY_FEC_BASER |\
TXGBE_PHY_FEC_RS)
#define FW_PHY_GET_LINK_CMD 0xC0
#define FW_PHY_SET_LINK_CMD 0xC1
#define FW_GET_MODULE_INFO_CMD 0xC5
#define FW_READ_EEPROM_CMD 0xC6
struct txgbe_sff_id {
u8 identifier; /* A0H 0x00 */
u8 com_1g_code; /* A0H 0x06 */
u8 com_10g_code; /* A0H 0x03 */
u8 com_25g_code; /* A0H 0x24 */
u8 cable_spec; /* A0H 0x3C */
u8 cable_tech; /* A0H 0x08 */
u8 vendor_oui0; /* A0H 0x25 */
u8 vendor_oui1; /* A0H 0x26 */
u8 vendor_oui2; /* A0H 0x27 */
u8 transceiver_type; /* A0H 0x83 */
u8 sff_opt1; /* A0H 0xC0 */
u8 reserved[5];
};
struct txgbe_hic_get_module_info {
struct wx_hic_hdr hdr;
struct txgbe_sff_id id;
};
struct txgbe_hic_ephy_setlink {
struct wx_hic_hdr hdr;
u8 speed;
u8 duplex;
u8 autoneg;
u8 fec_mode;
u8 resv[4];
};
struct txgbe_hic_ephy_getlink {
struct wx_hic_hdr hdr;
u8 speed;
u8 duplex;
u8 autoneg;
u8 flow_ctl;
u8 power;
u8 fec_mode;
u8 resv[6];
};
struct txgbe_hic_i2c_read {
struct wx_hic_hdr hdr;
__be32 offset;
__be32 length;
u8 page;
u8 bank;
u8 i2c_address;
u8 resv;
};
#define NODE_PROP(_NAME, _PROP) \
(const struct software_node) { \
.name = _NAME, \
.properties = _PROP, \
}
enum txgbe_swnodes {
SWNODE_GPIO = 0,
SWNODE_I2C,
SWNODE_SFP,
SWNODE_PHYLINK,
SWNODE_MAX
};
struct txgbe_nodes {
char gpio_name[32];
char i2c_name[32];
char sfp_name[32];
char phylink_name[32];
struct property_entry gpio_props[2];
struct property_entry i2c_props[4];
struct property_entry sfp_props[9];
struct property_entry phylink_props[3];
struct software_node_ref_args i2c_ref[1];
struct software_node_ref_args gpio0_ref[1];
struct software_node_ref_args gpio1_ref[1];
struct software_node_ref_args gpio2_ref[1];
struct software_node_ref_args gpio3_ref[1];
struct software_node_ref_args gpio4_ref[1];
struct software_node_ref_args gpio5_ref[1];
struct software_node_ref_args sfp_ref[1];
struct software_node swnodes[SWNODE_MAX];
const struct software_node *group[SWNODE_MAX + 1];
};
enum txgbe_misc_irqs {
TXGBE_IRQ_LINK = 0,
TXGBE_IRQ_GPIO,
TXGBE_IRQ_MAX
};
struct txgbe_irq {
struct irq_chip chip;
struct irq_domain *domain;
int nirqs;
int irq;
};
struct txgbe {
struct wx *wx;
struct txgbe_nodes nodes;
struct txgbe_irq misc;
struct phylink_pcs *pcs;
struct platform_device *sfp_dev;
struct platform_device *i2c_dev;
struct clk_lookup *clock;
struct clk *clk;
struct gpio_chip *gpio;
unsigned int link_irq;
unsigned int gpio_irq;
u32 eicr;
/* flow director */
struct hlist_head fdir_filter_list;
union txgbe_atr_input fdir_mask;
int fdir_filter_count;
spinlock_t fdir_perfect_lock; /* spinlock for FDIR */
DECLARE_PHY_INTERFACE_MASK(link_interfaces);
__ETHTOOL_DECLARE_LINK_MODE_MASK(link_support);
__ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
u8 link_port;
};
#endif /* _TXGBE_TYPE_H_ */