// SPDX-License-Identifier: GPL-2.0+
/* Microchip Sparx5 Switch driver
*
* Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
*/
#include <linux/module.h>
#include <linux/phy/phy.h>
#include <net/dcbnl.h>
#include "sparx5_main_regs.h"
#include "sparx5_main.h"
#include "sparx5_port.h"
#define SPX5_ETYPE_TAG_C 0x8100
#define SPX5_ETYPE_TAG_S 0x88a8
#define SPX5_WAIT_US 1000
#define SPX5_WAIT_MAX_US 2000
enum port_error {
SPX5_PERR_SPEED,
SPX5_PERR_IFTYPE,
};
#define PAUSE_DISCARD 0xC
#define ETH_MAXLEN (ETH_DATA_LEN + ETH_HLEN + ETH_FCS_LEN)
static void decode_sgmii_word(u16 lp_abil, struct sparx5_port_status *status)
{
status->an_complete = true;
if (!(lp_abil & LPA_SGMII_LINK)) {
status->link = false;
return;
}
switch (lp_abil & LPA_SGMII_SPD_MASK) {
case LPA_SGMII_10:
status->speed = SPEED_10;
break;
case LPA_SGMII_100:
status->speed = SPEED_100;
break;
case LPA_SGMII_1000:
status->speed = SPEED_1000;
break;
default:
status->link = false;
return;
}
if (lp_abil & LPA_SGMII_FULL_DUPLEX)
status->duplex = DUPLEX_FULL;
else
status->duplex = DUPLEX_HALF;
}
static void decode_cl37_word(u16 lp_abil, uint16_t ld_abil, struct sparx5_port_status *status)
{
status->link = !(lp_abil & ADVERTISE_RFAULT) && status->link;
status->an_complete = true;
status->duplex = (ADVERTISE_1000XFULL & lp_abil) ?
DUPLEX_FULL : DUPLEX_UNKNOWN; // 1G HDX not supported
if ((ld_abil & ADVERTISE_1000XPAUSE) &&
(lp_abil & ADVERTISE_1000XPAUSE)) {
status->pause = MLO_PAUSE_RX | MLO_PAUSE_TX;
} else if ((ld_abil & ADVERTISE_1000XPSE_ASYM) &&
(lp_abil & ADVERTISE_1000XPSE_ASYM)) {
status->pause |= (lp_abil & ADVERTISE_1000XPAUSE) ?
MLO_PAUSE_TX : 0;
status->pause |= (ld_abil & ADVERTISE_1000XPAUSE) ?
MLO_PAUSE_RX : 0;
} else {
status->pause = MLO_PAUSE_NONE;
}
}
static int sparx5_get_dev2g5_status(struct sparx5 *sparx5,
struct sparx5_port *port,
struct sparx5_port_status *status)
{
u32 portno = port->portno;
u16 lp_adv, ld_adv;
u32 value;
/* Get PCS Link down sticky */