Commit bb5dbf2c authored by Volodymyr Mytnyk's avatar Volodymyr Mytnyk Committed by David S. Miller
Browse files

net: marvell: prestera: add firmware v4.0 support



Add firmware (FW) version 4.0 support for Marvell Prestera
driver.

Major changes have been made to new v4.0 FW ABI to add support
of new features, introduce the stability of the FW ABI and ensure
better forward compatibility for the future driver vesrions.

Current v4.0 FW feature set support does not expect any changes
to ABI, as it was defined and tested through long period of time.
The ABI may be extended in case of new features, but it will not
break the backward compatibility.

ABI major changes done in v4.0:
- L1 ABI, where MAC and PHY API configuration are split.
- ACL has been split to low-level TCAM and Counters ABI
  to provide more HW ACL capabilities for future driver
  versions.

To support backward support, the addition compatibility layer is
required in the driver which will have two different codebase under
"if FW-VER elif FW-VER else" conditions that will be removed
in the future anyway, So, the idea was to break backward support
and focus on more stable FW instead of supporting old version
with very minimal and limited set of features/capabilities.

Improve FW msg validation:
 * Use __le64, __le32, __le16 types in msg to/from FW to
   catch endian mismatch by sparse.
 * Use BUILD_BUG_ON for structures sent/recv to/from FW.

Co-developed-by: default avatarVadym Kochan <vkochan@marvell.com>
Signed-off-by: default avatarVadym Kochan <vkochan@marvell.com>
Signed-off-by: default avatarYevhen Orlov <yevhen.orlov@plvision.eu>
Signed-off-by: default avatarTaras Chornyi <tchornyi@marvell.com>
Signed-off-by: default avatarVolodymyr Mytnyk <vmytnyk@marvell.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c52ef04d
Loading
Loading
Loading
Loading
+64 −5
Original line number Diff line number Diff line
@@ -53,6 +53,8 @@ struct prestera_port_stats {
	u64 good_octets_sent;
};

#define PRESTERA_AP_PORT_MAX   (10)

