Files
linux/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c
Jiawen Wu 3d778e65b4 net: txgbe: remove the redundant data return in SW-FW mailbox
For these two firmware mailbox commands, in txgbe_test_hostif() and
txgbe_set_phy_link_hostif(), there is no need to read data from the
buffer.

Under the current setting, OEM firmware will cause the driver to fail to
probe. Because OEM firmware returns more link information, with a larger
OEM structure txgbe_hic_ephy_getlink. However, the current driver does
not support the OEM function. So just fix it in the way that does not
involve reading the returned data.

Fixes: d84a3ff9aa ("net: txgbe: Restrict the use of mismatched FW versions")
Cc: stable@vger.kernel.org
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
Link: https://patch.msgid.link/2914AB0BC6158DDA+20260119065935.6015-1-jiawenwu@trustnetic.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2026-01-20 18:35:56 -08:00

531 lines
14 KiB
C

// 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++) {
value = rd32a(wx, WX_FW2SW_MBOX, i + offset);
le32_to_cpus(&value);
memcpy(data, &value, 4);
data += 4;
}
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;
}
int 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");
return err;
}
return 0;
}
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(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(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;
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 &&
id->identifier != TXGBE_SFF_IDENTIFIER_QSFP &&
id->identifier != TXGBE_SFF_IDENTIFIER_QSFP_PLUS &&
id->identifier != TXGBE_SFF_IDENTIFIER_QSFP28) {
wx_err(wx, "Invalid module\n");
return -ENODEV;
}
if (id->transceiver_type == 0xFF)
return txgbe_sfp_to_linkmodes(wx, id);
return txgbe_qsfp_to_linkmodes(wx, id);
}
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");
return err;
}
wx->phylink = phylink;
return 0;
}