1501ef306SVadym Kochan // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 2501ef306SVadym Kochan /* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */ 3501ef306SVadym Kochan 4501ef306SVadym Kochan #include <linux/etherdevice.h> 5501ef306SVadym Kochan #include <linux/jiffies.h> 6501ef306SVadym Kochan #include <linux/list.h> 7501ef306SVadym Kochan #include <linux/module.h> 8501ef306SVadym Kochan #include <linux/netdev_features.h> 9501ef306SVadym Kochan #include <linux/of.h> 10501ef306SVadym Kochan #include <linux/of_net.h> 11255213caSSerhiy Boiko #include <linux/if_vlan.h> 12501ef306SVadym Kochan 13501ef306SVadym Kochan #include "prestera.h" 14501ef306SVadym Kochan #include "prestera_hw.h" 158b474a9fSSerhiy Boiko #include "prestera_acl.h" 168b474a9fSSerhiy Boiko #include "prestera_flow.h" 1713defa27SSerhiy Boiko #include "prestera_span.h" 18501ef306SVadym Kochan #include "prestera_rxtx.h" 1934dd1710SVadym Kochan #include "prestera_devlink.h" 20a97d3c69SVadym Kochan #include "prestera_ethtool.h" 216e36c7bcSVolodymyr Mytnyk #include "prestera_counter.h" 22e1189d9aSVadym Kochan #include "prestera_switchdev.h" 23501ef306SVadym Kochan 24501ef306SVadym Kochan #define PRESTERA_MTU_DEFAULT 1536 25501ef306SVadym Kochan 26501ef306SVadym Kochan #define PRESTERA_STATS_DELAY_MS 1000 27501ef306SVadym Kochan 28501ef306SVadym Kochan #define PRESTERA_MAC_ADDR_NUM_MAX 255 29501ef306SVadym Kochan 30501ef306SVadym Kochan static struct workqueue_struct *prestera_wq; 314394fbcbSYevhen Orlov static struct workqueue_struct *prestera_owq; 324394fbcbSYevhen Orlov 334394fbcbSYevhen Orlov void prestera_queue_work(struct work_struct *work) 344394fbcbSYevhen Orlov { 354394fbcbSYevhen Orlov queue_work(prestera_owq, work); 364394fbcbSYevhen Orlov } 37501ef306SVadym Kochan 38e1189d9aSVadym Kochan int prestera_port_pvid_set(struct prestera_port *port, u16 vid) 39e1189d9aSVadym Kochan { 40e1189d9aSVadym Kochan enum prestera_accept_frm_type frm_type; 41e1189d9aSVadym Kochan int err; 42e1189d9aSVadym Kochan 43e1189d9aSVadym Kochan frm_type = PRESTERA_ACCEPT_FRAME_TYPE_TAGGED; 44e1189d9aSVadym Kochan 45e1189d9aSVadym Kochan if (vid) { 46e1189d9aSVadym Kochan err = prestera_hw_vlan_port_vid_set(port, vid); 47e1189d9aSVadym Kochan if (err) 48e1189d9aSVadym Kochan return err; 49e1189d9aSVadym Kochan 50e1189d9aSVadym Kochan frm_type = PRESTERA_ACCEPT_FRAME_TYPE_ALL; 51e1189d9aSVadym Kochan } 52e1189d9aSVadym Kochan 53e1189d9aSVadym Kochan err = prestera_hw_port_accept_frm_type(port, frm_type); 54e1189d9aSVadym Kochan if (err && frm_type == PRESTERA_ACCEPT_FRAME_TYPE_ALL) 55e1189d9aSVadym Kochan prestera_hw_vlan_port_vid_set(port, port->pvid); 56e1189d9aSVadym Kochan 57e1189d9aSVadym Kochan port->pvid = vid; 58e1189d9aSVadym Kochan return 0; 59e1189d9aSVadym Kochan } 60e1189d9aSVadym Kochan 61501ef306SVadym Kochan struct prestera_port *prestera_port_find_by_hwid(struct prestera_switch *sw, 62501ef306SVadym Kochan u32 dev_id, u32 hw_id) 63501ef306SVadym Kochan { 648b681bd7SYevhen Orlov struct prestera_port *port = NULL, *tmp; 65501ef306SVadym Kochan 66501ef306SVadym Kochan read_lock(&sw->port_list_lock); 678b681bd7SYevhen Orlov list_for_each_entry(tmp, &sw->port_list, list) { 688b681bd7SYevhen Orlov if (tmp->dev_id == dev_id && tmp->hw_id == hw_id) { 698b681bd7SYevhen Orlov port = tmp; 70501ef306SVadym Kochan break; 71501ef306SVadym Kochan } 728b681bd7SYevhen Orlov } 73501ef306SVadym Kochan read_unlock(&sw->port_list_lock); 74501ef306SVadym Kochan 75501ef306SVadym Kochan return port; 76501ef306SVadym Kochan } 77501ef306SVadym Kochan 78e1189d9aSVadym Kochan struct prestera_port *prestera_find_port(struct prestera_switch *sw, u32 id) 79501ef306SVadym Kochan { 808b681bd7SYevhen Orlov struct prestera_port *port = NULL, *tmp; 81501ef306SVadym Kochan 82501ef306SVadym Kochan read_lock(&sw->port_list_lock); 838b681bd7SYevhen Orlov list_for_each_entry(tmp, &sw->port_list, list) { 848b681bd7SYevhen Orlov if (tmp->id == id) { 858b681bd7SYevhen Orlov port = tmp; 86501ef306SVadym Kochan break; 87501ef306SVadym Kochan } 888b681bd7SYevhen Orlov } 89501ef306SVadym Kochan read_unlock(&sw->port_list_lock); 90501ef306SVadym Kochan 91501ef306SVadym Kochan return port; 92501ef306SVadym Kochan } 93501ef306SVadym Kochan 94bb5dbf2cSVolodymyr Mytnyk int prestera_port_cfg_mac_read(struct prestera_port *port, 95bb5dbf2cSVolodymyr Mytnyk struct prestera_port_mac_config *cfg) 96501ef306SVadym Kochan { 97bb5dbf2cSVolodymyr Mytnyk *cfg = port->cfg_mac; 98bb5dbf2cSVolodymyr Mytnyk return 0; 99bb5dbf2cSVolodymyr Mytnyk } 100bb5dbf2cSVolodymyr Mytnyk 101bb5dbf2cSVolodymyr Mytnyk int prestera_port_cfg_mac_write(struct prestera_port *port, 102bb5dbf2cSVolodymyr Mytnyk struct prestera_port_mac_config *cfg) 103bb5dbf2cSVolodymyr Mytnyk { 104501ef306SVadym Kochan int err; 105501ef306SVadym Kochan 106bb5dbf2cSVolodymyr Mytnyk err = prestera_hw_port_mac_mode_set(port, cfg->admin, 107bb5dbf2cSVolodymyr Mytnyk cfg->mode, cfg->inband, cfg->speed, 108bb5dbf2cSVolodymyr Mytnyk cfg->duplex, cfg->fec); 109501ef306SVadym Kochan if (err) 110501ef306SVadym Kochan return err; 111501ef306SVadym Kochan 112bb5dbf2cSVolodymyr Mytnyk port->cfg_mac = *cfg; 113bb5dbf2cSVolodymyr Mytnyk return 0; 114bb5dbf2cSVolodymyr Mytnyk } 115bb5dbf2cSVolodymyr Mytnyk 116bb5dbf2cSVolodymyr Mytnyk static int prestera_port_open(struct net_device *dev) 117bb5dbf2cSVolodymyr Mytnyk { 118bb5dbf2cSVolodymyr Mytnyk struct prestera_port *port = netdev_priv(dev); 119bb5dbf2cSVolodymyr Mytnyk struct prestera_port_mac_config cfg_mac; 120bb5dbf2cSVolodymyr Mytnyk int err = 0; 121bb5dbf2cSVolodymyr Mytnyk 122bb5dbf2cSVolodymyr Mytnyk if (port->caps.transceiver == PRESTERA_PORT_TCVR_SFP) { 123bb5dbf2cSVolodymyr Mytnyk err = prestera_port_cfg_mac_read(port, &cfg_mac); 124bb5dbf2cSVolodymyr Mytnyk if (!err) { 125bb5dbf2cSVolodymyr Mytnyk cfg_mac.admin = true; 126bb5dbf2cSVolodymyr Mytnyk err = prestera_port_cfg_mac_write(port, &cfg_mac); 127bb5dbf2cSVolodymyr Mytnyk } 128bb5dbf2cSVolodymyr Mytnyk } else { 129bb5dbf2cSVolodymyr Mytnyk port->cfg_phy.admin = true; 130bb5dbf2cSVolodymyr Mytnyk err = prestera_hw_port_phy_mode_set(port, true, port->autoneg, 131bb5dbf2cSVolodymyr Mytnyk port->cfg_phy.mode, 132bb5dbf2cSVolodymyr Mytnyk port->adver_link_modes, 133bb5dbf2cSVolodymyr Mytnyk port->cfg_phy.mdix); 134bb5dbf2cSVolodymyr Mytnyk } 135bb5dbf2cSVolodymyr Mytnyk 136501ef306SVadym Kochan netif_start_queue(dev); 137501ef306SVadym Kochan 138bb5dbf2cSVolodymyr Mytnyk return err; 139501ef306SVadym Kochan } 140501ef306SVadym Kochan 141501ef306SVadym Kochan static int prestera_port_close(struct net_device *dev) 142501ef306SVadym Kochan { 143501ef306SVadym Kochan struct prestera_port *port = netdev_priv(dev); 144bb5dbf2cSVolodymyr Mytnyk struct prestera_port_mac_config cfg_mac; 145bb5dbf2cSVolodymyr Mytnyk int err = 0; 146501ef306SVadym Kochan 147501ef306SVadym Kochan netif_stop_queue(dev); 148501ef306SVadym Kochan 149bb5dbf2cSVolodymyr Mytnyk if (port->caps.transceiver == PRESTERA_PORT_TCVR_SFP) { 150bb5dbf2cSVolodymyr Mytnyk err = prestera_port_cfg_mac_read(port, &cfg_mac); 151bb5dbf2cSVolodymyr Mytnyk if (!err) { 152bb5dbf2cSVolodymyr Mytnyk cfg_mac.admin = false; 153bb5dbf2cSVolodymyr Mytnyk prestera_port_cfg_mac_write(port, &cfg_mac); 154bb5dbf2cSVolodymyr Mytnyk } 155bb5dbf2cSVolodymyr Mytnyk } else { 156bb5dbf2cSVolodymyr Mytnyk port->cfg_phy.admin = false; 157bb5dbf2cSVolodymyr Mytnyk err = prestera_hw_port_phy_mode_set(port, false, port->autoneg, 158bb5dbf2cSVolodymyr Mytnyk port->cfg_phy.mode, 159bb5dbf2cSVolodymyr Mytnyk port->adver_link_modes, 160bb5dbf2cSVolodymyr Mytnyk port->cfg_phy.mdix); 161bb5dbf2cSVolodymyr Mytnyk } 162bb5dbf2cSVolodymyr Mytnyk 163bb5dbf2cSVolodymyr Mytnyk return err; 164501ef306SVadym Kochan } 165501ef306SVadym Kochan 166501ef306SVadym Kochan static netdev_tx_t prestera_port_xmit(struct sk_buff *skb, 167501ef306SVadym Kochan struct net_device *dev) 168501ef306SVadym Kochan { 169501ef306SVadym Kochan return prestera_rxtx_xmit(netdev_priv(dev), skb); 170501ef306SVadym Kochan } 171501ef306SVadym Kochan 172da3c1639SYevhen Orlov int prestera_is_valid_mac_addr(struct prestera_port *port, const u8 *addr) 173501ef306SVadym Kochan { 174501ef306SVadym Kochan if (!is_valid_ether_addr(addr)) 175501ef306SVadym Kochan return -EADDRNOTAVAIL; 176501ef306SVadym Kochan 177501ef306SVadym Kochan /* firmware requires that port's MAC address contains first 5 bytes 178501ef306SVadym Kochan * of the base MAC address 179501ef306SVadym Kochan */ 180501ef306SVadym Kochan if (memcmp(port->sw->base_mac, addr, ETH_ALEN - 1)) 181501ef306SVadym Kochan return -EINVAL; 182501ef306SVadym Kochan 183501ef306SVadym Kochan return 0; 184501ef306SVadym Kochan } 185501ef306SVadym Kochan 186501ef306SVadym Kochan static int prestera_port_set_mac_address(struct net_device *dev, void *p) 187501ef306SVadym Kochan { 188501ef306SVadym Kochan struct prestera_port *port = netdev_priv(dev); 189501ef306SVadym Kochan struct sockaddr *addr = p; 190501ef306SVadym Kochan int err; 191501ef306SVadym Kochan 192501ef306SVadym Kochan err = prestera_is_valid_mac_addr(port, addr->sa_data); 193501ef306SVadym Kochan if (err) 194501ef306SVadym Kochan return err; 195501ef306SVadym Kochan 196501ef306SVadym Kochan err = prestera_hw_port_mac_set(port, addr->sa_data); 197501ef306SVadym Kochan if (err) 198501ef306SVadym Kochan return err; 199501ef306SVadym Kochan 200f3956ebbSJakub Kicinski eth_hw_addr_set(dev, addr->sa_data); 201501ef306SVadym Kochan 202501ef306SVadym Kochan return 0; 203501ef306SVadym Kochan } 204501ef306SVadym Kochan 205501ef306SVadym Kochan static int prestera_port_change_mtu(struct net_device *dev, int mtu) 206501ef306SVadym Kochan { 207501ef306SVadym Kochan struct prestera_port *port = netdev_priv(dev); 208501ef306SVadym Kochan int err; 209501ef306SVadym Kochan 210501ef306SVadym Kochan err = prestera_hw_port_mtu_set(port, mtu); 211501ef306SVadym Kochan if (err) 212501ef306SVadym Kochan return err; 213501ef306SVadym Kochan 214501ef306SVadym Kochan dev->mtu = mtu; 215501ef306SVadym Kochan 216501ef306SVadym Kochan return 0; 217501ef306SVadym Kochan } 218501ef306SVadym Kochan 219501ef306SVadym Kochan static void prestera_port_get_stats64(struct net_device *dev, 220501ef306SVadym Kochan struct rtnl_link_stats64 *stats) 221501ef306SVadym Kochan { 222501ef306SVadym Kochan struct prestera_port *port = netdev_priv(dev); 223501ef306SVadym Kochan struct prestera_port_stats *port_stats = &port->cached_hw_stats.stats; 224501ef306SVadym Kochan 225501ef306SVadym Kochan stats->rx_packets = port_stats->broadcast_frames_received + 226501ef306SVadym Kochan port_stats->multicast_frames_received + 227501ef306SVadym Kochan port_stats->unicast_frames_received; 228501ef306SVadym Kochan 229501ef306SVadym Kochan stats->tx_packets = port_stats->broadcast_frames_sent + 230501ef306SVadym Kochan port_stats->multicast_frames_sent + 231501ef306SVadym Kochan port_stats->unicast_frames_sent; 232501ef306SVadym Kochan 233501ef306SVadym Kochan stats->rx_bytes = port_stats->good_octets_received; 234501ef306SVadym Kochan 235501ef306SVadym Kochan stats->tx_bytes = port_stats->good_octets_sent; 236501ef306SVadym Kochan 237501ef306SVadym Kochan stats->rx_errors = port_stats->rx_error_frame_received; 238501ef306SVadym Kochan stats->tx_errors = port_stats->mac_trans_error; 239501ef306SVadym Kochan 240501ef306SVadym Kochan stats->rx_dropped = port_stats->buffer_overrun; 241501ef306SVadym Kochan stats->tx_dropped = 0; 242501ef306SVadym Kochan 243501ef306SVadym Kochan stats->multicast = port_stats->multicast_frames_received; 244501ef306SVadym Kochan stats->collisions = port_stats->excessive_collision; 245501ef306SVadym Kochan 246501ef306SVadym Kochan stats->rx_crc_errors = port_stats->bad_crc; 247501ef306SVadym Kochan } 248501ef306SVadym Kochan 249501ef306SVadym Kochan static void prestera_port_get_hw_stats(struct prestera_port *port) 250501ef306SVadym Kochan { 251501ef306SVadym Kochan prestera_hw_port_stats_get(port, &port->cached_hw_stats.stats); 252501ef306SVadym Kochan } 253501ef306SVadym Kochan 254501ef306SVadym Kochan static void prestera_port_stats_update(struct work_struct *work) 255501ef306SVadym Kochan { 256501ef306SVadym Kochan struct prestera_port *port = 257501ef306SVadym Kochan container_of(work, struct prestera_port, 258501ef306SVadym Kochan cached_hw_stats.caching_dw.work); 259501ef306SVadym Kochan 260501ef306SVadym Kochan prestera_port_get_hw_stats(port); 261501ef306SVadym Kochan 262501ef306SVadym Kochan queue_delayed_work(prestera_wq, &port->cached_hw_stats.caching_dw, 263501ef306SVadym Kochan msecs_to_jiffies(PRESTERA_STATS_DELAY_MS)); 264501ef306SVadym Kochan } 265501ef306SVadym Kochan 2668b474a9fSSerhiy Boiko static int prestera_port_setup_tc(struct net_device *dev, 2678b474a9fSSerhiy Boiko enum tc_setup_type type, 2688b474a9fSSerhiy Boiko void *type_data) 2698b474a9fSSerhiy Boiko { 2708b474a9fSSerhiy Boiko struct prestera_port *port = netdev_priv(dev); 2718b474a9fSSerhiy Boiko 2728b474a9fSSerhiy Boiko switch (type) { 2738b474a9fSSerhiy Boiko case TC_SETUP_BLOCK: 2748b474a9fSSerhiy Boiko return prestera_flow_block_setup(port, type_data); 2758b474a9fSSerhiy Boiko default: 2768b474a9fSSerhiy Boiko return -EOPNOTSUPP; 2778b474a9fSSerhiy Boiko } 2788b474a9fSSerhiy Boiko } 2798b474a9fSSerhiy Boiko 280501ef306SVadym Kochan static const struct net_device_ops prestera_netdev_ops = { 281501ef306SVadym Kochan .ndo_open = prestera_port_open, 282501ef306SVadym Kochan .ndo_stop = prestera_port_close, 283501ef306SVadym Kochan .ndo_start_xmit = prestera_port_xmit, 2848b474a9fSSerhiy Boiko .ndo_setup_tc = prestera_port_setup_tc, 285501ef306SVadym Kochan .ndo_change_mtu = prestera_port_change_mtu, 286501ef306SVadym Kochan .ndo_get_stats64 = prestera_port_get_stats64, 287501ef306SVadym Kochan .ndo_set_mac_address = prestera_port_set_mac_address, 28834dd1710SVadym Kochan .ndo_get_devlink_port = prestera_devlink_get_port, 289501ef306SVadym Kochan }; 290501ef306SVadym Kochan 291bb5dbf2cSVolodymyr Mytnyk int prestera_port_autoneg_set(struct prestera_port *port, u64 link_modes) 292501ef306SVadym Kochan { 293501ef306SVadym Kochan int err; 294501ef306SVadym Kochan 295bb5dbf2cSVolodymyr Mytnyk if (port->autoneg && port->adver_link_modes == link_modes) 296501ef306SVadym Kochan return 0; 297501ef306SVadym Kochan 298bb5dbf2cSVolodymyr Mytnyk err = prestera_hw_port_phy_mode_set(port, port->cfg_phy.admin, 299bb5dbf2cSVolodymyr Mytnyk true, 0, link_modes, 300bb5dbf2cSVolodymyr Mytnyk port->cfg_phy.mdix); 301501ef306SVadym Kochan if (err) 302501ef306SVadym Kochan return err; 303501ef306SVadym Kochan 304bb5dbf2cSVolodymyr Mytnyk port->adver_fec = BIT(PRESTERA_PORT_FEC_OFF); 305bb5dbf2cSVolodymyr Mytnyk port->adver_link_modes = link_modes; 306bb5dbf2cSVolodymyr Mytnyk port->cfg_phy.mode = 0; 307bb5dbf2cSVolodymyr Mytnyk port->autoneg = true; 308501ef306SVadym Kochan 309501ef306SVadym Kochan return 0; 310501ef306SVadym Kochan } 311501ef306SVadym Kochan 312501ef306SVadym Kochan static void prestera_port_list_add(struct prestera_port *port) 313501ef306SVadym Kochan { 314501ef306SVadym Kochan write_lock(&port->sw->port_list_lock); 315501ef306SVadym Kochan list_add(&port->list, &port->sw->port_list); 316501ef306SVadym Kochan write_unlock(&port->sw->port_list_lock); 317501ef306SVadym Kochan } 318501ef306SVadym Kochan 319501ef306SVadym Kochan static void prestera_port_list_del(struct prestera_port *port) 320501ef306SVadym Kochan { 321501ef306SVadym Kochan write_lock(&port->sw->port_list_lock); 322501ef306SVadym Kochan list_del(&port->list); 323501ef306SVadym Kochan write_unlock(&port->sw->port_list_lock); 324501ef306SVadym Kochan } 325501ef306SVadym Kochan 326501ef306SVadym Kochan static int prestera_port_create(struct prestera_switch *sw, u32 id) 327501ef306SVadym Kochan { 328bb5dbf2cSVolodymyr Mytnyk struct prestera_port_mac_config cfg_mac; 329501ef306SVadym Kochan struct prestera_port *port; 330501ef306SVadym Kochan struct net_device *dev; 331501ef306SVadym Kochan int err; 332501ef306SVadym Kochan 333501ef306SVadym Kochan dev = alloc_etherdev(sizeof(*port)); 334501ef306SVadym Kochan if (!dev) 335501ef306SVadym Kochan return -ENOMEM; 336501ef306SVadym Kochan 337501ef306SVadym Kochan port = netdev_priv(dev); 338501ef306SVadym Kochan 339e1189d9aSVadym Kochan INIT_LIST_HEAD(&port->vlans_list); 340e1189d9aSVadym Kochan port->pvid = PRESTERA_DEFAULT_VID; 341255213caSSerhiy Boiko port->lag = NULL; 342501ef306SVadym Kochan port->dev = dev; 343501ef306SVadym Kochan port->id = id; 344501ef306SVadym Kochan port->sw = sw; 345501ef306SVadym Kochan 346501ef306SVadym Kochan err = prestera_hw_port_info_get(port, &port->dev_id, &port->hw_id, 347501ef306SVadym Kochan &port->fp_id); 348501ef306SVadym Kochan if (err) { 349501ef306SVadym Kochan dev_err(prestera_dev(sw), "Failed to get port(%u) info\n", id); 35034dd1710SVadym Kochan goto err_port_info_get; 351501ef306SVadym Kochan } 352501ef306SVadym Kochan 35334dd1710SVadym Kochan err = prestera_devlink_port_register(port); 35434dd1710SVadym Kochan if (err) 35534dd1710SVadym Kochan goto err_dl_port_register; 35634dd1710SVadym Kochan 3578b474a9fSSerhiy Boiko dev->features |= NETIF_F_NETNS_LOCAL | NETIF_F_HW_TC; 358501ef306SVadym Kochan dev->netdev_ops = &prestera_netdev_ops; 359a97d3c69SVadym Kochan dev->ethtool_ops = &prestera_ethtool_ops; 360501ef306SVadym Kochan 361501ef306SVadym Kochan netif_carrier_off(dev); 362501ef306SVadym Kochan 363501ef306SVadym Kochan dev->mtu = min_t(unsigned int, sw->mtu_max, PRESTERA_MTU_DEFAULT); 364501ef306SVadym Kochan dev->min_mtu = sw->mtu_min; 365501ef306SVadym Kochan dev->max_mtu = sw->mtu_max; 366501ef306SVadym Kochan 367501ef306SVadym Kochan err = prestera_hw_port_mtu_set(port, dev->mtu); 368501ef306SVadym Kochan if (err) { 369501ef306SVadym Kochan dev_err(prestera_dev(sw), "Failed to set port(%u) mtu(%d)\n", 370501ef306SVadym Kochan id, dev->mtu); 371501ef306SVadym Kochan goto err_port_init; 372501ef306SVadym Kochan } 373501ef306SVadym Kochan 3744de377b6SZhang Changzhong if (port->fp_id >= PRESTERA_MAC_ADDR_NUM_MAX) { 3754de377b6SZhang Changzhong err = -EINVAL; 376501ef306SVadym Kochan goto err_port_init; 3774de377b6SZhang Changzhong } 378501ef306SVadym Kochan 3798eb8192eSJakub Kicinski eth_hw_addr_gen(dev, sw->base_mac, port->fp_id); 380501ef306SVadym Kochan /* firmware requires that port's MAC address consist of the first 381501ef306SVadym Kochan * 5 bytes of the base MAC address 382501ef306SVadym Kochan */ 3838eb8192eSJakub Kicinski if (memcmp(dev->dev_addr, sw->base_mac, ETH_ALEN - 1)) { 3848eb8192eSJakub Kicinski dev_warn(prestera_dev(sw), "Port MAC address wraps for port(%u)\n", id); 3858eb8192eSJakub Kicinski dev_addr_mod(dev, 0, sw->base_mac, ETH_ALEN - 1); 3868eb8192eSJakub Kicinski } 387501ef306SVadym Kochan 388501ef306SVadym Kochan err = prestera_hw_port_mac_set(port, dev->dev_addr); 389501ef306SVadym Kochan if (err) { 390501ef306SVadym Kochan dev_err(prestera_dev(sw), "Failed to set port(%u) mac addr\n", id); 391501ef306SVadym Kochan goto err_port_init; 392501ef306SVadym Kochan } 393501ef306SVadym Kochan 394501ef306SVadym Kochan err = prestera_hw_port_cap_get(port, &port->caps); 395501ef306SVadym Kochan if (err) { 396501ef306SVadym Kochan dev_err(prestera_dev(sw), "Failed to get port(%u) caps\n", id); 397501ef306SVadym Kochan goto err_port_init; 398501ef306SVadym Kochan } 399501ef306SVadym Kochan 400bb5dbf2cSVolodymyr Mytnyk port->adver_link_modes = port->caps.supp_link_modes; 401bb5dbf2cSVolodymyr Mytnyk port->adver_fec = 0; 402bb5dbf2cSVolodymyr Mytnyk port->autoneg = true; 403501ef306SVadym Kochan 404bb5dbf2cSVolodymyr Mytnyk /* initialize config mac */ 405bb5dbf2cSVolodymyr Mytnyk if (port->caps.transceiver != PRESTERA_PORT_TCVR_SFP) { 406bb5dbf2cSVolodymyr Mytnyk cfg_mac.admin = true; 407bb5dbf2cSVolodymyr Mytnyk cfg_mac.mode = PRESTERA_MAC_MODE_INTERNAL; 408bb5dbf2cSVolodymyr Mytnyk } else { 409bb5dbf2cSVolodymyr Mytnyk cfg_mac.admin = false; 410bb5dbf2cSVolodymyr Mytnyk cfg_mac.mode = PRESTERA_MAC_MODE_MAX; 411bb5dbf2cSVolodymyr Mytnyk } 412bb5dbf2cSVolodymyr Mytnyk cfg_mac.inband = false; 413bb5dbf2cSVolodymyr Mytnyk cfg_mac.speed = 0; 414bb5dbf2cSVolodymyr Mytnyk cfg_mac.duplex = DUPLEX_UNKNOWN; 415bb5dbf2cSVolodymyr Mytnyk cfg_mac.fec = PRESTERA_PORT_FEC_OFF; 416bb5dbf2cSVolodymyr Mytnyk 417bb5dbf2cSVolodymyr Mytnyk err = prestera_port_cfg_mac_write(port, &cfg_mac); 418501ef306SVadym Kochan if (err) { 419a46a5036SVolodymyr Mytnyk dev_err(prestera_dev(sw), 420a46a5036SVolodymyr Mytnyk "Failed to set port(%u) mac mode\n", id); 421501ef306SVadym Kochan goto err_port_init; 422501ef306SVadym Kochan } 423501ef306SVadym Kochan 424bb5dbf2cSVolodymyr Mytnyk /* initialize config phy (if this is inegral) */ 425bb5dbf2cSVolodymyr Mytnyk if (port->caps.transceiver != PRESTERA_PORT_TCVR_SFP) { 426bb5dbf2cSVolodymyr Mytnyk port->cfg_phy.mdix = ETH_TP_MDI_AUTO; 427bb5dbf2cSVolodymyr Mytnyk port->cfg_phy.admin = false; 428bb5dbf2cSVolodymyr Mytnyk err = prestera_hw_port_phy_mode_set(port, 429bb5dbf2cSVolodymyr Mytnyk port->cfg_phy.admin, 430bb5dbf2cSVolodymyr Mytnyk false, 0, 0, 431bb5dbf2cSVolodymyr Mytnyk port->cfg_phy.mdix); 432bb5dbf2cSVolodymyr Mytnyk if (err) { 433a46a5036SVolodymyr Mytnyk dev_err(prestera_dev(sw), 434a46a5036SVolodymyr Mytnyk "Failed to set port(%u) phy mode\n", id); 435bb5dbf2cSVolodymyr Mytnyk goto err_port_init; 436bb5dbf2cSVolodymyr Mytnyk } 437bb5dbf2cSVolodymyr Mytnyk } 438bb5dbf2cSVolodymyr Mytnyk 439501ef306SVadym Kochan err = prestera_rxtx_port_init(port); 440501ef306SVadym Kochan if (err) 441501ef306SVadym Kochan goto err_port_init; 442501ef306SVadym Kochan 443501ef306SVadym Kochan INIT_DELAYED_WORK(&port->cached_hw_stats.caching_dw, 444501ef306SVadym Kochan &prestera_port_stats_update); 445501ef306SVadym Kochan 446501ef306SVadym Kochan prestera_port_list_add(port); 447501ef306SVadym Kochan 448501ef306SVadym Kochan err = register_netdev(dev); 449501ef306SVadym Kochan if (err) 450501ef306SVadym Kochan goto err_register_netdev; 451501ef306SVadym Kochan 45234dd1710SVadym Kochan prestera_devlink_port_set(port); 45334dd1710SVadym Kochan 454501ef306SVadym Kochan return 0; 455501ef306SVadym Kochan 456501ef306SVadym Kochan err_register_netdev: 457501ef306SVadym Kochan prestera_port_list_del(port); 458501ef306SVadym Kochan err_port_init: 45934dd1710SVadym Kochan prestera_devlink_port_unregister(port); 46034dd1710SVadym Kochan err_dl_port_register: 46134dd1710SVadym Kochan err_port_info_get: 462501ef306SVadym Kochan free_netdev(dev); 463501ef306SVadym Kochan return err; 464501ef306SVadym Kochan } 465501ef306SVadym Kochan 466501ef306SVadym Kochan static void prestera_port_destroy(struct prestera_port *port) 467501ef306SVadym Kochan { 468501ef306SVadym Kochan struct net_device *dev = port->dev; 469501ef306SVadym Kochan 470501ef306SVadym Kochan cancel_delayed_work_sync(&port->cached_hw_stats.caching_dw); 47134dd1710SVadym Kochan prestera_devlink_port_clear(port); 472501ef306SVadym Kochan unregister_netdev(dev); 473501ef306SVadym Kochan prestera_port_list_del(port); 47434dd1710SVadym Kochan prestera_devlink_port_unregister(port); 475501ef306SVadym Kochan free_netdev(dev); 476501ef306SVadym Kochan } 477501ef306SVadym Kochan 478501ef306SVadym Kochan static void prestera_destroy_ports(struct prestera_switch *sw) 479501ef306SVadym Kochan { 480501ef306SVadym Kochan struct prestera_port *port, *tmp; 481501ef306SVadym Kochan 482501ef306SVadym Kochan list_for_each_entry_safe(port, tmp, &sw->port_list, list) 483501ef306SVadym Kochan prestera_port_destroy(port); 484501ef306SVadym Kochan } 485501ef306SVadym Kochan 486501ef306SVadym Kochan static int prestera_create_ports(struct prestera_switch *sw) 487501ef306SVadym Kochan { 488501ef306SVadym Kochan struct prestera_port *port, *tmp; 489501ef306SVadym Kochan u32 port_idx; 490501ef306SVadym Kochan int err; 491501ef306SVadym Kochan 492501ef306SVadym Kochan for (port_idx = 0; port_idx < sw->port_count; port_idx++) { 493501ef306SVadym Kochan err = prestera_port_create(sw, port_idx); 494501ef306SVadym Kochan if (err) 495501ef306SVadym Kochan goto err_port_create; 496501ef306SVadym Kochan } 497501ef306SVadym Kochan 498501ef306SVadym Kochan return 0; 499501ef306SVadym Kochan 500501ef306SVadym Kochan err_port_create: 501501ef306SVadym Kochan list_for_each_entry_safe(port, tmp, &sw->port_list, list) 502501ef306SVadym Kochan prestera_port_destroy(port); 503501ef306SVadym Kochan 504501ef306SVadym Kochan return err; 505501ef306SVadym Kochan } 506501ef306SVadym Kochan 507501ef306SVadym Kochan static void prestera_port_handle_event(struct prestera_switch *sw, 508501ef306SVadym Kochan struct prestera_event *evt, void *arg) 509501ef306SVadym Kochan { 510501ef306SVadym Kochan struct delayed_work *caching_dw; 511501ef306SVadym Kochan struct prestera_port *port; 512501ef306SVadym Kochan 513501ef306SVadym Kochan port = prestera_find_port(sw, evt->port_evt.port_id); 514501ef306SVadym Kochan if (!port || !port->dev) 515501ef306SVadym Kochan return; 516501ef306SVadym Kochan 517501ef306SVadym Kochan caching_dw = &port->cached_hw_stats.caching_dw; 518501ef306SVadym Kochan 519bb5dbf2cSVolodymyr Mytnyk prestera_ethtool_port_state_changed(port, &evt->port_evt); 520bb5dbf2cSVolodymyr Mytnyk 521bb5dbf2cSVolodymyr Mytnyk if (evt->id == PRESTERA_PORT_EVENT_MAC_STATE_CHANGED) { 522bb5dbf2cSVolodymyr Mytnyk if (port->state_mac.oper) { 523501ef306SVadym Kochan netif_carrier_on(port->dev); 524501ef306SVadym Kochan if (!delayed_work_pending(caching_dw)) 525501ef306SVadym Kochan queue_delayed_work(prestera_wq, caching_dw, 0); 52633398048SVadym Kochan } else if (netif_running(port->dev) && 52733398048SVadym Kochan netif_carrier_ok(port->dev)) { 528501ef306SVadym Kochan netif_carrier_off(port->dev); 529501ef306SVadym Kochan if (delayed_work_pending(caching_dw)) 530501ef306SVadym Kochan cancel_delayed_work(caching_dw); 531501ef306SVadym Kochan } 532501ef306SVadym Kochan } 533501ef306SVadym Kochan } 534501ef306SVadym Kochan 535501ef306SVadym Kochan static int prestera_event_handlers_register(struct prestera_switch *sw) 536501ef306SVadym Kochan { 537501ef306SVadym Kochan return prestera_hw_event_handler_register(sw, PRESTERA_EVENT_TYPE_PORT, 538501ef306SVadym Kochan prestera_port_handle_event, 539501ef306SVadym Kochan NULL); 540501ef306SVadym Kochan } 541501ef306SVadym Kochan 542501ef306SVadym Kochan static void prestera_event_handlers_unregister(struct prestera_switch *sw) 543501ef306SVadym Kochan { 544501ef306SVadym Kochan prestera_hw_event_handler_unregister(sw, PRESTERA_EVENT_TYPE_PORT, 545501ef306SVadym Kochan prestera_port_handle_event); 546501ef306SVadym Kochan } 547501ef306SVadym Kochan 548501ef306SVadym Kochan static int prestera_switch_set_base_mac_addr(struct prestera_switch *sw) 549501ef306SVadym Kochan { 550501ef306SVadym Kochan struct device_node *base_mac_np; 551501ef306SVadym Kochan struct device_node *np; 55283216e39SMichael Walle int ret; 553501ef306SVadym Kochan 554501ef306SVadym Kochan np = of_find_compatible_node(NULL, NULL, "marvell,prestera"); 555501ef306SVadym Kochan base_mac_np = of_parse_phandle(np, "base-mac-provider", 0); 556501ef306SVadym Kochan 55783216e39SMichael Walle ret = of_get_mac_address(base_mac_np, sw->base_mac); 55883216e39SMichael Walle if (ret) { 559501ef306SVadym Kochan eth_random_addr(sw->base_mac); 560501ef306SVadym Kochan dev_info(prestera_dev(sw), "using random base mac address\n"); 561501ef306SVadym Kochan } 56283216e39SMichael Walle of_node_put(base_mac_np); 563c9ffa3e2SMiaoqian Lin of_node_put(np); 564501ef306SVadym Kochan 565501ef306SVadym Kochan return prestera_hw_switch_mac_set(sw, sw->base_mac); 566501ef306SVadym Kochan } 567501ef306SVadym Kochan 568255213caSSerhiy Boiko struct prestera_lag *prestera_lag_by_id(struct prestera_switch *sw, u16 id) 569255213caSSerhiy Boiko { 570255213caSSerhiy Boiko return id < sw->lag_max ? &sw->lags[id] : NULL; 571255213caSSerhiy Boiko } 572255213caSSerhiy Boiko 573255213caSSerhiy Boiko static struct prestera_lag *prestera_lag_by_dev(struct prestera_switch *sw, 574255213caSSerhiy Boiko struct net_device *dev) 575255213caSSerhiy Boiko { 576255213caSSerhiy Boiko struct prestera_lag *lag; 577255213caSSerhiy Boiko u16 id; 578255213caSSerhiy Boiko 579255213caSSerhiy Boiko for (id = 0; id < sw->lag_max; id++) { 580255213caSSerhiy Boiko lag = &sw->lags[id]; 581255213caSSerhiy Boiko if (lag->dev == dev) 582255213caSSerhiy Boiko return lag; 583255213caSSerhiy Boiko } 584255213caSSerhiy Boiko 585255213caSSerhiy Boiko return NULL; 586255213caSSerhiy Boiko } 587255213caSSerhiy Boiko 588255213caSSerhiy Boiko static struct prestera_lag *prestera_lag_create(struct prestera_switch *sw, 589255213caSSerhiy Boiko struct net_device *lag_dev) 590255213caSSerhiy Boiko { 591255213caSSerhiy Boiko struct prestera_lag *lag = NULL; 592255213caSSerhiy Boiko u16 id; 593255213caSSerhiy Boiko 594255213caSSerhiy Boiko for (id = 0; id < sw->lag_max; id++) { 595255213caSSerhiy Boiko lag = &sw->lags[id]; 596255213caSSerhiy Boiko if (!lag->dev) 597255213caSSerhiy Boiko break; 598255213caSSerhiy Boiko } 599255213caSSerhiy Boiko if (lag) { 600255213caSSerhiy Boiko INIT_LIST_HEAD(&lag->members); 601255213caSSerhiy Boiko lag->dev = lag_dev; 602255213caSSerhiy Boiko } 603255213caSSerhiy Boiko 604255213caSSerhiy Boiko return lag; 605255213caSSerhiy Boiko } 606255213caSSerhiy Boiko 607255213caSSerhiy Boiko static void prestera_lag_destroy(struct prestera_switch *sw, 608255213caSSerhiy Boiko struct prestera_lag *lag) 609255213caSSerhiy Boiko { 610255213caSSerhiy Boiko WARN_ON(!list_empty(&lag->members)); 611255213caSSerhiy Boiko lag->member_count = 0; 612255213caSSerhiy Boiko lag->dev = NULL; 613255213caSSerhiy Boiko } 614255213caSSerhiy Boiko 615255213caSSerhiy Boiko static int prestera_lag_port_add(struct prestera_port *port, 616255213caSSerhiy Boiko struct net_device *lag_dev) 617255213caSSerhiy Boiko { 618255213caSSerhiy Boiko struct prestera_switch *sw = port->sw; 619255213caSSerhiy Boiko struct prestera_lag *lag; 620255213caSSerhiy Boiko int err; 621255213caSSerhiy Boiko 622255213caSSerhiy Boiko lag = prestera_lag_by_dev(sw, lag_dev); 623255213caSSerhiy Boiko if (!lag) { 624255213caSSerhiy Boiko lag = prestera_lag_create(sw, lag_dev); 625255213caSSerhiy Boiko if (!lag) 626255213caSSerhiy Boiko return -ENOSPC; 627255213caSSerhiy Boiko } 628255213caSSerhiy Boiko 629255213caSSerhiy Boiko if (lag->member_count >= sw->lag_member_max) 630255213caSSerhiy Boiko return -ENOSPC; 631255213caSSerhiy Boiko 632255213caSSerhiy Boiko err = prestera_hw_lag_member_add(port, lag->lag_id); 633255213caSSerhiy Boiko if (err) { 634255213caSSerhiy Boiko if (!lag->member_count) 635255213caSSerhiy Boiko prestera_lag_destroy(sw, lag); 636255213caSSerhiy Boiko return err; 637255213caSSerhiy Boiko } 638255213caSSerhiy Boiko 639255213caSSerhiy Boiko list_add(&port->lag_member, &lag->members); 640255213caSSerhiy Boiko lag->member_count++; 641255213caSSerhiy Boiko port->lag = lag; 642255213caSSerhiy Boiko 643255213caSSerhiy Boiko return 0; 644255213caSSerhiy Boiko } 645255213caSSerhiy Boiko 646255213caSSerhiy Boiko static int prestera_lag_port_del(struct prestera_port *port) 647255213caSSerhiy Boiko { 648255213caSSerhiy Boiko struct prestera_switch *sw = port->sw; 649255213caSSerhiy Boiko struct prestera_lag *lag = port->lag; 650255213caSSerhiy Boiko int err; 651255213caSSerhiy Boiko 652255213caSSerhiy Boiko if (!lag || !lag->member_count) 653255213caSSerhiy Boiko return -EINVAL; 654255213caSSerhiy Boiko 655255213caSSerhiy Boiko err = prestera_hw_lag_member_del(port, lag->lag_id); 656255213caSSerhiy Boiko if (err) 657255213caSSerhiy Boiko return err; 658255213caSSerhiy Boiko 659255213caSSerhiy Boiko list_del(&port->lag_member); 660255213caSSerhiy Boiko lag->member_count--; 661255213caSSerhiy Boiko port->lag = NULL; 662255213caSSerhiy Boiko 663255213caSSerhiy Boiko if (netif_is_bridge_port(lag->dev)) { 664255213caSSerhiy Boiko struct net_device *br_dev; 665255213caSSerhiy Boiko 666255213caSSerhiy Boiko br_dev = netdev_master_upper_dev_get(lag->dev); 667255213caSSerhiy Boiko 668255213caSSerhiy Boiko prestera_bridge_port_leave(br_dev, port); 669255213caSSerhiy Boiko } 670255213caSSerhiy Boiko 671255213caSSerhiy Boiko if (!lag->member_count) 672255213caSSerhiy Boiko prestera_lag_destroy(sw, lag); 673255213caSSerhiy Boiko 674255213caSSerhiy Boiko return 0; 675255213caSSerhiy Boiko } 676255213caSSerhiy Boiko 677255213caSSerhiy Boiko bool prestera_port_is_lag_member(const struct prestera_port *port) 678255213caSSerhiy Boiko { 679255213caSSerhiy Boiko return !!port->lag; 680255213caSSerhiy Boiko } 681255213caSSerhiy Boiko 682255213caSSerhiy Boiko u16 prestera_port_lag_id(const struct prestera_port *port) 683255213caSSerhiy Boiko { 684255213caSSerhiy Boiko return port->lag->lag_id; 685255213caSSerhiy Boiko } 686255213caSSerhiy Boiko 687255213caSSerhiy Boiko static int prestera_lag_init(struct prestera_switch *sw) 688255213caSSerhiy Boiko { 689255213caSSerhiy Boiko u16 id; 690255213caSSerhiy Boiko 691255213caSSerhiy Boiko sw->lags = kcalloc(sw->lag_max, sizeof(*sw->lags), GFP_KERNEL); 692255213caSSerhiy Boiko if (!sw->lags) 693255213caSSerhiy Boiko return -ENOMEM; 694255213caSSerhiy Boiko 695255213caSSerhiy Boiko for (id = 0; id < sw->lag_max; id++) 696255213caSSerhiy Boiko sw->lags[id].lag_id = id; 697255213caSSerhiy Boiko 698255213caSSerhiy Boiko return 0; 699255213caSSerhiy Boiko } 700255213caSSerhiy Boiko 701255213caSSerhiy Boiko static void prestera_lag_fini(struct prestera_switch *sw) 702255213caSSerhiy Boiko { 703255213caSSerhiy Boiko u8 idx; 704255213caSSerhiy Boiko 705255213caSSerhiy Boiko for (idx = 0; idx < sw->lag_max; idx++) 706255213caSSerhiy Boiko WARN_ON(sw->lags[idx].member_count); 707255213caSSerhiy Boiko 708255213caSSerhiy Boiko kfree(sw->lags); 709255213caSSerhiy Boiko } 710255213caSSerhiy Boiko 711e1189d9aSVadym Kochan bool prestera_netdev_check(const struct net_device *dev) 712e1189d9aSVadym Kochan { 713e1189d9aSVadym Kochan return dev->netdev_ops == &prestera_netdev_ops; 714e1189d9aSVadym Kochan } 715e1189d9aSVadym Kochan 7168b0308feSDavid S. Miller static int prestera_lower_dev_walk(struct net_device *dev, 7178b0308feSDavid S. Miller struct netdev_nested_priv *priv) 718e1189d9aSVadym Kochan { 7198b0308feSDavid S. Miller struct prestera_port **pport = (struct prestera_port **)priv->data; 720e1189d9aSVadym Kochan 721e1189d9aSVadym Kochan if (prestera_netdev_check(dev)) { 722e1189d9aSVadym Kochan *pport = netdev_priv(dev); 723e1189d9aSVadym Kochan return 1; 724e1189d9aSVadym Kochan } 725e1189d9aSVadym Kochan 726e1189d9aSVadym Kochan return 0; 727e1189d9aSVadym Kochan } 728e1189d9aSVadym Kochan 729e1189d9aSVadym Kochan struct prestera_port *prestera_port_dev_lower_find(struct net_device *dev) 730e1189d9aSVadym Kochan { 731e1189d9aSVadym Kochan struct prestera_port *port = NULL; 7328b0308feSDavid S. Miller struct netdev_nested_priv priv = { 7338b0308feSDavid S. Miller .data = (void *)&port, 7348b0308feSDavid S. Miller }; 735e1189d9aSVadym Kochan 736e1189d9aSVadym Kochan if (prestera_netdev_check(dev)) 737e1189d9aSVadym Kochan return netdev_priv(dev); 738e1189d9aSVadym Kochan 7398b0308feSDavid S. Miller netdev_walk_all_lower_dev(dev, prestera_lower_dev_walk, &priv); 740e1189d9aSVadym Kochan 741e1189d9aSVadym Kochan return port; 742e1189d9aSVadym Kochan } 743e1189d9aSVadym Kochan 744255213caSSerhiy Boiko static int prestera_netdev_port_lower_event(struct net_device *dev, 745255213caSSerhiy Boiko unsigned long event, void *ptr) 746255213caSSerhiy Boiko { 747255213caSSerhiy Boiko struct netdev_notifier_changelowerstate_info *info = ptr; 748255213caSSerhiy Boiko struct netdev_lag_lower_state_info *lower_state_info; 749255213caSSerhiy Boiko struct prestera_port *port = netdev_priv(dev); 750255213caSSerhiy Boiko bool enabled; 751255213caSSerhiy Boiko 752255213caSSerhiy Boiko if (!netif_is_lag_port(dev)) 753255213caSSerhiy Boiko return 0; 754255213caSSerhiy Boiko if (!prestera_port_is_lag_member(port)) 755255213caSSerhiy Boiko return 0; 756255213caSSerhiy Boiko 757255213caSSerhiy Boiko lower_state_info = info->lower_state_info; 758255213caSSerhiy Boiko enabled = lower_state_info->link_up && lower_state_info->tx_enabled; 759255213caSSerhiy Boiko 760255213caSSerhiy Boiko return prestera_hw_lag_member_enable(port, port->lag->lag_id, enabled); 761255213caSSerhiy Boiko } 762255213caSSerhiy Boiko 763255213caSSerhiy Boiko static bool prestera_lag_master_check(struct net_device *lag_dev, 764255213caSSerhiy Boiko struct netdev_lag_upper_info *info, 765255213caSSerhiy Boiko struct netlink_ext_ack *ext_ack) 766255213caSSerhiy Boiko { 767255213caSSerhiy Boiko if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) { 768255213caSSerhiy Boiko NL_SET_ERR_MSG_MOD(ext_ack, "Unsupported LAG Tx type"); 769255213caSSerhiy Boiko return false; 770255213caSSerhiy Boiko } 771255213caSSerhiy Boiko 772255213caSSerhiy Boiko return true; 773255213caSSerhiy Boiko } 774255213caSSerhiy Boiko 775255213caSSerhiy Boiko static int prestera_netdev_port_event(struct net_device *lower, 776255213caSSerhiy Boiko struct net_device *dev, 777e1189d9aSVadym Kochan unsigned long event, void *ptr) 778e1189d9aSVadym Kochan { 7792efc2256SYevhen Orlov struct netdev_notifier_info *info = ptr; 7802efc2256SYevhen Orlov struct netdev_notifier_changeupper_info *cu_info; 78182bbaa05SVadym Kochan struct prestera_port *port = netdev_priv(dev); 7823d5048ccSVadym Kochan struct netlink_ext_ack *extack; 7833d5048ccSVadym Kochan struct net_device *upper; 7843d5048ccSVadym Kochan 7852efc2256SYevhen Orlov extack = netdev_notifier_info_to_extack(info); 7862efc2256SYevhen Orlov cu_info = container_of(info, 7872efc2256SYevhen Orlov struct netdev_notifier_changeupper_info, 7882efc2256SYevhen Orlov info); 7893d5048ccSVadym Kochan 790e1189d9aSVadym Kochan switch (event) { 791e1189d9aSVadym Kochan case NETDEV_PRECHANGEUPPER: 7922efc2256SYevhen Orlov upper = cu_info->upper_dev; 793255213caSSerhiy Boiko if (!netif_is_bridge_master(upper) && 794255213caSSerhiy Boiko !netif_is_lag_master(upper)) { 7953d5048ccSVadym Kochan NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type"); 7963d5048ccSVadym Kochan return -EINVAL; 797e1189d9aSVadym Kochan } 7983d5048ccSVadym Kochan 7992efc2256SYevhen Orlov if (!cu_info->linking) 8003d5048ccSVadym Kochan break; 8013d5048ccSVadym Kochan 8023d5048ccSVadym Kochan if (netdev_has_any_upper_dev(upper)) { 8033d5048ccSVadym Kochan NL_SET_ERR_MSG_MOD(extack, "Upper device is already enslaved"); 8043d5048ccSVadym Kochan return -EINVAL; 8053d5048ccSVadym Kochan } 806255213caSSerhiy Boiko 807255213caSSerhiy Boiko if (netif_is_lag_master(upper) && 8082efc2256SYevhen Orlov !prestera_lag_master_check(upper, cu_info->upper_info, extack)) 809255213caSSerhiy Boiko return -EOPNOTSUPP; 810255213caSSerhiy Boiko if (netif_is_lag_master(upper) && vlan_uses_dev(dev)) { 811255213caSSerhiy Boiko NL_SET_ERR_MSG_MOD(extack, 812255213caSSerhiy Boiko "Master device is a LAG master and port has a VLAN"); 813255213caSSerhiy Boiko return -EINVAL; 814255213caSSerhiy Boiko } 815255213caSSerhiy Boiko if (netif_is_lag_port(dev) && is_vlan_dev(upper) && 816255213caSSerhiy Boiko !netif_is_lag_master(vlan_dev_real_dev(upper))) { 817255213caSSerhiy Boiko NL_SET_ERR_MSG_MOD(extack, 818255213caSSerhiy Boiko "Can not put a VLAN on a LAG port"); 819255213caSSerhiy Boiko return -EINVAL; 820255213caSSerhiy Boiko } 8213d5048ccSVadym Kochan break; 8223d5048ccSVadym Kochan 8233d5048ccSVadym Kochan case NETDEV_CHANGEUPPER: 8242efc2256SYevhen Orlov upper = cu_info->upper_dev; 82582bbaa05SVadym Kochan if (netif_is_bridge_master(upper)) { 8262efc2256SYevhen Orlov if (cu_info->linking) 8272f5dc00fSVladimir Oltean return prestera_bridge_port_join(upper, port, 8282f5dc00fSVladimir Oltean extack); 82982bbaa05SVadym Kochan else 83082bbaa05SVadym Kochan prestera_bridge_port_leave(upper, port); 831255213caSSerhiy Boiko } else if (netif_is_lag_master(upper)) { 8322efc2256SYevhen Orlov if (cu_info->linking) 833255213caSSerhiy Boiko return prestera_lag_port_add(port, upper); 834255213caSSerhiy Boiko else 835255213caSSerhiy Boiko prestera_lag_port_del(port); 83682bbaa05SVadym Kochan } 8373d5048ccSVadym Kochan break; 838255213caSSerhiy Boiko 839255213caSSerhiy Boiko case NETDEV_CHANGELOWERSTATE: 840255213caSSerhiy Boiko return prestera_netdev_port_lower_event(dev, event, ptr); 841255213caSSerhiy Boiko } 842255213caSSerhiy Boiko 843255213caSSerhiy Boiko return 0; 844255213caSSerhiy Boiko } 845255213caSSerhiy Boiko 846255213caSSerhiy Boiko static int prestera_netdevice_lag_event(struct net_device *lag_dev, 847255213caSSerhiy Boiko unsigned long event, void *ptr) 848255213caSSerhiy Boiko { 849255213caSSerhiy Boiko struct net_device *dev; 850255213caSSerhiy Boiko struct list_head *iter; 851255213caSSerhiy Boiko int err; 852255213caSSerhiy Boiko 853255213caSSerhiy Boiko netdev_for_each_lower_dev(lag_dev, dev, iter) { 854255213caSSerhiy Boiko if (prestera_netdev_check(dev)) { 855255213caSSerhiy Boiko err = prestera_netdev_port_event(lag_dev, dev, event, 856255213caSSerhiy Boiko ptr); 857255213caSSerhiy Boiko if (err) 858255213caSSerhiy Boiko return err; 859255213caSSerhiy Boiko } 8603d5048ccSVadym Kochan } 8613d5048ccSVadym Kochan 8623d5048ccSVadym Kochan return 0; 863e1189d9aSVadym Kochan } 864e1189d9aSVadym Kochan 865e1189d9aSVadym Kochan static int prestera_netdev_event_handler(struct notifier_block *nb, 866e1189d9aSVadym Kochan unsigned long event, void *ptr) 867e1189d9aSVadym Kochan { 868e1189d9aSVadym Kochan struct net_device *dev = netdev_notifier_info_to_dev(ptr); 869e1189d9aSVadym Kochan int err = 0; 870e1189d9aSVadym Kochan 871e1189d9aSVadym Kochan if (prestera_netdev_check(dev)) 872255213caSSerhiy Boiko err = prestera_netdev_port_event(dev, dev, event, ptr); 873255213caSSerhiy Boiko else if (netif_is_lag_master(dev)) 874255213caSSerhiy Boiko err = prestera_netdevice_lag_event(dev, event, ptr); 875e1189d9aSVadym Kochan 876e1189d9aSVadym Kochan return notifier_from_errno(err); 877e1189d9aSVadym Kochan } 878e1189d9aSVadym Kochan 879e1189d9aSVadym Kochan static int prestera_netdev_event_handler_register(struct prestera_switch *sw) 880e1189d9aSVadym Kochan { 881e1189d9aSVadym Kochan sw->netdev_nb.notifier_call = prestera_netdev_event_handler; 882e1189d9aSVadym Kochan 883e1189d9aSVadym Kochan return register_netdevice_notifier(&sw->netdev_nb); 884e1189d9aSVadym Kochan } 885e1189d9aSVadym Kochan 886e1189d9aSVadym Kochan static void prestera_netdev_event_handler_unregister(struct prestera_switch *sw) 887e1189d9aSVadym Kochan { 888e1189d9aSVadym Kochan unregister_netdevice_notifier(&sw->netdev_nb); 889e1189d9aSVadym Kochan } 890e1189d9aSVadym Kochan 891501ef306SVadym Kochan static int prestera_switch_init(struct prestera_switch *sw) 892501ef306SVadym Kochan { 893501ef306SVadym Kochan int err; 894501ef306SVadym Kochan 895501ef306SVadym Kochan err = prestera_hw_switch_init(sw); 896501ef306SVadym Kochan if (err) { 897501ef306SVadym Kochan dev_err(prestera_dev(sw), "Failed to init Switch device\n"); 898501ef306SVadym Kochan return err; 899501ef306SVadym Kochan } 900501ef306SVadym Kochan 901501ef306SVadym Kochan rwlock_init(&sw->port_list_lock); 902501ef306SVadym Kochan INIT_LIST_HEAD(&sw->port_list); 903501ef306SVadym Kochan 904501ef306SVadym Kochan err = prestera_switch_set_base_mac_addr(sw); 905501ef306SVadym Kochan if (err) 906501ef306SVadym Kochan return err; 907501ef306SVadym Kochan 908e1189d9aSVadym Kochan err = prestera_netdev_event_handler_register(sw); 909501ef306SVadym Kochan if (err) 910501ef306SVadym Kochan return err; 911501ef306SVadym Kochan 91269204174SYevhen Orlov err = prestera_router_init(sw); 91369204174SYevhen Orlov if (err) 91469204174SYevhen Orlov goto err_router_init; 91569204174SYevhen Orlov 916e1189d9aSVadym Kochan err = prestera_switchdev_init(sw); 917e1189d9aSVadym Kochan if (err) 918e1189d9aSVadym Kochan goto err_swdev_register; 919e1189d9aSVadym Kochan 920e1189d9aSVadym Kochan err = prestera_rxtx_switch_init(sw); 921e1189d9aSVadym Kochan if (err) 922e1189d9aSVadym Kochan goto err_rxtx_register; 923e1189d9aSVadym Kochan 924501ef306SVadym Kochan err = prestera_event_handlers_register(sw); 925501ef306SVadym Kochan if (err) 926501ef306SVadym Kochan goto err_handlers_register; 927501ef306SVadym Kochan 9286e36c7bcSVolodymyr Mytnyk err = prestera_counter_init(sw); 9296e36c7bcSVolodymyr Mytnyk if (err) 9306e36c7bcSVolodymyr Mytnyk goto err_counter_init; 9316e36c7bcSVolodymyr Mytnyk 9328b474a9fSSerhiy Boiko err = prestera_acl_init(sw); 9338b474a9fSSerhiy Boiko if (err) 9348b474a9fSSerhiy Boiko goto err_acl_init; 9358b474a9fSSerhiy Boiko 93613defa27SSerhiy Boiko err = prestera_span_init(sw); 93713defa27SSerhiy Boiko if (err) 93813defa27SSerhiy Boiko goto err_span_init; 93913defa27SSerhiy Boiko 9404beb0c24SLeon Romanovsky err = prestera_devlink_traps_register(sw); 94134dd1710SVadym Kochan if (err) 94234dd1710SVadym Kochan goto err_dl_register; 94334dd1710SVadym Kochan 944255213caSSerhiy Boiko err = prestera_lag_init(sw); 945255213caSSerhiy Boiko if (err) 946255213caSSerhiy Boiko goto err_lag_init; 947255213caSSerhiy Boiko 948501ef306SVadym Kochan err = prestera_create_ports(sw); 949501ef306SVadym Kochan if (err) 950501ef306SVadym Kochan goto err_ports_create; 951501ef306SVadym Kochan 9524beb0c24SLeon Romanovsky prestera_devlink_register(sw); 953501ef306SVadym Kochan return 0; 954501ef306SVadym Kochan 955501ef306SVadym Kochan err_ports_create: 956255213caSSerhiy Boiko prestera_lag_fini(sw); 957255213caSSerhiy Boiko err_lag_init: 9584beb0c24SLeon Romanovsky prestera_devlink_traps_unregister(sw); 95934dd1710SVadym Kochan err_dl_register: 96013defa27SSerhiy Boiko prestera_span_fini(sw); 96113defa27SSerhiy Boiko err_span_init: 9628b474a9fSSerhiy Boiko prestera_acl_fini(sw); 9638b474a9fSSerhiy Boiko err_acl_init: 9646e36c7bcSVolodymyr Mytnyk prestera_counter_fini(sw); 9656e36c7bcSVolodymyr Mytnyk err_counter_init: 966501ef306SVadym Kochan prestera_event_handlers_unregister(sw); 967501ef306SVadym Kochan err_handlers_register: 968501ef306SVadym Kochan prestera_rxtx_switch_fini(sw); 969e1189d9aSVadym Kochan err_rxtx_register: 970e1189d9aSVadym Kochan prestera_switchdev_fini(sw); 971e1189d9aSVadym Kochan err_swdev_register: 97269204174SYevhen Orlov prestera_router_fini(sw); 97369204174SYevhen Orlov err_router_init: 974e1189d9aSVadym Kochan prestera_netdev_event_handler_unregister(sw); 975501ef306SVadym Kochan prestera_hw_switch_fini(sw); 976501ef306SVadym Kochan 977501ef306SVadym Kochan return err; 978501ef306SVadym Kochan } 979501ef306SVadym Kochan 980501ef306SVadym Kochan static void prestera_switch_fini(struct prestera_switch *sw) 981501ef306SVadym Kochan { 9824beb0c24SLeon Romanovsky prestera_devlink_unregister(sw); 983501ef306SVadym Kochan prestera_destroy_ports(sw); 984255213caSSerhiy Boiko prestera_lag_fini(sw); 9854beb0c24SLeon Romanovsky prestera_devlink_traps_unregister(sw); 98613defa27SSerhiy Boiko prestera_span_fini(sw); 9878b474a9fSSerhiy Boiko prestera_acl_fini(sw); 9886e36c7bcSVolodymyr Mytnyk prestera_counter_fini(sw); 989501ef306SVadym Kochan prestera_event_handlers_unregister(sw); 990501ef306SVadym Kochan prestera_rxtx_switch_fini(sw); 991e1189d9aSVadym Kochan prestera_switchdev_fini(sw); 992e179f045SYevhen Orlov prestera_router_fini(sw); 993e1189d9aSVadym Kochan prestera_netdev_event_handler_unregister(sw); 994501ef306SVadym Kochan prestera_hw_switch_fini(sw); 995501ef306SVadym Kochan } 996501ef306SVadym Kochan 997501ef306SVadym Kochan int prestera_device_register(struct prestera_device *dev) 998501ef306SVadym Kochan { 999501ef306SVadym Kochan struct prestera_switch *sw; 1000501ef306SVadym Kochan int err; 1001501ef306SVadym Kochan 1002919d13a7SLeon Romanovsky sw = prestera_devlink_alloc(dev); 1003501ef306SVadym Kochan if (!sw) 1004501ef306SVadym Kochan return -ENOMEM; 1005501ef306SVadym Kochan 1006501ef306SVadym Kochan dev->priv = sw; 1007501ef306SVadym Kochan sw->dev = dev; 1008501ef306SVadym Kochan 1009501ef306SVadym Kochan err = prestera_switch_init(sw); 1010501ef306SVadym Kochan if (err) { 101134dd1710SVadym Kochan prestera_devlink_free(sw); 1012501ef306SVadym Kochan return err; 1013501ef306SVadym Kochan } 1014501ef306SVadym Kochan 1015501ef306SVadym Kochan return 0; 1016501ef306SVadym Kochan } 1017501ef306SVadym Kochan EXPORT_SYMBOL(prestera_device_register); 1018501ef306SVadym Kochan 1019501ef306SVadym Kochan void prestera_device_unregister(struct prestera_device *dev) 1020501ef306SVadym Kochan { 1021501ef306SVadym Kochan struct prestera_switch *sw = dev->priv; 1022501ef306SVadym Kochan 1023501ef306SVadym Kochan prestera_switch_fini(sw); 102434dd1710SVadym Kochan prestera_devlink_free(sw); 1025501ef306SVadym Kochan } 1026501ef306SVadym Kochan EXPORT_SYMBOL(prestera_device_unregister); 1027501ef306SVadym Kochan 1028501ef306SVadym Kochan static int __init prestera_module_init(void) 1029501ef306SVadym Kochan { 1030501ef306SVadym Kochan prestera_wq = alloc_workqueue("prestera", 0, 0); 1031501ef306SVadym Kochan if (!prestera_wq) 1032501ef306SVadym Kochan return -ENOMEM; 1033501ef306SVadym Kochan 10344394fbcbSYevhen Orlov prestera_owq = alloc_ordered_workqueue("prestera_ordered", 0); 1035*4a6806cfSYang Yingliang if (!prestera_owq) { 1036*4a6806cfSYang Yingliang destroy_workqueue(prestera_wq); 10374394fbcbSYevhen Orlov return -ENOMEM; 1038*4a6806cfSYang Yingliang } 10394394fbcbSYevhen Orlov 1040501ef306SVadym Kochan return 0; 1041501ef306SVadym Kochan } 1042501ef306SVadym Kochan 1043501ef306SVadym Kochan static void __exit prestera_module_exit(void) 1044501ef306SVadym Kochan { 1045501ef306SVadym Kochan destroy_workqueue(prestera_wq); 10464394fbcbSYevhen Orlov destroy_workqueue(prestera_owq); 1047501ef306SVadym Kochan } 1048501ef306SVadym Kochan 1049501ef306SVadym Kochan module_init(prestera_module_init); 1050501ef306SVadym Kochan module_exit(prestera_module_exit); 1051501ef306SVadym Kochan 1052501ef306SVadym Kochan MODULE_LICENSE("Dual BSD/GPL"); 1053501ef306SVadym Kochan MODULE_DESCRIPTION("Marvell Prestera switch driver"); 1054