struct prestera_port_caps {
	u64 supp_link_modes;
	u8 supp_fec;
@@ -69,6 +71,39 @@ struct prestera_lag {

struct prestera_flow_block;

struct prestera_port_mac_state {
	u32 mode;
	u32 speed;
	bool oper;
	u8 duplex;
	u8 fc;
	u8 fec;
};

struct prestera_port_phy_state {
	u64 lmode_bmap;
	struct {
		bool pause;
		bool asym_pause;
	} remote_fc;
	u8 mdix;
};

struct prestera_port_mac_config {
	u32 mode;
	u32 speed;
	bool admin;
	u8 inband;
	u8 duplex;
	u8 fec;
};

struct prestera_port_phy_config {
	u32 mode;
	bool admin;
	u8 mdix;
};

struct prestera_port {
	struct net_device *dev;
	struct prestera_switch *sw;
@@ -91,6 +126,10 @@ struct prestera_port {
		struct prestera_port_stats stats;
		struct delayed_work caching_dw;
	} cached_hw_stats;
	struct prestera_port_mac_config cfg_mac;
	struct prestera_port_phy_config cfg_phy;
	struct prestera_port_mac_state state_mac;
	struct prestera_port_phy_state state_phy;
};

struct prestera_device {
@@ -107,7 +146,7 @@ struct prestera_device {
	int (*recv_msg)(struct prestera_device *dev, void *msg, size_t size);

	/* called by higher layer to send request to the firmware */
	int (*send_req)(struct prestera_device *dev, void *in_msg,
	int (*send_req)(struct prestera_device *dev, int qid, void *in_msg,
			size_t in_size, void *out_msg, size_t out_size,
			unsigned int wait);
};
@@ -129,13 +168,28 @@ enum prestera_rxtx_event_id {

enum prestera_port_event_id {
	PRESTERA_PORT_EVENT_UNSPEC,
	PRESTERA_PORT_EVENT_STATE_CHANGED,
	PRESTERA_PORT_EVENT_MAC_STATE_CHANGED,
};

struct prestera_port_event {
	u32 port_id;
	union {
		u32 oper_state;
		struct {
			u32 mode;
			u32 speed;
			u8 oper;
			u8 duplex;
			u8 fc;
			u8 fec;
		} mac;
		struct {
			u64 lmode_bmap;
			struct {
				bool pause;
				bool asym_pause;
			} remote_fc;
			u8 mdix;
		} phy;
	} data;
};

@@ -223,11 +277,16 @@ void prestera_device_unregister(struct prestera_device *dev);
struct prestera_port *prestera_port_find_by_hwid(struct prestera_switch *sw,
						 u32 dev_id, u32 hw_id);

int prestera_port_autoneg_set(struct prestera_port *port, bool enable,
			      u64 adver_link_modes, u8 adver_fec);
int prestera_port_autoneg_set(struct prestera_port *port, u64 link_modes);

struct prestera_port *prestera_find_port(struct prestera_switch *sw, u32 id);

int prestera_port_cfg_mac_read(struct prestera_port *port,
			       struct prestera_port_mac_config *cfg);

int prestera_port_cfg_mac_write(struct prestera_port *port,
				struct prestera_port_mac_config *cfg);

struct prestera_port *prestera_port_dev_lower_find(struct net_device *dev);

int prestera_port_pvid_set(struct prestera_port *port, u16 vid);
+128 −91
Original line number Diff line number Diff line
@@ -323,7 +323,6 @@ static int prestera_port_type_set(const struct ethtool_link_ksettings *ecmd,
{
	u32 new_mode = PRESTERA_LINK_MODE_MAX;
	u32 type, mode;
	int err;

	for (type = 0; type < PRESTERA_PORT_TYPE_MAX; type++) {
		if (port_types[type].eth_type == ecmd->base.port &&
@@ -348,13 +347,8 @@ static int prestera_port_type_set(const struct ethtool_link_ksettings *ecmd,
		}
	}

	if (new_mode < PRESTERA_LINK_MODE_MAX)
		err = prestera_hw_port_link_mode_set(port, new_mode);
	else
		err = -EINVAL;

	if (err)
		return err;
	if (new_mode >= PRESTERA_LINK_MODE_MAX)
		return -EINVAL;

	port->caps.type = type;
	port->autoneg = false;
@@ -434,13 +428,21 @@ static void prestera_port_supp_types_get(struct ethtool_link_ksettings *ecmd,
static void prestera_port_remote_cap_get(struct ethtool_link_ksettings *ecmd,
					 struct prestera_port *port)
{
	struct prestera_port_phy_state *state = &port->state_phy;
	bool asym_pause;
	bool pause;
	u64 bitmap;
	int err;

	err = prestera_hw_port_remote_cap_get(port, &bitmap);
	if (!err) {
	err = prestera_hw_port_phy_mode_get(port, NULL, &state->lmode_bmap,
					    &state->remote_fc.pause,
					    &state->remote_fc.asym_pause);
	if (err)
		netdev_warn(port->dev, "Remote link caps get failed %d",
			    port->caps.transceiver);

	bitmap = state->lmode_bmap;

	prestera_modes_to_eth(ecmd->link_modes.lp_advertising,
			      bitmap, 0, PRESTERA_PORT_TYPE_NONE);

@@ -450,11 +452,9 @@ static void prestera_port_remote_cap_get(struct ethtool_link_ksettings *ecmd,
						     lp_advertising,
						     Autoneg);
	}
	}

	err = prestera_hw_port_remote_fc_get(port, &pause, &asym_pause);
	if (err)
		return;
	pause = state->remote_fc.pause;
	asym_pause = state->remote_fc.asym_pause;

	if (pause)
		ethtool_link_ksettings_add_link_mode(ecmd,
@@ -466,30 +466,46 @@ static void prestera_port_remote_cap_get(struct ethtool_link_ksettings *ecmd,
						     Asym_Pause);
}

static void prestera_port_speed_get(struct ethtool_link_ksettings *ecmd,
static void prestera_port_link_mode_get(struct ethtool_link_ksettings *ecmd,
					struct prestera_port *port)
{
	struct prestera_port_mac_state *state = &port->state_mac;
	u32 speed;
	u8 duplex;
	int err;

	err = prestera_hw_port_speed_get(port, &speed);
	ecmd->base.speed = err ? SPEED_UNKNOWN : speed;
	if (!port->state_mac.oper)
		return;

	if (state->speed == SPEED_UNKNOWN || state->duplex == DUPLEX_UNKNOWN) {
		err = prestera_hw_port_mac_mode_get(port, NULL, &speed,
						    &duplex, NULL);
		if (err) {
			state->speed = SPEED_UNKNOWN;
			state->duplex = DUPLEX_UNKNOWN;
		} else {
			state->speed = speed;
			state->duplex = duplex == PRESTERA_PORT_DUPLEX_FULL ?
					  DUPLEX_FULL : DUPLEX_HALF;
		}
	}

	ecmd->base.speed = port->state_mac.speed;
	ecmd->base.duplex = port->state_mac.duplex;
}

static void prestera_port_duplex_get(struct ethtool_link_ksettings *ecmd,
static void prestera_port_mdix_get(struct ethtool_link_ksettings *ecmd,
				   struct prestera_port *port)
{
	u8 duplex;
	int err;
	struct prestera_port_phy_state *state = &port->state_phy;

	err = prestera_hw_port_duplex_get(port, &duplex);
	if (err) {
		ecmd->base.duplex = DUPLEX_UNKNOWN;
		return;
	if (prestera_hw_port_phy_mode_get(port, &state->mdix, NULL, NULL, NULL)) {
		netdev_warn(port->dev, "MDIX params get failed");
		state->mdix = ETH_TP_MDI_INVALID;
	}

	ecmd->base.duplex = duplex == PRESTERA_PORT_DUPLEX_FULL ?
			    DUPLEX_FULL : DUPLEX_HALF;
	ecmd->base.eth_tp_mdix = port->state_phy.mdix;
	ecmd->base.eth_tp_mdix_ctrl = port->cfg_phy.mdix;
}

static int
@@ -501,6 +517,8 @@ prestera_ethtool_get_link_ksettings(struct net_device *dev,
	ethtool_link_ksettings_zero_link_mode(ecmd, supported);
	ethtool_link_ksettings_zero_link_mode(ecmd, advertising);
	ethtool_link_ksettings_zero_link_mode(ecmd, lp_advertising);
	ecmd->base.speed = SPEED_UNKNOWN;
	ecmd->base.duplex = DUPLEX_UNKNOWN;

	ecmd->base.autoneg = port->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE;

@@ -521,13 +539,8 @@ prestera_ethtool_get_link_ksettings(struct net_device *dev,

	prestera_port_supp_types_get(ecmd, port);

	if (netif_carrier_ok(dev)) {
		prestera_port_speed_get(ecmd, port);
		prestera_port_duplex_get(ecmd, port);
	} else {
		ecmd->base.speed = SPEED_UNKNOWN;
		ecmd->base.duplex = DUPLEX_UNKNOWN;
	}
	if (netif_carrier_ok(dev))
		prestera_port_link_mode_get(ecmd, port);

	ecmd->base.port = prestera_port_type_get(port);

@@ -545,8 +558,7 @@ prestera_ethtool_get_link_ksettings(struct net_device *dev,

	if (port->caps.type == PRESTERA_PORT_TYPE_TP &&
	    port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER)
		prestera_hw_port_mdix_get(port, &ecmd->base.eth_tp_mdix,
					  &ecmd->base.eth_tp_mdix_ctrl);
		prestera_port_mdix_get(ecmd, port);

	return 0;
}
@@ -556,11 +568,16 @@ static int prestera_port_mdix_set(const struct ethtool_link_ksettings *ecmd,
{
	if (ecmd->base.eth_tp_mdix_ctrl != ETH_TP_MDI_INVALID &&
	    port->caps.transceiver ==  PRESTERA_PORT_TCVR_COPPER &&
	    port->caps.type == PRESTERA_PORT_TYPE_TP)
		return prestera_hw_port_mdix_set(port,
						 ecmd->base.eth_tp_mdix_ctrl);

	    port->caps.type == PRESTERA_PORT_TYPE_TP) {
		port->cfg_phy.mdix = ecmd->base.eth_tp_mdix_ctrl;
		return prestera_hw_port_phy_mode_set(port, port->cfg_phy.admin,
						     port->autoneg,
						     port->cfg_phy.mode,
						     port->adver_link_modes,
						     port->cfg_phy.mdix);
	}
	return 0;

}

static int prestera_port_link_mode_set(struct prestera_port *port,
@@ -568,12 +585,15 @@ static int prestera_port_link_mode_set(struct prestera_port *port,
{
	u32 new_mode = PRESTERA_LINK_MODE_MAX;
	u32 mode;
	int err;

	for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
		if (speed != port_link_modes[mode].speed)
		if (speed != SPEED_UNKNOWN &&
		    speed != port_link_modes[mode].speed)
			continue;

		if (duplex != port_link_modes[mode].duplex)
		if (duplex != DUPLEX_UNKNOWN &&
		    duplex != port_link_modes[mode].duplex)
			continue;

		if (!(port_link_modes[mode].pr_mask &
@@ -590,36 +610,31 @@ static int prestera_port_link_mode_set(struct prestera_port *port,
	if (new_mode == PRESTERA_LINK_MODE_MAX)
		return -EOPNOTSUPP;

	return prestera_hw_port_link_mode_set(port, new_mode);
	err = prestera_hw_port_phy_mode_set(port, port->cfg_phy.admin,
					    false, new_mode, 0,
					    port->cfg_phy.mdix);
	if (err)
		return err;

	port->adver_fec = BIT(PRESTERA_PORT_FEC_OFF);
	port->adver_link_modes = 0;
	port->cfg_phy.mode = new_mode;
	port->autoneg = false;

	return 0;
}

static int
prestera_port_speed_duplex_set(const struct ethtool_link_ksettings *ecmd,
			       struct prestera_port *port)
{
	u32 curr_mode;
	u8 duplex;
	u32 speed;
	int err;

	err = prestera_hw_port_link_mode_get(port, &curr_mode);
	if (err)
		return err;
	if (curr_mode >= PRESTERA_LINK_MODE_MAX)
		return -EINVAL;
	u8 duplex = DUPLEX_UNKNOWN;

	if (ecmd->base.duplex != DUPLEX_UNKNOWN)
		duplex = ecmd->base.duplex == DUPLEX_FULL ?
			 PRESTERA_PORT_DUPLEX_FULL : PRESTERA_PORT_DUPLEX_HALF;
	else
		duplex = port_link_modes[curr_mode].duplex;

	if (ecmd->base.speed != SPEED_UNKNOWN)
		speed = ecmd->base.speed;
	else
		speed = port_link_modes[curr_mode].speed;

	return prestera_port_link_mode_set(port, speed, duplex,
	return prestera_port_link_mode_set(port, ecmd->base.speed, duplex,
					   port->caps.type);
}

@@ -645,19 +660,12 @@ prestera_ethtool_set_link_ksettings(struct net_device *dev,
	prestera_modes_from_eth(ecmd->link_modes.advertising, &adver_modes,
				&adver_fec, port->caps.type);

	err = prestera_port_autoneg_set(port,
					ecmd->base.autoneg == AUTONEG_ENABLE,
					adver_modes, adver_fec);
	if (err)
		return err;

	if (ecmd->base.autoneg == AUTONEG_DISABLE) {
	if (ecmd->base.autoneg == AUTONEG_ENABLE)
		err = prestera_port_autoneg_set(port, adver_modes);
	else
		err = prestera_port_speed_duplex_set(ecmd, port);
		if (err)
			return err;
	}

	return 0;
	return err;
}

static int prestera_ethtool_get_fecparam(struct net_device *dev,
@@ -668,7 +676,7 @@ static int prestera_ethtool_get_fecparam(struct net_device *dev,
	u32 mode;
	int err;

	err = prestera_hw_port_fec_get(port, &active);
	err = prestera_hw_port_mac_mode_get(port, NULL, NULL, NULL, &active);
	if (err)
		return err;

@@ -693,18 +701,19 @@ static int prestera_ethtool_set_fecparam(struct net_device *dev,
					 struct ethtool_fecparam *fecparam)
{
	struct prestera_port *port = netdev_priv(dev);
	u8 fec, active;
	struct prestera_port_mac_config cfg_mac;
	u32 mode;
	int err;
	u8 fec;

	if (port->autoneg) {
		netdev_err(dev, "FEC set is not allowed while autoneg is on\n");
		return -EINVAL;
	}

	err = prestera_hw_port_fec_get(port, &active);
	if (err)
		return err;
	if (port->caps.transceiver == PRESTERA_PORT_TCVR_SFP) {
		netdev_err(dev, "FEC set is not allowed on non-SFP ports\n");
		return -EINVAL;
	}

	fec = PRESTERA_PORT_FEC_MAX;
	for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) {
@@ -715,13 +724,19 @@ static int prestera_ethtool_set_fecparam(struct net_device *dev,
		}
	}

	if (fec == active)
	prestera_port_cfg_mac_read(port, &cfg_mac);

	if (fec == cfg_mac.fec)
		return 0;

	if (fec == PRESTERA_PORT_FEC_MAX)
		return -EOPNOTSUPP;
	if (fec == PRESTERA_PORT_FEC_MAX) {
		netdev_err(dev, "Unsupported FEC requested");
		return -EINVAL;
	}

	cfg_mac.fec = fec;

	return prestera_hw_port_fec_set(port, fec);
	return prestera_port_cfg_mac_write(port, &cfg_mac);
}

static int prestera_ethtool_get_sset_count(struct net_device *dev, int sset)
@@ -766,6 +781,28 @@ static int prestera_ethtool_nway_reset(struct net_device *dev)
	return -EINVAL;
}

void prestera_ethtool_port_state_changed(struct prestera_port *port,
					 struct prestera_port_event *evt)
{
	struct prestera_port_mac_state *smac = &port->state_mac;

	smac->oper = evt->data.mac.oper;

	if (smac->oper) {
		smac->mode = evt->data.mac.mode;
		smac->speed = evt->data.mac.speed;
		smac->duplex = evt->data.mac.duplex;
		smac->fc = evt->data.mac.fc;
		smac->fec = evt->data.mac.fec;
	} else {
		smac->mode = PRESTERA_MAC_MODE_MAX;
		smac->speed = SPEED_UNKNOWN;
		smac->duplex = DUPLEX_UNKNOWN;
		smac->fc = 0;
		smac->fec = 0;
	}
}

const struct ethtool_ops prestera_ethtool_ops = {
	.get_drvinfo = prestera_ethtool_get_drvinfo,
	.get_link_ksettings = prestera_ethtool_get_link_ksettings,
+6 −0
Original line number Diff line number Diff line
@@ -6,6 +6,12 @@

#include <linux/ethtool.h>

struct prestera_port_event;
struct prestera_port;

extern const struct ethtool_ops prestera_ethtool_ops;

void prestera_ethtool_port_state_changed(struct prestera_port *port,
					 struct prestera_port_event *evt);

#endif /* _PRESTERA_ETHTOOL_H_ */
+538 −526

File changed.

Preview size limit exceeded, changes collapsed.

+30 −17
Original line number Diff line number Diff line
@@ -19,6 +19,23 @@ enum prestera_fdb_flush_mode {
					| PRESTERA_FDB_FLUSH_MODE_STATIC,
};

enum {
	PRESTERA_MAC_MODE_INTERNAL,
	PRESTERA_MAC_MODE_SGMII,
	PRESTERA_MAC_MODE_1000BASE_X,
	PRESTERA_MAC_MODE_KR,
	PRESTERA_MAC_MODE_KR2,
	PRESTERA_MAC_MODE_KR4,
	PRESTERA_MAC_MODE_CR,
	PRESTERA_MAC_MODE_CR2,
	PRESTERA_MAC_MODE_CR4,
	PRESTERA_MAC_MODE_SR_LR,
	PRESTERA_MAC_MODE_SR_LR2,
	PRESTERA_MAC_MODE_SR_LR4,

	PRESTERA_MAC_MODE_MAX
};

enum {
	PRESTERA_LINK_MODE_10baseT_Half,
	PRESTERA_LINK_MODE_10baseT_Full,
@@ -116,32 +133,29 @@ int prestera_hw_switch_mac_set(struct prestera_switch *sw, const char *mac);
/* Port API */
int prestera_hw_port_info_get(const struct prestera_port *port,
			      u32 *dev_id, u32 *hw_id, u16 *fp_id);
int prestera_hw_port_state_set(const struct prestera_port *port,
			       bool admin_state);

int prestera_hw_port_mac_mode_get(const struct prestera_port *port,
				  u32 *mode, u32 *speed, u8 *duplex, u8 *fec);
int prestera_hw_port_mac_mode_set(const struct prestera_port *port,
				  bool admin, u32 mode, u8 inband,
				  u32 speed, u8 duplex, u8 fec);
int prestera_hw_port_phy_mode_get(const struct prestera_port *port,
				  u8 *mdix, u64 *lmode_bmap,
				  bool *fc_pause, bool *fc_asym);
int prestera_hw_port_phy_mode_set(const struct prestera_port *port,
				  bool admin, bool adv, u32 mode, u64 modes,
				  u8 mdix);

int prestera_hw_port_mtu_set(const struct prestera_port *port, u32 mtu);
int prestera_hw_port_mtu_get(const struct prestera_port *port, u32 *mtu);
int prestera_hw_port_mac_set(const struct prestera_port *port, const char *mac);
int prestera_hw_port_mac_get(const struct prestera_port *port, char *mac);
int prestera_hw_port_cap_get(const struct prestera_port *port,
			     struct prestera_port_caps *caps);
int prestera_hw_port_remote_cap_get(const struct prestera_port *port,
				    u64 *link_mode_bitmap);
int prestera_hw_port_remote_fc_get(const struct prestera_port *port,
				   bool *pause, bool *asym_pause);
int prestera_hw_port_type_get(const struct prestera_port *port, u8 *type);
int prestera_hw_port_fec_get(const struct prestera_port *port, u8 *fec);
int prestera_hw_port_fec_set(const struct prestera_port *port, u8 fec);
int prestera_hw_port_autoneg_set(const struct prestera_port *port,
				 bool autoneg, u64 link_modes, u8 fec);
int prestera_hw_port_autoneg_restart(struct prestera_port *port);
int prestera_hw_port_duplex_get(const struct prestera_port *port, u8 *duplex);
int prestera_hw_port_stats_get(const struct prestera_port *port,
			       struct prestera_port_stats *stats);
int prestera_hw_port_link_mode_set(const struct prestera_port *port, u32 mode);
int prestera_hw_port_link_mode_get(const struct prestera_port *port, u32 *mode);
int prestera_hw_port_mdix_get(const struct prestera_port *port, u8 *status,
			      u8 *admin_mode);
int prestera_hw_port_mdix_set(const struct prestera_port *port, u8 mode);
int prestera_hw_port_speed_get(const struct prestera_port *port, u32 *speed);
int prestera_hw_port_learning_set(struct prestera_port *port, bool enable);
int prestera_hw_port_flood_set(struct prestera_port *port, unsigned long mask,
@@ -206,7 +220,6 @@ void prestera_hw_event_handler_unregister(struct prestera_switch *sw,
/* RX/TX */
int prestera_hw_rxtx_init(struct prestera_switch *sw,
			  struct prestera_rxtx_params *params);
int prestera_hw_rxtx_port_init(struct prestera_port *port);

/* LAG API */
int prestera_hw_lag_member_add(struct prestera_port *port, u16 lag_id);
Loading