162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/etherdevice.h> 562306a36Sopenharmony_ci#include <linux/jiffies.h> 662306a36Sopenharmony_ci#include <linux/list.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/netdev_features.h> 962306a36Sopenharmony_ci#include <linux/of.h> 1062306a36Sopenharmony_ci#include <linux/of_net.h> 1162306a36Sopenharmony_ci#include <linux/if_vlan.h> 1262306a36Sopenharmony_ci#include <linux/phylink.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "prestera.h" 1562306a36Sopenharmony_ci#include "prestera_hw.h" 1662306a36Sopenharmony_ci#include "prestera_acl.h" 1762306a36Sopenharmony_ci#include "prestera_flow.h" 1862306a36Sopenharmony_ci#include "prestera_span.h" 1962306a36Sopenharmony_ci#include "prestera_rxtx.h" 2062306a36Sopenharmony_ci#include "prestera_devlink.h" 2162306a36Sopenharmony_ci#include "prestera_ethtool.h" 2262306a36Sopenharmony_ci#include "prestera_counter.h" 2362306a36Sopenharmony_ci#include "prestera_switchdev.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define PRESTERA_MTU_DEFAULT 1536 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define PRESTERA_STATS_DELAY_MS 1000 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define PRESTERA_MAC_ADDR_NUM_MAX 255 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic struct workqueue_struct *prestera_wq; 3262306a36Sopenharmony_cistatic struct workqueue_struct *prestera_owq; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_civoid prestera_queue_work(struct work_struct *work) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci queue_work(prestera_owq, work); 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_civoid prestera_queue_delayed_work(struct delayed_work *work, unsigned long delay) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci queue_delayed_work(prestera_wq, work, delay); 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_civoid prestera_queue_drain(void) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci drain_workqueue(prestera_wq); 4762306a36Sopenharmony_ci drain_workqueue(prestera_owq); 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ciint prestera_port_learning_set(struct prestera_port *port, bool learn) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci return prestera_hw_port_learning_set(port, learn); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ciint prestera_port_uc_flood_set(struct prestera_port *port, bool flood) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci return prestera_hw_port_uc_flood_set(port, flood); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ciint prestera_port_mc_flood_set(struct prestera_port *port, bool flood) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci return prestera_hw_port_mc_flood_set(port, flood); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ciint prestera_port_br_locked_set(struct prestera_port *port, bool br_locked) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci return prestera_hw_port_br_locked_set(port, br_locked); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ciint prestera_port_pvid_set(struct prestera_port *port, u16 vid) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci enum prestera_accept_frm_type frm_type; 7362306a36Sopenharmony_ci int err; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci frm_type = PRESTERA_ACCEPT_FRAME_TYPE_TAGGED; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (vid) { 7862306a36Sopenharmony_ci err = prestera_hw_vlan_port_vid_set(port, vid); 7962306a36Sopenharmony_ci if (err) 8062306a36Sopenharmony_ci return err; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci frm_type = PRESTERA_ACCEPT_FRAME_TYPE_ALL; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci err = prestera_hw_port_accept_frm_type(port, frm_type); 8662306a36Sopenharmony_ci if (err && frm_type == PRESTERA_ACCEPT_FRAME_TYPE_ALL) 8762306a36Sopenharmony_ci prestera_hw_vlan_port_vid_set(port, port->pvid); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci port->pvid = vid; 9062306a36Sopenharmony_ci return 0; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistruct prestera_port *prestera_port_find_by_hwid(struct prestera_switch *sw, 9462306a36Sopenharmony_ci u32 dev_id, u32 hw_id) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci struct prestera_port *port = NULL, *tmp; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci read_lock(&sw->port_list_lock); 9962306a36Sopenharmony_ci list_for_each_entry(tmp, &sw->port_list, list) { 10062306a36Sopenharmony_ci if (tmp->dev_id == dev_id && tmp->hw_id == hw_id) { 10162306a36Sopenharmony_ci port = tmp; 10262306a36Sopenharmony_ci break; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci read_unlock(&sw->port_list_lock); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci return port; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistruct prestera_port *prestera_find_port(struct prestera_switch *sw, u32 id) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct prestera_port *port = NULL, *tmp; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci read_lock(&sw->port_list_lock); 11562306a36Sopenharmony_ci list_for_each_entry(tmp, &sw->port_list, list) { 11662306a36Sopenharmony_ci if (tmp->id == id) { 11762306a36Sopenharmony_ci port = tmp; 11862306a36Sopenharmony_ci break; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci read_unlock(&sw->port_list_lock); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return port; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistruct prestera_switch *prestera_switch_get(struct net_device *dev) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci struct prestera_port *port; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci port = prestera_port_dev_lower_find(dev); 13162306a36Sopenharmony_ci return port ? port->sw : NULL; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ciint prestera_port_cfg_mac_read(struct prestera_port *port, 13562306a36Sopenharmony_ci struct prestera_port_mac_config *cfg) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci *cfg = port->cfg_mac; 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ciint prestera_port_cfg_mac_write(struct prestera_port *port, 14262306a36Sopenharmony_ci struct prestera_port_mac_config *cfg) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci int err; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci err = prestera_hw_port_mac_mode_set(port, cfg->admin, 14762306a36Sopenharmony_ci cfg->mode, cfg->inband, cfg->speed, 14862306a36Sopenharmony_ci cfg->duplex, cfg->fec); 14962306a36Sopenharmony_ci if (err) 15062306a36Sopenharmony_ci return err; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci port->cfg_mac = *cfg; 15362306a36Sopenharmony_ci return 0; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic int prestera_port_open(struct net_device *dev) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 15962306a36Sopenharmony_ci struct prestera_port_mac_config cfg_mac; 16062306a36Sopenharmony_ci int err = 0; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (port->phy_link) { 16362306a36Sopenharmony_ci phylink_start(port->phy_link); 16462306a36Sopenharmony_ci } else { 16562306a36Sopenharmony_ci if (port->caps.transceiver == PRESTERA_PORT_TCVR_SFP) { 16662306a36Sopenharmony_ci err = prestera_port_cfg_mac_read(port, &cfg_mac); 16762306a36Sopenharmony_ci if (!err) { 16862306a36Sopenharmony_ci cfg_mac.admin = true; 16962306a36Sopenharmony_ci err = prestera_port_cfg_mac_write(port, 17062306a36Sopenharmony_ci &cfg_mac); 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci } else { 17362306a36Sopenharmony_ci port->cfg_phy.admin = true; 17462306a36Sopenharmony_ci err = prestera_hw_port_phy_mode_set(port, true, 17562306a36Sopenharmony_ci port->autoneg, 17662306a36Sopenharmony_ci port->cfg_phy.mode, 17762306a36Sopenharmony_ci port->adver_link_modes, 17862306a36Sopenharmony_ci port->cfg_phy.mdix); 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci netif_start_queue(dev); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return err; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic int prestera_port_close(struct net_device *dev) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 19062306a36Sopenharmony_ci struct prestera_port_mac_config cfg_mac; 19162306a36Sopenharmony_ci int err = 0; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci netif_stop_queue(dev); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (port->phy_link) { 19662306a36Sopenharmony_ci phylink_stop(port->phy_link); 19762306a36Sopenharmony_ci phylink_disconnect_phy(port->phy_link); 19862306a36Sopenharmony_ci err = prestera_port_cfg_mac_read(port, &cfg_mac); 19962306a36Sopenharmony_ci if (!err) { 20062306a36Sopenharmony_ci cfg_mac.admin = false; 20162306a36Sopenharmony_ci prestera_port_cfg_mac_write(port, &cfg_mac); 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci } else { 20462306a36Sopenharmony_ci if (port->caps.transceiver == PRESTERA_PORT_TCVR_SFP) { 20562306a36Sopenharmony_ci err = prestera_port_cfg_mac_read(port, &cfg_mac); 20662306a36Sopenharmony_ci if (!err) { 20762306a36Sopenharmony_ci cfg_mac.admin = false; 20862306a36Sopenharmony_ci prestera_port_cfg_mac_write(port, &cfg_mac); 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci } else { 21162306a36Sopenharmony_ci port->cfg_phy.admin = false; 21262306a36Sopenharmony_ci err = prestera_hw_port_phy_mode_set(port, false, port->autoneg, 21362306a36Sopenharmony_ci port->cfg_phy.mode, 21462306a36Sopenharmony_ci port->adver_link_modes, 21562306a36Sopenharmony_ci port->cfg_phy.mdix); 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci return err; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic void 22362306a36Sopenharmony_ciprestera_port_mac_state_cache_read(struct prestera_port *port, 22462306a36Sopenharmony_ci struct prestera_port_mac_state *state) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci spin_lock(&port->state_mac_lock); 22762306a36Sopenharmony_ci *state = port->state_mac; 22862306a36Sopenharmony_ci spin_unlock(&port->state_mac_lock); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic void 23262306a36Sopenharmony_ciprestera_port_mac_state_cache_write(struct prestera_port *port, 23362306a36Sopenharmony_ci struct prestera_port_mac_state *state) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci spin_lock(&port->state_mac_lock); 23662306a36Sopenharmony_ci port->state_mac = *state; 23762306a36Sopenharmony_ci spin_unlock(&port->state_mac_lock); 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic struct prestera_port *prestera_pcs_to_port(struct phylink_pcs *pcs) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci return container_of(pcs, struct prestera_port, phylink_pcs); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic void prestera_mac_config(struct phylink_config *config, 24662306a36Sopenharmony_ci unsigned int an_mode, 24762306a36Sopenharmony_ci const struct phylink_link_state *state) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic void prestera_mac_link_down(struct phylink_config *config, 25262306a36Sopenharmony_ci unsigned int mode, phy_interface_t interface) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct net_device *ndev = to_net_dev(config->dev); 25562306a36Sopenharmony_ci struct prestera_port *port = netdev_priv(ndev); 25662306a36Sopenharmony_ci struct prestera_port_mac_state state_mac; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Invalidate. Parameters will update on next link event. */ 25962306a36Sopenharmony_ci memset(&state_mac, 0, sizeof(state_mac)); 26062306a36Sopenharmony_ci state_mac.valid = false; 26162306a36Sopenharmony_ci prestera_port_mac_state_cache_write(port, &state_mac); 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic void prestera_mac_link_up(struct phylink_config *config, 26562306a36Sopenharmony_ci struct phy_device *phy, 26662306a36Sopenharmony_ci unsigned int mode, phy_interface_t interface, 26762306a36Sopenharmony_ci int speed, int duplex, 26862306a36Sopenharmony_ci bool tx_pause, bool rx_pause) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic struct phylink_pcs * 27362306a36Sopenharmony_ciprestera_mac_select_pcs(struct phylink_config *config, 27462306a36Sopenharmony_ci phy_interface_t interface) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci struct net_device *dev = to_net_dev(config->dev); 27762306a36Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci return &port->phylink_pcs; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic void prestera_pcs_get_state(struct phylink_pcs *pcs, 28362306a36Sopenharmony_ci struct phylink_link_state *state) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct prestera_port *port = container_of(pcs, struct prestera_port, 28662306a36Sopenharmony_ci phylink_pcs); 28762306a36Sopenharmony_ci struct prestera_port_mac_state smac; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci prestera_port_mac_state_cache_read(port, &smac); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (smac.valid) { 29262306a36Sopenharmony_ci state->link = smac.oper ? 1 : 0; 29362306a36Sopenharmony_ci /* AN is completed, when port is up */ 29462306a36Sopenharmony_ci state->an_complete = (smac.oper && port->autoneg) ? 1 : 0; 29562306a36Sopenharmony_ci state->speed = smac.speed; 29662306a36Sopenharmony_ci state->duplex = smac.duplex; 29762306a36Sopenharmony_ci } else { 29862306a36Sopenharmony_ci state->link = 0; 29962306a36Sopenharmony_ci state->an_complete = 0; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic int prestera_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, 30462306a36Sopenharmony_ci phy_interface_t interface, 30562306a36Sopenharmony_ci const unsigned long *advertising, 30662306a36Sopenharmony_ci bool permit_pause_to_mac) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci struct prestera_port *port = prestera_pcs_to_port(pcs); 30962306a36Sopenharmony_ci struct prestera_port_mac_config cfg_mac; 31062306a36Sopenharmony_ci int err; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci err = prestera_port_cfg_mac_read(port, &cfg_mac); 31362306a36Sopenharmony_ci if (err) 31462306a36Sopenharmony_ci return err; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci cfg_mac.admin = true; 31762306a36Sopenharmony_ci cfg_mac.fec = PRESTERA_PORT_FEC_OFF; 31862306a36Sopenharmony_ci cfg_mac.inband = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci switch (interface) { 32162306a36Sopenharmony_ci case PHY_INTERFACE_MODE_10GBASER: 32262306a36Sopenharmony_ci cfg_mac.speed = SPEED_10000; 32362306a36Sopenharmony_ci cfg_mac.mode = PRESTERA_MAC_MODE_SR_LR; 32462306a36Sopenharmony_ci break; 32562306a36Sopenharmony_ci case PHY_INTERFACE_MODE_2500BASEX: 32662306a36Sopenharmony_ci cfg_mac.speed = SPEED_2500; 32762306a36Sopenharmony_ci cfg_mac.duplex = DUPLEX_FULL; 32862306a36Sopenharmony_ci cfg_mac.mode = PRESTERA_MAC_MODE_SGMII; 32962306a36Sopenharmony_ci break; 33062306a36Sopenharmony_ci case PHY_INTERFACE_MODE_SGMII: 33162306a36Sopenharmony_ci cfg_mac.mode = PRESTERA_MAC_MODE_SGMII; 33262306a36Sopenharmony_ci break; 33362306a36Sopenharmony_ci case PHY_INTERFACE_MODE_1000BASEX: 33462306a36Sopenharmony_ci default: 33562306a36Sopenharmony_ci cfg_mac.speed = SPEED_1000; 33662306a36Sopenharmony_ci cfg_mac.duplex = DUPLEX_FULL; 33762306a36Sopenharmony_ci cfg_mac.mode = PRESTERA_MAC_MODE_1000BASE_X; 33862306a36Sopenharmony_ci break; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci err = prestera_port_cfg_mac_write(port, &cfg_mac); 34262306a36Sopenharmony_ci if (err) 34362306a36Sopenharmony_ci return err; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci return 0; 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic void prestera_pcs_an_restart(struct phylink_pcs *pcs) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci /* TODO: add 1000basex AN restart support 35162306a36Sopenharmony_ci * (Currently FW has no support for 1000baseX AN restart, but it will in the future, 35262306a36Sopenharmony_ci * so as for now the function would stay empty.) 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic const struct phylink_mac_ops prestera_mac_ops = { 35762306a36Sopenharmony_ci .mac_select_pcs = prestera_mac_select_pcs, 35862306a36Sopenharmony_ci .mac_config = prestera_mac_config, 35962306a36Sopenharmony_ci .mac_link_down = prestera_mac_link_down, 36062306a36Sopenharmony_ci .mac_link_up = prestera_mac_link_up, 36162306a36Sopenharmony_ci}; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic const struct phylink_pcs_ops prestera_pcs_ops = { 36462306a36Sopenharmony_ci .pcs_get_state = prestera_pcs_get_state, 36562306a36Sopenharmony_ci .pcs_config = prestera_pcs_config, 36662306a36Sopenharmony_ci .pcs_an_restart = prestera_pcs_an_restart, 36762306a36Sopenharmony_ci}; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic int prestera_port_sfp_bind(struct prestera_port *port) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct prestera_switch *sw = port->sw; 37262306a36Sopenharmony_ci struct device_node *ports, *node; 37362306a36Sopenharmony_ci struct fwnode_handle *fwnode; 37462306a36Sopenharmony_ci struct phylink *phy_link; 37562306a36Sopenharmony_ci int err; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (!sw->np) 37862306a36Sopenharmony_ci return 0; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci of_node_get(sw->np); 38162306a36Sopenharmony_ci ports = of_find_node_by_name(sw->np, "ports"); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci for_each_child_of_node(ports, node) { 38462306a36Sopenharmony_ci int num; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci err = of_property_read_u32(node, "prestera,port-num", &num); 38762306a36Sopenharmony_ci if (err) { 38862306a36Sopenharmony_ci dev_err(sw->dev->dev, 38962306a36Sopenharmony_ci "device node %pOF has no valid reg property: %d\n", 39062306a36Sopenharmony_ci node, err); 39162306a36Sopenharmony_ci goto out; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (port->fp_id != num) 39562306a36Sopenharmony_ci continue; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci port->phylink_pcs.ops = &prestera_pcs_ops; 39862306a36Sopenharmony_ci port->phylink_pcs.neg_mode = true; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci port->phy_config.dev = &port->dev->dev; 40162306a36Sopenharmony_ci port->phy_config.type = PHYLINK_NETDEV; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci fwnode = of_fwnode_handle(node); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_10GBASER, 40662306a36Sopenharmony_ci port->phy_config.supported_interfaces); 40762306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_2500BASEX, 40862306a36Sopenharmony_ci port->phy_config.supported_interfaces); 40962306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_SGMII, 41062306a36Sopenharmony_ci port->phy_config.supported_interfaces); 41162306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_1000BASEX, 41262306a36Sopenharmony_ci port->phy_config.supported_interfaces); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci port->phy_config.mac_capabilities = 41562306a36Sopenharmony_ci MAC_1000 | MAC_2500FD | MAC_10000FD; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci phy_link = phylink_create(&port->phy_config, fwnode, 41862306a36Sopenharmony_ci PHY_INTERFACE_MODE_INTERNAL, 41962306a36Sopenharmony_ci &prestera_mac_ops); 42062306a36Sopenharmony_ci if (IS_ERR(phy_link)) { 42162306a36Sopenharmony_ci netdev_err(port->dev, "failed to create phylink\n"); 42262306a36Sopenharmony_ci err = PTR_ERR(phy_link); 42362306a36Sopenharmony_ci goto out; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci port->phy_link = phy_link; 42762306a36Sopenharmony_ci break; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ciout: 43162306a36Sopenharmony_ci of_node_put(node); 43262306a36Sopenharmony_ci of_node_put(ports); 43362306a36Sopenharmony_ci return err; 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic int prestera_port_sfp_unbind(struct prestera_port *port) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci if (port->phy_link) 43962306a36Sopenharmony_ci phylink_destroy(port->phy_link); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic netdev_tx_t prestera_port_xmit(struct sk_buff *skb, 44562306a36Sopenharmony_ci struct net_device *dev) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci return prestera_rxtx_xmit(netdev_priv(dev), skb); 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ciint prestera_is_valid_mac_addr(struct prestera_port *port, const u8 *addr) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci if (!is_valid_ether_addr(addr)) 45362306a36Sopenharmony_ci return -EADDRNOTAVAIL; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* firmware requires that port's MAC address contains first 5 bytes 45662306a36Sopenharmony_ci * of the base MAC address 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_ci if (memcmp(port->sw->base_mac, addr, ETH_ALEN - 1)) 45962306a36Sopenharmony_ci return -EINVAL; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci return 0; 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic int prestera_port_set_mac_address(struct net_device *dev, void *p) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 46762306a36Sopenharmony_ci struct sockaddr *addr = p; 46862306a36Sopenharmony_ci int err; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci err = prestera_is_valid_mac_addr(port, addr->sa_data); 47162306a36Sopenharmony_ci if (err) 47262306a36Sopenharmony_ci return err; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci err = prestera_hw_port_mac_set(port, addr->sa_data); 47562306a36Sopenharmony_ci if (err) 47662306a36Sopenharmony_ci return err; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci eth_hw_addr_set(dev, addr->sa_data); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci return 0; 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic int prestera_port_change_mtu(struct net_device *dev, int mtu) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 48662306a36Sopenharmony_ci int err; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci err = prestera_hw_port_mtu_set(port, mtu); 48962306a36Sopenharmony_ci if (err) 49062306a36Sopenharmony_ci return err; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci dev->mtu = mtu; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci return 0; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic void prestera_port_get_stats64(struct net_device *dev, 49862306a36Sopenharmony_ci struct rtnl_link_stats64 *stats) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 50162306a36Sopenharmony_ci struct prestera_port_stats *port_stats = &port->cached_hw_stats.stats; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci stats->rx_packets = port_stats->broadcast_frames_received + 50462306a36Sopenharmony_ci port_stats->multicast_frames_received + 50562306a36Sopenharmony_ci port_stats->unicast_frames_received; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci stats->tx_packets = port_stats->broadcast_frames_sent + 50862306a36Sopenharmony_ci port_stats->multicast_frames_sent + 50962306a36Sopenharmony_ci port_stats->unicast_frames_sent; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci stats->rx_bytes = port_stats->good_octets_received; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci stats->tx_bytes = port_stats->good_octets_sent; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci stats->rx_errors = port_stats->rx_error_frame_received; 51662306a36Sopenharmony_ci stats->tx_errors = port_stats->mac_trans_error; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci stats->rx_dropped = port_stats->buffer_overrun; 51962306a36Sopenharmony_ci stats->tx_dropped = 0; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci stats->multicast = port_stats->multicast_frames_received; 52262306a36Sopenharmony_ci stats->collisions = port_stats->excessive_collision; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci stats->rx_crc_errors = port_stats->bad_crc; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic void prestera_port_get_hw_stats(struct prestera_port *port) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci prestera_hw_port_stats_get(port, &port->cached_hw_stats.stats); 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic void prestera_port_stats_update(struct work_struct *work) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci struct prestera_port *port = 53562306a36Sopenharmony_ci container_of(work, struct prestera_port, 53662306a36Sopenharmony_ci cached_hw_stats.caching_dw.work); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci prestera_port_get_hw_stats(port); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci queue_delayed_work(prestera_wq, &port->cached_hw_stats.caching_dw, 54162306a36Sopenharmony_ci msecs_to_jiffies(PRESTERA_STATS_DELAY_MS)); 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic int prestera_port_setup_tc(struct net_device *dev, 54562306a36Sopenharmony_ci enum tc_setup_type type, 54662306a36Sopenharmony_ci void *type_data) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci switch (type) { 55162306a36Sopenharmony_ci case TC_SETUP_BLOCK: 55262306a36Sopenharmony_ci return prestera_flow_block_setup(port, type_data); 55362306a36Sopenharmony_ci default: 55462306a36Sopenharmony_ci return -EOPNOTSUPP; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic const struct net_device_ops prestera_netdev_ops = { 55962306a36Sopenharmony_ci .ndo_open = prestera_port_open, 56062306a36Sopenharmony_ci .ndo_stop = prestera_port_close, 56162306a36Sopenharmony_ci .ndo_start_xmit = prestera_port_xmit, 56262306a36Sopenharmony_ci .ndo_setup_tc = prestera_port_setup_tc, 56362306a36Sopenharmony_ci .ndo_change_mtu = prestera_port_change_mtu, 56462306a36Sopenharmony_ci .ndo_get_stats64 = prestera_port_get_stats64, 56562306a36Sopenharmony_ci .ndo_set_mac_address = prestera_port_set_mac_address, 56662306a36Sopenharmony_ci}; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ciint prestera_port_autoneg_set(struct prestera_port *port, u64 link_modes) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci int err; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci if (port->autoneg && port->adver_link_modes == link_modes) 57362306a36Sopenharmony_ci return 0; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci err = prestera_hw_port_phy_mode_set(port, port->cfg_phy.admin, 57662306a36Sopenharmony_ci true, 0, link_modes, 57762306a36Sopenharmony_ci port->cfg_phy.mdix); 57862306a36Sopenharmony_ci if (err) 57962306a36Sopenharmony_ci return err; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci port->adver_fec = BIT(PRESTERA_PORT_FEC_OFF); 58262306a36Sopenharmony_ci port->adver_link_modes = link_modes; 58362306a36Sopenharmony_ci port->cfg_phy.mode = 0; 58462306a36Sopenharmony_ci port->autoneg = true; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci return 0; 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cistatic void prestera_port_list_add(struct prestera_port *port) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci write_lock(&port->sw->port_list_lock); 59262306a36Sopenharmony_ci list_add(&port->list, &port->sw->port_list); 59362306a36Sopenharmony_ci write_unlock(&port->sw->port_list_lock); 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic void prestera_port_list_del(struct prestera_port *port) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci write_lock(&port->sw->port_list_lock); 59962306a36Sopenharmony_ci list_del(&port->list); 60062306a36Sopenharmony_ci write_unlock(&port->sw->port_list_lock); 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic int prestera_port_create(struct prestera_switch *sw, u32 id) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci struct prestera_port_mac_config cfg_mac; 60662306a36Sopenharmony_ci struct prestera_port *port; 60762306a36Sopenharmony_ci struct net_device *dev; 60862306a36Sopenharmony_ci int err; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci dev = alloc_etherdev(sizeof(*port)); 61162306a36Sopenharmony_ci if (!dev) 61262306a36Sopenharmony_ci return -ENOMEM; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci port = netdev_priv(dev); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci INIT_LIST_HEAD(&port->vlans_list); 61762306a36Sopenharmony_ci port->pvid = PRESTERA_DEFAULT_VID; 61862306a36Sopenharmony_ci port->lag = NULL; 61962306a36Sopenharmony_ci port->dev = dev; 62062306a36Sopenharmony_ci port->id = id; 62162306a36Sopenharmony_ci port->sw = sw; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci spin_lock_init(&port->state_mac_lock); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci err = prestera_hw_port_info_get(port, &port->dev_id, &port->hw_id, 62662306a36Sopenharmony_ci &port->fp_id); 62762306a36Sopenharmony_ci if (err) { 62862306a36Sopenharmony_ci dev_err(prestera_dev(sw), "Failed to get port(%u) info\n", id); 62962306a36Sopenharmony_ci goto err_port_info_get; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci err = prestera_devlink_port_register(port); 63362306a36Sopenharmony_ci if (err) 63462306a36Sopenharmony_ci goto err_dl_port_register; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci dev->features |= NETIF_F_NETNS_LOCAL | NETIF_F_HW_TC; 63762306a36Sopenharmony_ci dev->netdev_ops = &prestera_netdev_ops; 63862306a36Sopenharmony_ci dev->ethtool_ops = &prestera_ethtool_ops; 63962306a36Sopenharmony_ci SET_NETDEV_DEV(dev, sw->dev->dev); 64062306a36Sopenharmony_ci SET_NETDEV_DEVLINK_PORT(dev, &port->dl_port); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci if (port->caps.transceiver != PRESTERA_PORT_TCVR_SFP) 64362306a36Sopenharmony_ci netif_carrier_off(dev); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci dev->mtu = min_t(unsigned int, sw->mtu_max, PRESTERA_MTU_DEFAULT); 64662306a36Sopenharmony_ci dev->min_mtu = sw->mtu_min; 64762306a36Sopenharmony_ci dev->max_mtu = sw->mtu_max; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci err = prestera_hw_port_mtu_set(port, dev->mtu); 65062306a36Sopenharmony_ci if (err) { 65162306a36Sopenharmony_ci dev_err(prestera_dev(sw), "Failed to set port(%u) mtu(%d)\n", 65262306a36Sopenharmony_ci id, dev->mtu); 65362306a36Sopenharmony_ci goto err_port_init; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (port->fp_id >= PRESTERA_MAC_ADDR_NUM_MAX) { 65762306a36Sopenharmony_ci err = -EINVAL; 65862306a36Sopenharmony_ci goto err_port_init; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci eth_hw_addr_gen(dev, sw->base_mac, port->fp_id); 66262306a36Sopenharmony_ci /* firmware requires that port's MAC address consist of the first 66362306a36Sopenharmony_ci * 5 bytes of the base MAC address 66462306a36Sopenharmony_ci */ 66562306a36Sopenharmony_ci if (memcmp(dev->dev_addr, sw->base_mac, ETH_ALEN - 1)) { 66662306a36Sopenharmony_ci dev_warn(prestera_dev(sw), "Port MAC address wraps for port(%u)\n", id); 66762306a36Sopenharmony_ci dev_addr_mod(dev, 0, sw->base_mac, ETH_ALEN - 1); 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci err = prestera_hw_port_mac_set(port, dev->dev_addr); 67162306a36Sopenharmony_ci if (err) { 67262306a36Sopenharmony_ci dev_err(prestera_dev(sw), "Failed to set port(%u) mac addr\n", id); 67362306a36Sopenharmony_ci goto err_port_init; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci err = prestera_hw_port_cap_get(port, &port->caps); 67762306a36Sopenharmony_ci if (err) { 67862306a36Sopenharmony_ci dev_err(prestera_dev(sw), "Failed to get port(%u) caps\n", id); 67962306a36Sopenharmony_ci goto err_port_init; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci port->adver_link_modes = port->caps.supp_link_modes; 68362306a36Sopenharmony_ci port->adver_fec = 0; 68462306a36Sopenharmony_ci port->autoneg = true; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* initialize config mac */ 68762306a36Sopenharmony_ci if (port->caps.transceiver != PRESTERA_PORT_TCVR_SFP) { 68862306a36Sopenharmony_ci cfg_mac.admin = true; 68962306a36Sopenharmony_ci cfg_mac.mode = PRESTERA_MAC_MODE_INTERNAL; 69062306a36Sopenharmony_ci } else { 69162306a36Sopenharmony_ci cfg_mac.admin = false; 69262306a36Sopenharmony_ci cfg_mac.mode = PRESTERA_MAC_MODE_MAX; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci cfg_mac.inband = 0; 69562306a36Sopenharmony_ci cfg_mac.speed = 0; 69662306a36Sopenharmony_ci cfg_mac.duplex = DUPLEX_UNKNOWN; 69762306a36Sopenharmony_ci cfg_mac.fec = PRESTERA_PORT_FEC_OFF; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci err = prestera_port_cfg_mac_write(port, &cfg_mac); 70062306a36Sopenharmony_ci if (err) { 70162306a36Sopenharmony_ci dev_err(prestera_dev(sw), 70262306a36Sopenharmony_ci "Failed to set port(%u) mac mode\n", id); 70362306a36Sopenharmony_ci goto err_port_init; 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci /* initialize config phy (if this is inegral) */ 70762306a36Sopenharmony_ci if (port->caps.transceiver != PRESTERA_PORT_TCVR_SFP) { 70862306a36Sopenharmony_ci port->cfg_phy.mdix = ETH_TP_MDI_AUTO; 70962306a36Sopenharmony_ci port->cfg_phy.admin = false; 71062306a36Sopenharmony_ci err = prestera_hw_port_phy_mode_set(port, 71162306a36Sopenharmony_ci port->cfg_phy.admin, 71262306a36Sopenharmony_ci false, 0, 0, 71362306a36Sopenharmony_ci port->cfg_phy.mdix); 71462306a36Sopenharmony_ci if (err) { 71562306a36Sopenharmony_ci dev_err(prestera_dev(sw), 71662306a36Sopenharmony_ci "Failed to set port(%u) phy mode\n", id); 71762306a36Sopenharmony_ci goto err_port_init; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci err = prestera_rxtx_port_init(port); 72262306a36Sopenharmony_ci if (err) 72362306a36Sopenharmony_ci goto err_port_init; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci INIT_DELAYED_WORK(&port->cached_hw_stats.caching_dw, 72662306a36Sopenharmony_ci &prestera_port_stats_update); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci prestera_port_list_add(port); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci err = register_netdev(dev); 73162306a36Sopenharmony_ci if (err) 73262306a36Sopenharmony_ci goto err_register_netdev; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci err = prestera_port_sfp_bind(port); 73562306a36Sopenharmony_ci if (err) 73662306a36Sopenharmony_ci goto err_sfp_bind; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci return 0; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cierr_sfp_bind: 74162306a36Sopenharmony_ci unregister_netdev(dev); 74262306a36Sopenharmony_cierr_register_netdev: 74362306a36Sopenharmony_ci prestera_port_list_del(port); 74462306a36Sopenharmony_cierr_port_init: 74562306a36Sopenharmony_ci prestera_devlink_port_unregister(port); 74662306a36Sopenharmony_cierr_dl_port_register: 74762306a36Sopenharmony_cierr_port_info_get: 74862306a36Sopenharmony_ci free_netdev(dev); 74962306a36Sopenharmony_ci return err; 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_cistatic void prestera_port_destroy(struct prestera_port *port) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci struct net_device *dev = port->dev; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci cancel_delayed_work_sync(&port->cached_hw_stats.caching_dw); 75762306a36Sopenharmony_ci unregister_netdev(dev); 75862306a36Sopenharmony_ci prestera_port_list_del(port); 75962306a36Sopenharmony_ci prestera_devlink_port_unregister(port); 76062306a36Sopenharmony_ci free_netdev(dev); 76162306a36Sopenharmony_ci} 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_cistatic void prestera_destroy_ports(struct prestera_switch *sw) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci struct prestera_port *port, *tmp; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci list_for_each_entry_safe(port, tmp, &sw->port_list, list) 76862306a36Sopenharmony_ci prestera_port_destroy(port); 76962306a36Sopenharmony_ci} 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_cistatic int prestera_create_ports(struct prestera_switch *sw) 77262306a36Sopenharmony_ci{ 77362306a36Sopenharmony_ci struct prestera_port *port, *tmp; 77462306a36Sopenharmony_ci u32 port_idx; 77562306a36Sopenharmony_ci int err; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci for (port_idx = 0; port_idx < sw->port_count; port_idx++) { 77862306a36Sopenharmony_ci err = prestera_port_create(sw, port_idx); 77962306a36Sopenharmony_ci if (err) 78062306a36Sopenharmony_ci goto err_port_create; 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci return 0; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_cierr_port_create: 78662306a36Sopenharmony_ci list_for_each_entry_safe(port, tmp, &sw->port_list, list) { 78762306a36Sopenharmony_ci prestera_port_sfp_unbind(port); 78862306a36Sopenharmony_ci prestera_port_destroy(port); 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci return err; 79262306a36Sopenharmony_ci} 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_cistatic void prestera_port_handle_event(struct prestera_switch *sw, 79562306a36Sopenharmony_ci struct prestera_event *evt, void *arg) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci struct prestera_port_mac_state smac; 79862306a36Sopenharmony_ci struct prestera_port_event *pevt; 79962306a36Sopenharmony_ci struct delayed_work *caching_dw; 80062306a36Sopenharmony_ci struct prestera_port *port; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci if (evt->id == PRESTERA_PORT_EVENT_MAC_STATE_CHANGED) { 80362306a36Sopenharmony_ci pevt = &evt->port_evt; 80462306a36Sopenharmony_ci port = prestera_find_port(sw, pevt->port_id); 80562306a36Sopenharmony_ci if (!port || !port->dev) 80662306a36Sopenharmony_ci return; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci caching_dw = &port->cached_hw_stats.caching_dw; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci memset(&smac, 0, sizeof(smac)); 81162306a36Sopenharmony_ci smac.valid = true; 81262306a36Sopenharmony_ci smac.oper = pevt->data.mac.oper; 81362306a36Sopenharmony_ci if (smac.oper) { 81462306a36Sopenharmony_ci smac.mode = pevt->data.mac.mode; 81562306a36Sopenharmony_ci smac.speed = pevt->data.mac.speed; 81662306a36Sopenharmony_ci smac.duplex = pevt->data.mac.duplex; 81762306a36Sopenharmony_ci smac.fc = pevt->data.mac.fc; 81862306a36Sopenharmony_ci smac.fec = pevt->data.mac.fec; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci prestera_port_mac_state_cache_write(port, &smac); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci if (port->state_mac.oper) { 82362306a36Sopenharmony_ci if (port->phy_link) 82462306a36Sopenharmony_ci phylink_mac_change(port->phy_link, true); 82562306a36Sopenharmony_ci else 82662306a36Sopenharmony_ci netif_carrier_on(port->dev); 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci if (!delayed_work_pending(caching_dw)) 82962306a36Sopenharmony_ci queue_delayed_work(prestera_wq, caching_dw, 0); 83062306a36Sopenharmony_ci } else { 83162306a36Sopenharmony_ci if (port->phy_link) 83262306a36Sopenharmony_ci phylink_mac_change(port->phy_link, false); 83362306a36Sopenharmony_ci else if (netif_running(port->dev) && netif_carrier_ok(port->dev)) 83462306a36Sopenharmony_ci netif_carrier_off(port->dev); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci if (delayed_work_pending(caching_dw)) 83762306a36Sopenharmony_ci cancel_delayed_work(caching_dw); 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_cistatic int prestera_event_handlers_register(struct prestera_switch *sw) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci return prestera_hw_event_handler_register(sw, PRESTERA_EVENT_TYPE_PORT, 84562306a36Sopenharmony_ci prestera_port_handle_event, 84662306a36Sopenharmony_ci NULL); 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_cistatic void prestera_event_handlers_unregister(struct prestera_switch *sw) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci prestera_hw_event_handler_unregister(sw, PRESTERA_EVENT_TYPE_PORT, 85262306a36Sopenharmony_ci prestera_port_handle_event); 85362306a36Sopenharmony_ci} 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_cistatic int prestera_switch_set_base_mac_addr(struct prestera_switch *sw) 85662306a36Sopenharmony_ci{ 85762306a36Sopenharmony_ci int ret; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci if (sw->np) 86062306a36Sopenharmony_ci ret = of_get_mac_address(sw->np, sw->base_mac); 86162306a36Sopenharmony_ci if (!is_valid_ether_addr(sw->base_mac) || ret) { 86262306a36Sopenharmony_ci eth_random_addr(sw->base_mac); 86362306a36Sopenharmony_ci dev_info(prestera_dev(sw), "using random base mac address\n"); 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci return prestera_hw_switch_mac_set(sw, sw->base_mac); 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_cistruct prestera_lag *prestera_lag_by_id(struct prestera_switch *sw, u16 id) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci return id < sw->lag_max ? &sw->lags[id] : NULL; 87262306a36Sopenharmony_ci} 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_cistatic struct prestera_lag *prestera_lag_by_dev(struct prestera_switch *sw, 87562306a36Sopenharmony_ci struct net_device *dev) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci struct prestera_lag *lag; 87862306a36Sopenharmony_ci u16 id; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci for (id = 0; id < sw->lag_max; id++) { 88162306a36Sopenharmony_ci lag = &sw->lags[id]; 88262306a36Sopenharmony_ci if (lag->dev == dev) 88362306a36Sopenharmony_ci return lag; 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci return NULL; 88762306a36Sopenharmony_ci} 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ciint prestera_lag_id(struct prestera_switch *sw, 89062306a36Sopenharmony_ci struct net_device *lag_dev, u16 *lag_id) 89162306a36Sopenharmony_ci{ 89262306a36Sopenharmony_ci struct prestera_lag *lag; 89362306a36Sopenharmony_ci int free_id = -1; 89462306a36Sopenharmony_ci int id; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci for (id = 0; id < sw->lag_max; id++) { 89762306a36Sopenharmony_ci lag = prestera_lag_by_id(sw, id); 89862306a36Sopenharmony_ci if (lag->member_count) { 89962306a36Sopenharmony_ci if (lag->dev == lag_dev) { 90062306a36Sopenharmony_ci *lag_id = id; 90162306a36Sopenharmony_ci return 0; 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci } else if (free_id < 0) { 90462306a36Sopenharmony_ci free_id = id; 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci if (free_id < 0) 90862306a36Sopenharmony_ci return -ENOSPC; 90962306a36Sopenharmony_ci *lag_id = free_id; 91062306a36Sopenharmony_ci return 0; 91162306a36Sopenharmony_ci} 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_cistatic struct prestera_lag *prestera_lag_create(struct prestera_switch *sw, 91462306a36Sopenharmony_ci struct net_device *lag_dev) 91562306a36Sopenharmony_ci{ 91662306a36Sopenharmony_ci struct prestera_lag *lag = NULL; 91762306a36Sopenharmony_ci u16 id; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci for (id = 0; id < sw->lag_max; id++) { 92062306a36Sopenharmony_ci lag = &sw->lags[id]; 92162306a36Sopenharmony_ci if (!lag->dev) 92262306a36Sopenharmony_ci break; 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci if (lag) { 92562306a36Sopenharmony_ci INIT_LIST_HEAD(&lag->members); 92662306a36Sopenharmony_ci lag->dev = lag_dev; 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci return lag; 93062306a36Sopenharmony_ci} 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_cistatic void prestera_lag_destroy(struct prestera_switch *sw, 93362306a36Sopenharmony_ci struct prestera_lag *lag) 93462306a36Sopenharmony_ci{ 93562306a36Sopenharmony_ci WARN_ON(!list_empty(&lag->members)); 93662306a36Sopenharmony_ci lag->member_count = 0; 93762306a36Sopenharmony_ci lag->dev = NULL; 93862306a36Sopenharmony_ci} 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_cistatic int prestera_lag_port_add(struct prestera_port *port, 94162306a36Sopenharmony_ci struct net_device *lag_dev) 94262306a36Sopenharmony_ci{ 94362306a36Sopenharmony_ci struct prestera_switch *sw = port->sw; 94462306a36Sopenharmony_ci struct prestera_lag *lag; 94562306a36Sopenharmony_ci int err; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci lag = prestera_lag_by_dev(sw, lag_dev); 94862306a36Sopenharmony_ci if (!lag) { 94962306a36Sopenharmony_ci lag = prestera_lag_create(sw, lag_dev); 95062306a36Sopenharmony_ci if (!lag) 95162306a36Sopenharmony_ci return -ENOSPC; 95262306a36Sopenharmony_ci } 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci if (lag->member_count >= sw->lag_member_max) 95562306a36Sopenharmony_ci return -ENOSPC; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci err = prestera_hw_lag_member_add(port, lag->lag_id); 95862306a36Sopenharmony_ci if (err) { 95962306a36Sopenharmony_ci if (!lag->member_count) 96062306a36Sopenharmony_ci prestera_lag_destroy(sw, lag); 96162306a36Sopenharmony_ci return err; 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci list_add(&port->lag_member, &lag->members); 96562306a36Sopenharmony_ci lag->member_count++; 96662306a36Sopenharmony_ci port->lag = lag; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci return 0; 96962306a36Sopenharmony_ci} 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_cistatic int prestera_lag_port_del(struct prestera_port *port) 97262306a36Sopenharmony_ci{ 97362306a36Sopenharmony_ci struct prestera_switch *sw = port->sw; 97462306a36Sopenharmony_ci struct prestera_lag *lag = port->lag; 97562306a36Sopenharmony_ci int err; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci if (!lag || !lag->member_count) 97862306a36Sopenharmony_ci return -EINVAL; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci err = prestera_hw_lag_member_del(port, lag->lag_id); 98162306a36Sopenharmony_ci if (err) 98262306a36Sopenharmony_ci return err; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci list_del(&port->lag_member); 98562306a36Sopenharmony_ci lag->member_count--; 98662306a36Sopenharmony_ci port->lag = NULL; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci if (netif_is_bridge_port(lag->dev)) { 98962306a36Sopenharmony_ci struct net_device *br_dev; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci br_dev = netdev_master_upper_dev_get(lag->dev); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci prestera_bridge_port_leave(br_dev, port); 99462306a36Sopenharmony_ci } 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci if (!lag->member_count) 99762306a36Sopenharmony_ci prestera_lag_destroy(sw, lag); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci return 0; 100062306a36Sopenharmony_ci} 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_cibool prestera_port_is_lag_member(const struct prestera_port *port) 100362306a36Sopenharmony_ci{ 100462306a36Sopenharmony_ci return !!port->lag; 100562306a36Sopenharmony_ci} 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ciu16 prestera_port_lag_id(const struct prestera_port *port) 100862306a36Sopenharmony_ci{ 100962306a36Sopenharmony_ci return port->lag->lag_id; 101062306a36Sopenharmony_ci} 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_cistatic int prestera_lag_init(struct prestera_switch *sw) 101362306a36Sopenharmony_ci{ 101462306a36Sopenharmony_ci u16 id; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci sw->lags = kcalloc(sw->lag_max, sizeof(*sw->lags), GFP_KERNEL); 101762306a36Sopenharmony_ci if (!sw->lags) 101862306a36Sopenharmony_ci return -ENOMEM; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci for (id = 0; id < sw->lag_max; id++) 102162306a36Sopenharmony_ci sw->lags[id].lag_id = id; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci return 0; 102462306a36Sopenharmony_ci} 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_cistatic void prestera_lag_fini(struct prestera_switch *sw) 102762306a36Sopenharmony_ci{ 102862306a36Sopenharmony_ci u8 idx; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci for (idx = 0; idx < sw->lag_max; idx++) 103162306a36Sopenharmony_ci WARN_ON(sw->lags[idx].member_count); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci kfree(sw->lags); 103462306a36Sopenharmony_ci} 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_cibool prestera_netdev_check(const struct net_device *dev) 103762306a36Sopenharmony_ci{ 103862306a36Sopenharmony_ci return dev->netdev_ops == &prestera_netdev_ops; 103962306a36Sopenharmony_ci} 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_cistatic int prestera_lower_dev_walk(struct net_device *dev, 104262306a36Sopenharmony_ci struct netdev_nested_priv *priv) 104362306a36Sopenharmony_ci{ 104462306a36Sopenharmony_ci struct prestera_port **pport = (struct prestera_port **)priv->data; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci if (prestera_netdev_check(dev)) { 104762306a36Sopenharmony_ci *pport = netdev_priv(dev); 104862306a36Sopenharmony_ci return 1; 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci return 0; 105262306a36Sopenharmony_ci} 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_cistruct prestera_port *prestera_port_dev_lower_find(struct net_device *dev) 105562306a36Sopenharmony_ci{ 105662306a36Sopenharmony_ci struct prestera_port *port = NULL; 105762306a36Sopenharmony_ci struct netdev_nested_priv priv = { 105862306a36Sopenharmony_ci .data = (void *)&port, 105962306a36Sopenharmony_ci }; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci if (prestera_netdev_check(dev)) 106262306a36Sopenharmony_ci return netdev_priv(dev); 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci netdev_walk_all_lower_dev(dev, prestera_lower_dev_walk, &priv); 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci return port; 106762306a36Sopenharmony_ci} 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_cistatic int prestera_netdev_port_lower_event(struct net_device *dev, 107062306a36Sopenharmony_ci unsigned long event, void *ptr) 107162306a36Sopenharmony_ci{ 107262306a36Sopenharmony_ci struct netdev_notifier_changelowerstate_info *info = ptr; 107362306a36Sopenharmony_ci struct netdev_lag_lower_state_info *lower_state_info; 107462306a36Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 107562306a36Sopenharmony_ci bool enabled; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci if (!netif_is_lag_port(dev)) 107862306a36Sopenharmony_ci return 0; 107962306a36Sopenharmony_ci if (!prestera_port_is_lag_member(port)) 108062306a36Sopenharmony_ci return 0; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci lower_state_info = info->lower_state_info; 108362306a36Sopenharmony_ci enabled = lower_state_info->link_up && lower_state_info->tx_enabled; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci return prestera_hw_lag_member_enable(port, port->lag->lag_id, enabled); 108662306a36Sopenharmony_ci} 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_cistatic bool prestera_lag_master_check(struct net_device *lag_dev, 108962306a36Sopenharmony_ci struct netdev_lag_upper_info *info, 109062306a36Sopenharmony_ci struct netlink_ext_ack *ext_ack) 109162306a36Sopenharmony_ci{ 109262306a36Sopenharmony_ci if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) { 109362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(ext_ack, "Unsupported LAG Tx type"); 109462306a36Sopenharmony_ci return false; 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci return true; 109862306a36Sopenharmony_ci} 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_cistatic int prestera_netdev_port_event(struct net_device *lower, 110162306a36Sopenharmony_ci struct net_device *dev, 110262306a36Sopenharmony_ci unsigned long event, void *ptr) 110362306a36Sopenharmony_ci{ 110462306a36Sopenharmony_ci struct netdev_notifier_info *info = ptr; 110562306a36Sopenharmony_ci struct netdev_notifier_changeupper_info *cu_info; 110662306a36Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 110762306a36Sopenharmony_ci struct netlink_ext_ack *extack; 110862306a36Sopenharmony_ci struct net_device *upper; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci extack = netdev_notifier_info_to_extack(info); 111162306a36Sopenharmony_ci cu_info = container_of(info, 111262306a36Sopenharmony_ci struct netdev_notifier_changeupper_info, 111362306a36Sopenharmony_ci info); 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci switch (event) { 111662306a36Sopenharmony_ci case NETDEV_PRECHANGEUPPER: 111762306a36Sopenharmony_ci upper = cu_info->upper_dev; 111862306a36Sopenharmony_ci if (!netif_is_bridge_master(upper) && 111962306a36Sopenharmony_ci !netif_is_lag_master(upper)) { 112062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type"); 112162306a36Sopenharmony_ci return -EINVAL; 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci if (!cu_info->linking) 112562306a36Sopenharmony_ci break; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci if (netdev_has_any_upper_dev(upper)) { 112862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Upper device is already enslaved"); 112962306a36Sopenharmony_ci return -EINVAL; 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci if (netif_is_lag_master(upper) && 113362306a36Sopenharmony_ci !prestera_lag_master_check(upper, cu_info->upper_info, extack)) 113462306a36Sopenharmony_ci return -EOPNOTSUPP; 113562306a36Sopenharmony_ci if (netif_is_lag_master(upper) && vlan_uses_dev(dev)) { 113662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 113762306a36Sopenharmony_ci "Master device is a LAG master and port has a VLAN"); 113862306a36Sopenharmony_ci return -EINVAL; 113962306a36Sopenharmony_ci } 114062306a36Sopenharmony_ci if (netif_is_lag_port(dev) && is_vlan_dev(upper) && 114162306a36Sopenharmony_ci !netif_is_lag_master(vlan_dev_real_dev(upper))) { 114262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 114362306a36Sopenharmony_ci "Can not put a VLAN on a LAG port"); 114462306a36Sopenharmony_ci return -EINVAL; 114562306a36Sopenharmony_ci } 114662306a36Sopenharmony_ci break; 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci case NETDEV_CHANGEUPPER: 114962306a36Sopenharmony_ci upper = cu_info->upper_dev; 115062306a36Sopenharmony_ci if (netif_is_bridge_master(upper)) { 115162306a36Sopenharmony_ci if (cu_info->linking) 115262306a36Sopenharmony_ci return prestera_bridge_port_join(upper, port, 115362306a36Sopenharmony_ci extack); 115462306a36Sopenharmony_ci else 115562306a36Sopenharmony_ci prestera_bridge_port_leave(upper, port); 115662306a36Sopenharmony_ci } else if (netif_is_lag_master(upper)) { 115762306a36Sopenharmony_ci if (cu_info->linking) 115862306a36Sopenharmony_ci return prestera_lag_port_add(port, upper); 115962306a36Sopenharmony_ci else 116062306a36Sopenharmony_ci prestera_lag_port_del(port); 116162306a36Sopenharmony_ci } 116262306a36Sopenharmony_ci break; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci case NETDEV_CHANGELOWERSTATE: 116562306a36Sopenharmony_ci return prestera_netdev_port_lower_event(dev, event, ptr); 116662306a36Sopenharmony_ci } 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci return 0; 116962306a36Sopenharmony_ci} 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_cistatic int prestera_netdevice_lag_event(struct net_device *lag_dev, 117262306a36Sopenharmony_ci unsigned long event, void *ptr) 117362306a36Sopenharmony_ci{ 117462306a36Sopenharmony_ci struct net_device *dev; 117562306a36Sopenharmony_ci struct list_head *iter; 117662306a36Sopenharmony_ci int err; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci netdev_for_each_lower_dev(lag_dev, dev, iter) { 117962306a36Sopenharmony_ci if (prestera_netdev_check(dev)) { 118062306a36Sopenharmony_ci err = prestera_netdev_port_event(lag_dev, dev, event, 118162306a36Sopenharmony_ci ptr); 118262306a36Sopenharmony_ci if (err) 118362306a36Sopenharmony_ci return err; 118462306a36Sopenharmony_ci } 118562306a36Sopenharmony_ci } 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci return 0; 118862306a36Sopenharmony_ci} 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_cistatic int prestera_netdev_event_handler(struct notifier_block *nb, 119162306a36Sopenharmony_ci unsigned long event, void *ptr) 119262306a36Sopenharmony_ci{ 119362306a36Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 119462306a36Sopenharmony_ci int err = 0; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci if (prestera_netdev_check(dev)) 119762306a36Sopenharmony_ci err = prestera_netdev_port_event(dev, dev, event, ptr); 119862306a36Sopenharmony_ci else if (netif_is_lag_master(dev)) 119962306a36Sopenharmony_ci err = prestera_netdevice_lag_event(dev, event, ptr); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci return notifier_from_errno(err); 120262306a36Sopenharmony_ci} 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_cistruct prestera_mdb_entry * 120562306a36Sopenharmony_ciprestera_mdb_entry_create(struct prestera_switch *sw, 120662306a36Sopenharmony_ci const unsigned char *addr, u16 vid) 120762306a36Sopenharmony_ci{ 120862306a36Sopenharmony_ci struct prestera_flood_domain *flood_domain; 120962306a36Sopenharmony_ci struct prestera_mdb_entry *mdb_entry; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci mdb_entry = kzalloc(sizeof(*mdb_entry), GFP_KERNEL); 121262306a36Sopenharmony_ci if (!mdb_entry) 121362306a36Sopenharmony_ci goto err_mdb_alloc; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci flood_domain = prestera_flood_domain_create(sw); 121662306a36Sopenharmony_ci if (!flood_domain) 121762306a36Sopenharmony_ci goto err_flood_domain_create; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci mdb_entry->sw = sw; 122062306a36Sopenharmony_ci mdb_entry->vid = vid; 122162306a36Sopenharmony_ci mdb_entry->flood_domain = flood_domain; 122262306a36Sopenharmony_ci ether_addr_copy(mdb_entry->addr, addr); 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci if (prestera_hw_mdb_create(mdb_entry)) 122562306a36Sopenharmony_ci goto err_mdb_hw_create; 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci return mdb_entry; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_cierr_mdb_hw_create: 123062306a36Sopenharmony_ci prestera_flood_domain_destroy(flood_domain); 123162306a36Sopenharmony_cierr_flood_domain_create: 123262306a36Sopenharmony_ci kfree(mdb_entry); 123362306a36Sopenharmony_cierr_mdb_alloc: 123462306a36Sopenharmony_ci return NULL; 123562306a36Sopenharmony_ci} 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_civoid prestera_mdb_entry_destroy(struct prestera_mdb_entry *mdb_entry) 123862306a36Sopenharmony_ci{ 123962306a36Sopenharmony_ci prestera_hw_mdb_destroy(mdb_entry); 124062306a36Sopenharmony_ci prestera_flood_domain_destroy(mdb_entry->flood_domain); 124162306a36Sopenharmony_ci kfree(mdb_entry); 124262306a36Sopenharmony_ci} 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_cistruct prestera_flood_domain * 124562306a36Sopenharmony_ciprestera_flood_domain_create(struct prestera_switch *sw) 124662306a36Sopenharmony_ci{ 124762306a36Sopenharmony_ci struct prestera_flood_domain *domain; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci domain = kzalloc(sizeof(*domain), GFP_KERNEL); 125062306a36Sopenharmony_ci if (!domain) 125162306a36Sopenharmony_ci return NULL; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci domain->sw = sw; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci if (prestera_hw_flood_domain_create(domain)) { 125662306a36Sopenharmony_ci kfree(domain); 125762306a36Sopenharmony_ci return NULL; 125862306a36Sopenharmony_ci } 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci INIT_LIST_HEAD(&domain->flood_domain_port_list); 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci return domain; 126362306a36Sopenharmony_ci} 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_civoid prestera_flood_domain_destroy(struct prestera_flood_domain *flood_domain) 126662306a36Sopenharmony_ci{ 126762306a36Sopenharmony_ci WARN_ON(!list_empty(&flood_domain->flood_domain_port_list)); 126862306a36Sopenharmony_ci WARN_ON_ONCE(prestera_hw_flood_domain_destroy(flood_domain)); 126962306a36Sopenharmony_ci kfree(flood_domain); 127062306a36Sopenharmony_ci} 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ciint 127362306a36Sopenharmony_ciprestera_flood_domain_port_create(struct prestera_flood_domain *flood_domain, 127462306a36Sopenharmony_ci struct net_device *dev, 127562306a36Sopenharmony_ci u16 vid) 127662306a36Sopenharmony_ci{ 127762306a36Sopenharmony_ci struct prestera_flood_domain_port *flood_domain_port; 127862306a36Sopenharmony_ci bool is_first_port_in_list = false; 127962306a36Sopenharmony_ci int err; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci flood_domain_port = kzalloc(sizeof(*flood_domain_port), GFP_KERNEL); 128262306a36Sopenharmony_ci if (!flood_domain_port) { 128362306a36Sopenharmony_ci err = -ENOMEM; 128462306a36Sopenharmony_ci goto err_port_alloc; 128562306a36Sopenharmony_ci } 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci flood_domain_port->vid = vid; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci if (list_empty(&flood_domain->flood_domain_port_list)) 129062306a36Sopenharmony_ci is_first_port_in_list = true; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci list_add(&flood_domain_port->flood_domain_port_node, 129362306a36Sopenharmony_ci &flood_domain->flood_domain_port_list); 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci flood_domain_port->flood_domain = flood_domain; 129662306a36Sopenharmony_ci flood_domain_port->dev = dev; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci if (!is_first_port_in_list) { 129962306a36Sopenharmony_ci err = prestera_hw_flood_domain_ports_reset(flood_domain); 130062306a36Sopenharmony_ci if (err) 130162306a36Sopenharmony_ci goto err_prestera_mdb_port_create_hw; 130262306a36Sopenharmony_ci } 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci err = prestera_hw_flood_domain_ports_set(flood_domain); 130562306a36Sopenharmony_ci if (err) 130662306a36Sopenharmony_ci goto err_prestera_mdb_port_create_hw; 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci return 0; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_cierr_prestera_mdb_port_create_hw: 131162306a36Sopenharmony_ci list_del(&flood_domain_port->flood_domain_port_node); 131262306a36Sopenharmony_ci kfree(flood_domain_port); 131362306a36Sopenharmony_cierr_port_alloc: 131462306a36Sopenharmony_ci return err; 131562306a36Sopenharmony_ci} 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_civoid 131862306a36Sopenharmony_ciprestera_flood_domain_port_destroy(struct prestera_flood_domain_port *port) 131962306a36Sopenharmony_ci{ 132062306a36Sopenharmony_ci struct prestera_flood_domain *flood_domain = port->flood_domain; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci list_del(&port->flood_domain_port_node); 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci WARN_ON_ONCE(prestera_hw_flood_domain_ports_reset(flood_domain)); 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci if (!list_empty(&flood_domain->flood_domain_port_list)) 132762306a36Sopenharmony_ci WARN_ON_ONCE(prestera_hw_flood_domain_ports_set(flood_domain)); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci kfree(port); 133062306a36Sopenharmony_ci} 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_cistruct prestera_flood_domain_port * 133362306a36Sopenharmony_ciprestera_flood_domain_port_find(struct prestera_flood_domain *flood_domain, 133462306a36Sopenharmony_ci struct net_device *dev, u16 vid) 133562306a36Sopenharmony_ci{ 133662306a36Sopenharmony_ci struct prestera_flood_domain_port *flood_domain_port; 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci list_for_each_entry(flood_domain_port, 133962306a36Sopenharmony_ci &flood_domain->flood_domain_port_list, 134062306a36Sopenharmony_ci flood_domain_port_node) 134162306a36Sopenharmony_ci if (flood_domain_port->dev == dev && 134262306a36Sopenharmony_ci vid == flood_domain_port->vid) 134362306a36Sopenharmony_ci return flood_domain_port; 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci return NULL; 134662306a36Sopenharmony_ci} 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_cistatic int prestera_netdev_event_handler_register(struct prestera_switch *sw) 134962306a36Sopenharmony_ci{ 135062306a36Sopenharmony_ci sw->netdev_nb.notifier_call = prestera_netdev_event_handler; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci return register_netdevice_notifier(&sw->netdev_nb); 135362306a36Sopenharmony_ci} 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_cistatic void prestera_netdev_event_handler_unregister(struct prestera_switch *sw) 135662306a36Sopenharmony_ci{ 135762306a36Sopenharmony_ci unregister_netdevice_notifier(&sw->netdev_nb); 135862306a36Sopenharmony_ci} 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_cistatic int prestera_switch_init(struct prestera_switch *sw) 136162306a36Sopenharmony_ci{ 136262306a36Sopenharmony_ci int err; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci sw->np = sw->dev->dev->of_node; 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci err = prestera_hw_switch_init(sw); 136762306a36Sopenharmony_ci if (err) { 136862306a36Sopenharmony_ci dev_err(prestera_dev(sw), "Failed to init Switch device\n"); 136962306a36Sopenharmony_ci return err; 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci rwlock_init(&sw->port_list_lock); 137362306a36Sopenharmony_ci INIT_LIST_HEAD(&sw->port_list); 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci err = prestera_switch_set_base_mac_addr(sw); 137662306a36Sopenharmony_ci if (err) 137762306a36Sopenharmony_ci return err; 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci err = prestera_netdev_event_handler_register(sw); 138062306a36Sopenharmony_ci if (err) 138162306a36Sopenharmony_ci return err; 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci err = prestera_router_init(sw); 138462306a36Sopenharmony_ci if (err) 138562306a36Sopenharmony_ci goto err_router_init; 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci err = prestera_switchdev_init(sw); 138862306a36Sopenharmony_ci if (err) 138962306a36Sopenharmony_ci goto err_swdev_register; 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci err = prestera_rxtx_switch_init(sw); 139262306a36Sopenharmony_ci if (err) 139362306a36Sopenharmony_ci goto err_rxtx_register; 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci err = prestera_event_handlers_register(sw); 139662306a36Sopenharmony_ci if (err) 139762306a36Sopenharmony_ci goto err_handlers_register; 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci err = prestera_counter_init(sw); 140062306a36Sopenharmony_ci if (err) 140162306a36Sopenharmony_ci goto err_counter_init; 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci err = prestera_acl_init(sw); 140462306a36Sopenharmony_ci if (err) 140562306a36Sopenharmony_ci goto err_acl_init; 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci err = prestera_span_init(sw); 140862306a36Sopenharmony_ci if (err) 140962306a36Sopenharmony_ci goto err_span_init; 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci err = prestera_devlink_traps_register(sw); 141262306a36Sopenharmony_ci if (err) 141362306a36Sopenharmony_ci goto err_dl_register; 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci err = prestera_lag_init(sw); 141662306a36Sopenharmony_ci if (err) 141762306a36Sopenharmony_ci goto err_lag_init; 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci err = prestera_create_ports(sw); 142062306a36Sopenharmony_ci if (err) 142162306a36Sopenharmony_ci goto err_ports_create; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci prestera_devlink_register(sw); 142462306a36Sopenharmony_ci return 0; 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_cierr_ports_create: 142762306a36Sopenharmony_ci prestera_lag_fini(sw); 142862306a36Sopenharmony_cierr_lag_init: 142962306a36Sopenharmony_ci prestera_devlink_traps_unregister(sw); 143062306a36Sopenharmony_cierr_dl_register: 143162306a36Sopenharmony_ci prestera_span_fini(sw); 143262306a36Sopenharmony_cierr_span_init: 143362306a36Sopenharmony_ci prestera_acl_fini(sw); 143462306a36Sopenharmony_cierr_acl_init: 143562306a36Sopenharmony_ci prestera_counter_fini(sw); 143662306a36Sopenharmony_cierr_counter_init: 143762306a36Sopenharmony_ci prestera_event_handlers_unregister(sw); 143862306a36Sopenharmony_cierr_handlers_register: 143962306a36Sopenharmony_ci prestera_rxtx_switch_fini(sw); 144062306a36Sopenharmony_cierr_rxtx_register: 144162306a36Sopenharmony_ci prestera_switchdev_fini(sw); 144262306a36Sopenharmony_cierr_swdev_register: 144362306a36Sopenharmony_ci prestera_router_fini(sw); 144462306a36Sopenharmony_cierr_router_init: 144562306a36Sopenharmony_ci prestera_netdev_event_handler_unregister(sw); 144662306a36Sopenharmony_ci prestera_hw_switch_fini(sw); 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci return err; 144962306a36Sopenharmony_ci} 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_cistatic void prestera_switch_fini(struct prestera_switch *sw) 145262306a36Sopenharmony_ci{ 145362306a36Sopenharmony_ci prestera_devlink_unregister(sw); 145462306a36Sopenharmony_ci prestera_destroy_ports(sw); 145562306a36Sopenharmony_ci prestera_lag_fini(sw); 145662306a36Sopenharmony_ci prestera_devlink_traps_unregister(sw); 145762306a36Sopenharmony_ci prestera_span_fini(sw); 145862306a36Sopenharmony_ci prestera_acl_fini(sw); 145962306a36Sopenharmony_ci prestera_counter_fini(sw); 146062306a36Sopenharmony_ci prestera_event_handlers_unregister(sw); 146162306a36Sopenharmony_ci prestera_rxtx_switch_fini(sw); 146262306a36Sopenharmony_ci prestera_switchdev_fini(sw); 146362306a36Sopenharmony_ci prestera_router_fini(sw); 146462306a36Sopenharmony_ci prestera_netdev_event_handler_unregister(sw); 146562306a36Sopenharmony_ci prestera_hw_switch_fini(sw); 146662306a36Sopenharmony_ci of_node_put(sw->np); 146762306a36Sopenharmony_ci} 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ciint prestera_device_register(struct prestera_device *dev) 147062306a36Sopenharmony_ci{ 147162306a36Sopenharmony_ci struct prestera_switch *sw; 147262306a36Sopenharmony_ci int err; 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci sw = prestera_devlink_alloc(dev); 147562306a36Sopenharmony_ci if (!sw) 147662306a36Sopenharmony_ci return -ENOMEM; 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci dev->priv = sw; 147962306a36Sopenharmony_ci sw->dev = dev; 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci err = prestera_switch_init(sw); 148262306a36Sopenharmony_ci if (err) { 148362306a36Sopenharmony_ci prestera_devlink_free(sw); 148462306a36Sopenharmony_ci return err; 148562306a36Sopenharmony_ci } 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci return 0; 148862306a36Sopenharmony_ci} 148962306a36Sopenharmony_ciEXPORT_SYMBOL(prestera_device_register); 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_civoid prestera_device_unregister(struct prestera_device *dev) 149262306a36Sopenharmony_ci{ 149362306a36Sopenharmony_ci struct prestera_switch *sw = dev->priv; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci prestera_switch_fini(sw); 149662306a36Sopenharmony_ci prestera_devlink_free(sw); 149762306a36Sopenharmony_ci} 149862306a36Sopenharmony_ciEXPORT_SYMBOL(prestera_device_unregister); 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_cistatic int __init prestera_module_init(void) 150162306a36Sopenharmony_ci{ 150262306a36Sopenharmony_ci prestera_wq = alloc_workqueue("prestera", 0, 0); 150362306a36Sopenharmony_ci if (!prestera_wq) 150462306a36Sopenharmony_ci return -ENOMEM; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci prestera_owq = alloc_ordered_workqueue("prestera_ordered", 0); 150762306a36Sopenharmony_ci if (!prestera_owq) { 150862306a36Sopenharmony_ci destroy_workqueue(prestera_wq); 150962306a36Sopenharmony_ci return -ENOMEM; 151062306a36Sopenharmony_ci } 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci return 0; 151362306a36Sopenharmony_ci} 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_cistatic void __exit prestera_module_exit(void) 151662306a36Sopenharmony_ci{ 151762306a36Sopenharmony_ci destroy_workqueue(prestera_wq); 151862306a36Sopenharmony_ci destroy_workqueue(prestera_owq); 151962306a36Sopenharmony_ci} 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_cimodule_init(prestera_module_init); 152262306a36Sopenharmony_cimodule_exit(prestera_module_exit); 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 152562306a36Sopenharmony_ciMODULE_DESCRIPTION("Marvell Prestera switch driver"); 1526