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" 21e1189d9aSVadym Kochan #include "prestera_switchdev.h" 22501ef306SVadym Kochan 23501ef306SVadym Kochan #define PRESTERA_MTU_DEFAULT 1536 24501ef306SVadym Kochan 25501ef306SVadym Kochan #define PRESTERA_STATS_DELAY_MS 1000 26501ef306SVadym Kochan 27501ef306SVadym Kochan #define PRESTERA_MAC_ADDR_NUM_MAX 255 28501ef306SVadym Kochan 29501ef306SVadym Kochan static struct workqueue_struct *prestera_wq; 30501ef306SVadym Kochan 31e1189d9aSVadym Kochan int prestera_port_pvid_set(struct prestera_port *port, u16 vid) 32e1189d9aSVadym Kochan { 33e1189d9aSVadym Kochan enum prestera_accept_frm_type frm_type; 34e1189d9aSVadym Kochan int err; 35e1189d9aSVadym Kochan 36e1189d9aSVadym Kochan frm_type = PRESTERA_ACCEPT_FRAME_TYPE_TAGGED; 37e1189d9aSVadym Kochan 38e1189d9aSVadym Kochan if (vid) { 39e1189d9aSVadym Kochan err = prestera_hw_vlan_port_vid_set(port, vid); 40e1189d9aSVadym Kochan if (err) 41e1189d9aSVadym Kochan return err; 42e1189d9aSVadym Kochan 43e1189d9aSVadym Kochan frm_type = PRESTERA_ACCEPT_FRAME_TYPE_ALL; 44e1189d9aSVadym Kochan } 45e1189d9aSVadym Kochan 46e1189d9aSVadym Kochan err = prestera_hw_port_accept_frm_type(port, frm_type); 47e1189d9aSVadym Kochan if (err && frm_type == PRESTERA_ACCEPT_FRAME_TYPE_ALL) 48e1189d9aSVadym Kochan prestera_hw_vlan_port_vid_set(port, port->pvid); 49e1189d9aSVadym Kochan 50e1189d9aSVadym Kochan port->pvid = vid; 51e1189d9aSVadym Kochan return 0; 52e1189d9aSVadym Kochan } 53e1189d9aSVadym Kochan 54501ef306SVadym Kochan struct prestera_port *prestera_port_find_by_hwid(struct prestera_switch *sw, 55501ef306SVadym Kochan u32 dev_id, u32 hw_id) 56501ef306SVadym Kochan { 57501ef306SVadym Kochan struct prestera_port *port = NULL; 58501ef306SVadym Kochan 59501ef306SVadym Kochan read_lock(&sw->port_list_lock); 60501ef306SVadym Kochan list_for_each_entry(port, &sw->port_list, list) { 61501ef306SVadym Kochan if (port->dev_id == dev_id && port->hw_id == hw_id) 62501ef306SVadym Kochan break; 63501ef306SVadym Kochan } 64501ef306SVadym Kochan read_unlock(&sw->port_list_lock); 65501ef306SVadym Kochan 66501ef306SVadym Kochan return port; 67501ef306SVadym Kochan } 68501ef306SVadym Kochan 69e1189d9aSVadym Kochan struct prestera_port *prestera_find_port(struct prestera_switch *sw, u32 id) 70501ef306SVadym Kochan { 71501ef306SVadym Kochan struct prestera_port *port = NULL; 72501ef306SVadym Kochan 73501ef306SVadym Kochan read_lock(&sw->port_list_lock); 74501ef306SVadym Kochan list_for_each_entry(port, &sw->port_list, list) { 75501ef306SVadym Kochan if (port->id == id) 76501ef306SVadym Kochan break; 77501ef306SVadym Kochan } 78501ef306SVadym Kochan read_unlock(&sw->port_list_lock); 79501ef306SVadym Kochan 80501ef306SVadym Kochan return port; 81501ef306SVadym Kochan } 82501ef306SVadym Kochan 83501ef306SVadym Kochan static int prestera_port_open(struct net_device *dev) 84501ef306SVadym Kochan { 85501ef306SVadym Kochan struct prestera_port *port = netdev_priv(dev); 86501ef306SVadym Kochan int err; 87501ef306SVadym Kochan 88501ef306SVadym Kochan err = prestera_hw_port_state_set(port, true); 89501ef306SVadym Kochan if (err) 90501ef306SVadym Kochan return err; 91501ef306SVadym Kochan 92501ef306SVadym Kochan netif_start_queue(dev); 93501ef306SVadym Kochan 94501ef306SVadym Kochan return 0; 95501ef306SVadym Kochan } 96501ef306SVadym Kochan 97501ef306SVadym Kochan static int prestera_port_close(struct net_device *dev) 98501ef306SVadym Kochan { 99501ef306SVadym Kochan struct prestera_port *port = netdev_priv(dev); 100501ef306SVadym Kochan 101501ef306SVadym Kochan netif_stop_queue(dev); 102501ef306SVadym Kochan 10305372c45SZheng Yongjun return prestera_hw_port_state_set(port, false); 104501ef306SVadym Kochan } 105501ef306SVadym Kochan 106501ef306SVadym Kochan static netdev_tx_t prestera_port_xmit(struct sk_buff *skb, 107501ef306SVadym Kochan struct net_device *dev) 108501ef306SVadym Kochan { 109501ef306SVadym Kochan return prestera_rxtx_xmit(netdev_priv(dev), skb); 110501ef306SVadym Kochan } 111501ef306SVadym Kochan 112501ef306SVadym Kochan static int prestera_is_valid_mac_addr(struct prestera_port *port, u8 *addr) 113501ef306SVadym Kochan { 114501ef306SVadym Kochan if (!is_valid_ether_addr(addr)) 115501ef306SVadym Kochan return -EADDRNOTAVAIL; 116501ef306SVadym Kochan 117501ef306SVadym Kochan /* firmware requires that port's MAC address contains first 5 bytes 118501ef306SVadym Kochan * of the base MAC address 119501ef306SVadym Kochan */ 120501ef306SVadym Kochan if (memcmp(port->sw->base_mac, addr, ETH_ALEN - 1)) 121501ef306SVadym Kochan return -EINVAL; 122501ef306SVadym Kochan 123501ef306SVadym Kochan return 0; 124501ef306SVadym Kochan } 125501ef306SVadym Kochan 126501ef306SVadym Kochan static int prestera_port_set_mac_address(struct net_device *dev, void *p) 127501ef306SVadym Kochan { 128501ef306SVadym Kochan struct prestera_port *port = netdev_priv(dev); 129501ef306SVadym Kochan struct sockaddr *addr = p; 130501ef306SVadym Kochan int err; 131501ef306SVadym Kochan 132501ef306SVadym Kochan err = prestera_is_valid_mac_addr(port, addr->sa_data); 133501ef306SVadym Kochan if (err) 134501ef306SVadym Kochan return err; 135501ef306SVadym Kochan 136501ef306SVadym Kochan err = prestera_hw_port_mac_set(port, addr->sa_data); 137501ef306SVadym Kochan if (err) 138501ef306SVadym Kochan return err; 139501ef306SVadym Kochan 140*f3956ebbSJakub Kicinski eth_hw_addr_set(dev, addr->sa_data); 141501ef306SVadym Kochan 142501ef306SVadym Kochan return 0; 143501ef306SVadym Kochan } 144501ef306SVadym Kochan 145501ef306SVadym Kochan static int prestera_port_change_mtu(struct net_device *dev, int mtu) 146501ef306SVadym Kochan { 147501ef306SVadym Kochan struct prestera_port *port = netdev_priv(dev); 148501ef306SVadym Kochan int err; 149501ef306SVadym Kochan 150501ef306SVadym Kochan err = prestera_hw_port_mtu_set(port, mtu); 151501ef306SVadym Kochan if (err) 152501ef306SVadym Kochan return err; 153501ef306SVadym Kochan 154501ef306SVadym Kochan dev->mtu = mtu; 155501ef306SVadym Kochan 156501ef306SVadym Kochan return 0; 157501ef306SVadym Kochan } 158501ef306SVadym Kochan 159501ef306SVadym Kochan static void prestera_port_get_stats64(struct net_device *dev, 160501ef306SVadym Kochan struct rtnl_link_stats64 *stats) 161501ef306SVadym Kochan { 162501ef306SVadym Kochan struct prestera_port *port = netdev_priv(dev); 163501ef306SVadym Kochan struct prestera_port_stats *port_stats = &port->cached_hw_stats.stats; 164501ef306SVadym Kochan 165501ef306SVadym Kochan stats->rx_packets = port_stats->broadcast_frames_received + 166501ef306SVadym Kochan port_stats->multicast_frames_received + 167501ef306SVadym Kochan port_stats->unicast_frames_received; 168501ef306SVadym Kochan 169501ef306SVadym Kochan stats->tx_packets = port_stats->broadcast_frames_sent + 170501ef306SVadym Kochan port_stats->multicast_frames_sent + 171501ef306SVadym Kochan port_stats->unicast_frames_sent; 172501ef306SVadym Kochan 173501ef306SVadym Kochan stats->rx_bytes = port_stats->good_octets_received; 174501ef306SVadym Kochan 175501ef306SVadym Kochan stats->tx_bytes = port_stats->good_octets_sent; 176501ef306SVadym Kochan 177501ef306SVadym Kochan stats->rx_errors = port_stats->rx_error_frame_received; 178501ef306SVadym Kochan stats->tx_errors = port_stats->mac_trans_error; 179501ef306SVadym Kochan 180501ef306SVadym Kochan stats->rx_dropped = port_stats->buffer_overrun; 181501ef306SVadym Kochan stats->tx_dropped = 0; 182501ef306SVadym Kochan 183501ef306SVadym Kochan stats->multicast = port_stats->multicast_frames_received; 184501ef306SVadym Kochan stats->collisions = port_stats->excessive_collision; 185501ef306SVadym Kochan 186501ef306SVadym Kochan stats->rx_crc_errors = port_stats->bad_crc; 187501ef306SVadym Kochan } 188501ef306SVadym Kochan 189501ef306SVadym Kochan static void prestera_port_get_hw_stats(struct prestera_port *port) 190501ef306SVadym Kochan { 191501ef306SVadym Kochan prestera_hw_port_stats_get(port, &port->cached_hw_stats.stats); 192501ef306SVadym Kochan } 193501ef306SVadym Kochan 194501ef306SVadym Kochan static void prestera_port_stats_update(struct work_struct *work) 195501ef306SVadym Kochan { 196501ef306SVadym Kochan struct prestera_port *port = 197501ef306SVadym Kochan container_of(work, struct prestera_port, 198501ef306SVadym Kochan cached_hw_stats.caching_dw.work); 199501ef306SVadym Kochan 200501ef306SVadym Kochan prestera_port_get_hw_stats(port); 201501ef306SVadym Kochan 202501ef306SVadym Kochan queue_delayed_work(prestera_wq, &port->cached_hw_stats.caching_dw, 203501ef306SVadym Kochan msecs_to_jiffies(PRESTERA_STATS_DELAY_MS)); 204501ef306SVadym Kochan } 205501ef306SVadym Kochan 2068b474a9fSSerhiy Boiko static int prestera_port_setup_tc(struct net_device *dev, 2078b474a9fSSerhiy Boiko enum tc_setup_type type, 2088b474a9fSSerhiy Boiko void *type_data) 2098b474a9fSSerhiy Boiko { 2108b474a9fSSerhiy Boiko struct prestera_port *port = netdev_priv(dev); 2118b474a9fSSerhiy Boiko 2128b474a9fSSerhiy Boiko switch (type) { 2138b474a9fSSerhiy Boiko case TC_SETUP_BLOCK: 2148b474a9fSSerhiy Boiko return prestera_flow_block_setup(port, type_data); 2158b474a9fSSerhiy Boiko default: 2168b474a9fSSerhiy Boiko return -EOPNOTSUPP; 2178b474a9fSSerhiy Boiko } 2188b474a9fSSerhiy Boiko } 2198b474a9fSSerhiy Boiko 220501ef306SVadym Kochan static const struct net_device_ops prestera_netdev_ops = { 221501ef306SVadym Kochan .ndo_open = prestera_port_open, 222501ef306SVadym Kochan .ndo_stop = prestera_port_close, 223501ef306SVadym Kochan .ndo_start_xmit = prestera_port_xmit, 2248b474a9fSSerhiy Boiko .ndo_setup_tc = prestera_port_setup_tc, 225501ef306SVadym Kochan .ndo_change_mtu = prestera_port_change_mtu, 226501ef306SVadym Kochan .ndo_get_stats64 = prestera_port_get_stats64, 227501ef306SVadym Kochan .ndo_set_mac_address = prestera_port_set_mac_address, 22834dd1710SVadym Kochan .ndo_get_devlink_port = prestera_devlink_get_port, 229501ef306SVadym Kochan }; 230501ef306SVadym Kochan 231a97d3c69SVadym Kochan int prestera_port_autoneg_set(struct prestera_port *port, bool enable, 232a97d3c69SVadym Kochan u64 adver_link_modes, u8 adver_fec) 233501ef306SVadym Kochan { 234501ef306SVadym Kochan bool refresh = false; 235a97d3c69SVadym Kochan u64 link_modes; 236501ef306SVadym Kochan int err; 237a97d3c69SVadym Kochan u8 fec; 238501ef306SVadym Kochan 239501ef306SVadym Kochan if (port->caps.type != PRESTERA_PORT_TYPE_TP) 240501ef306SVadym Kochan return enable ? -EINVAL : 0; 241501ef306SVadym Kochan 242a97d3c69SVadym Kochan if (!enable) 243a97d3c69SVadym Kochan goto set_autoneg; 244a97d3c69SVadym Kochan 245a97d3c69SVadym Kochan link_modes = port->caps.supp_link_modes & adver_link_modes; 246a97d3c69SVadym Kochan fec = port->caps.supp_fec & adver_fec; 247a97d3c69SVadym Kochan 248a97d3c69SVadym Kochan if (!link_modes && !fec) 249a97d3c69SVadym Kochan return -EOPNOTSUPP; 250a97d3c69SVadym Kochan 251a97d3c69SVadym Kochan if (link_modes && port->adver_link_modes != link_modes) { 252501ef306SVadym Kochan port->adver_link_modes = link_modes; 253501ef306SVadym Kochan refresh = true; 254501ef306SVadym Kochan } 255501ef306SVadym Kochan 256a97d3c69SVadym Kochan if (fec && port->adver_fec != fec) { 257a97d3c69SVadym Kochan port->adver_fec = fec; 258a97d3c69SVadym Kochan refresh = true; 259a97d3c69SVadym Kochan } 260a97d3c69SVadym Kochan 261a97d3c69SVadym Kochan set_autoneg: 262a97d3c69SVadym Kochan if (port->autoneg == enable && !refresh) 263501ef306SVadym Kochan return 0; 264501ef306SVadym Kochan 265501ef306SVadym Kochan err = prestera_hw_port_autoneg_set(port, enable, port->adver_link_modes, 266501ef306SVadym Kochan port->adver_fec); 267501ef306SVadym Kochan if (err) 268501ef306SVadym Kochan return err; 269501ef306SVadym Kochan 270501ef306SVadym Kochan port->autoneg = enable; 271501ef306SVadym Kochan 272501ef306SVadym Kochan return 0; 273501ef306SVadym Kochan } 274501ef306SVadym Kochan 275501ef306SVadym Kochan static void prestera_port_list_add(struct prestera_port *port) 276501ef306SVadym Kochan { 277501ef306SVadym Kochan write_lock(&port->sw->port_list_lock); 278501ef306SVadym Kochan list_add(&port->list, &port->sw->port_list); 279501ef306SVadym Kochan write_unlock(&port->sw->port_list_lock); 280501ef306SVadym Kochan } 281501ef306SVadym Kochan 282501ef306SVadym Kochan static void prestera_port_list_del(struct prestera_port *port) 283501ef306SVadym Kochan { 284501ef306SVadym Kochan write_lock(&port->sw->port_list_lock); 285501ef306SVadym Kochan list_del(&port->list); 286501ef306SVadym Kochan write_unlock(&port->sw->port_list_lock); 287501ef306SVadym Kochan } 288501ef306SVadym Kochan 289501ef306SVadym Kochan static int prestera_port_create(struct prestera_switch *sw, u32 id) 290501ef306SVadym Kochan { 291501ef306SVadym Kochan struct prestera_port *port; 292501ef306SVadym Kochan struct net_device *dev; 293501ef306SVadym Kochan int err; 294501ef306SVadym Kochan 295501ef306SVadym Kochan dev = alloc_etherdev(sizeof(*port)); 296501ef306SVadym Kochan if (!dev) 297501ef306SVadym Kochan return -ENOMEM; 298501ef306SVadym Kochan 299501ef306SVadym Kochan port = netdev_priv(dev); 300501ef306SVadym Kochan 301e1189d9aSVadym Kochan INIT_LIST_HEAD(&port->vlans_list); 302e1189d9aSVadym Kochan port->pvid = PRESTERA_DEFAULT_VID; 303255213caSSerhiy Boiko port->lag = NULL; 304501ef306SVadym Kochan port->dev = dev; 305501ef306SVadym Kochan port->id = id; 306501ef306SVadym Kochan port->sw = sw; 307501ef306SVadym Kochan 308501ef306SVadym Kochan err = prestera_hw_port_info_get(port, &port->dev_id, &port->hw_id, 309501ef306SVadym Kochan &port->fp_id); 310501ef306SVadym Kochan if (err) { 311501ef306SVadym Kochan dev_err(prestera_dev(sw), "Failed to get port(%u) info\n", id); 31234dd1710SVadym Kochan goto err_port_info_get; 313501ef306SVadym Kochan } 314501ef306SVadym Kochan 31534dd1710SVadym Kochan err = prestera_devlink_port_register(port); 31634dd1710SVadym Kochan if (err) 31734dd1710SVadym Kochan goto err_dl_port_register; 31834dd1710SVadym Kochan 3198b474a9fSSerhiy Boiko dev->features |= NETIF_F_NETNS_LOCAL | NETIF_F_HW_TC; 320501ef306SVadym Kochan dev->netdev_ops = &prestera_netdev_ops; 321a97d3c69SVadym Kochan dev->ethtool_ops = &prestera_ethtool_ops; 322501ef306SVadym Kochan 323501ef306SVadym Kochan netif_carrier_off(dev); 324501ef306SVadym Kochan 325501ef306SVadym Kochan dev->mtu = min_t(unsigned int, sw->mtu_max, PRESTERA_MTU_DEFAULT); 326501ef306SVadym Kochan dev->min_mtu = sw->mtu_min; 327501ef306SVadym Kochan dev->max_mtu = sw->mtu_max; 328501ef306SVadym Kochan 329501ef306SVadym Kochan err = prestera_hw_port_mtu_set(port, dev->mtu); 330501ef306SVadym Kochan if (err) { 331501ef306SVadym Kochan dev_err(prestera_dev(sw), "Failed to set port(%u) mtu(%d)\n", 332501ef306SVadym Kochan id, dev->mtu); 333501ef306SVadym Kochan goto err_port_init; 334501ef306SVadym Kochan } 335501ef306SVadym Kochan 3364de377b6SZhang Changzhong if (port->fp_id >= PRESTERA_MAC_ADDR_NUM_MAX) { 3374de377b6SZhang Changzhong err = -EINVAL; 338501ef306SVadym Kochan goto err_port_init; 3394de377b6SZhang Changzhong } 340501ef306SVadym Kochan 341501ef306SVadym Kochan /* firmware requires that port's MAC address consist of the first 342501ef306SVadym Kochan * 5 bytes of the base MAC address 343501ef306SVadym Kochan */ 344501ef306SVadym Kochan memcpy(dev->dev_addr, sw->base_mac, dev->addr_len - 1); 345501ef306SVadym Kochan dev->dev_addr[dev->addr_len - 1] = port->fp_id; 346501ef306SVadym Kochan 347501ef306SVadym Kochan err = prestera_hw_port_mac_set(port, dev->dev_addr); 348501ef306SVadym Kochan if (err) { 349501ef306SVadym Kochan dev_err(prestera_dev(sw), "Failed to set port(%u) mac addr\n", id); 350501ef306SVadym Kochan goto err_port_init; 351501ef306SVadym Kochan } 352501ef306SVadym Kochan 353501ef306SVadym Kochan err = prestera_hw_port_cap_get(port, &port->caps); 354501ef306SVadym Kochan if (err) { 355501ef306SVadym Kochan dev_err(prestera_dev(sw), "Failed to get port(%u) caps\n", id); 356501ef306SVadym Kochan goto err_port_init; 357501ef306SVadym Kochan } 358501ef306SVadym Kochan 359501ef306SVadym Kochan port->adver_fec = BIT(PRESTERA_PORT_FEC_OFF); 360501ef306SVadym Kochan prestera_port_autoneg_set(port, true, port->caps.supp_link_modes, 361501ef306SVadym Kochan port->caps.supp_fec); 362501ef306SVadym Kochan 363501ef306SVadym Kochan err = prestera_hw_port_state_set(port, false); 364501ef306SVadym Kochan if (err) { 365501ef306SVadym Kochan dev_err(prestera_dev(sw), "Failed to set port(%u) down\n", id); 366501ef306SVadym Kochan goto err_port_init; 367501ef306SVadym Kochan } 368501ef306SVadym Kochan 369501ef306SVadym Kochan err = prestera_rxtx_port_init(port); 370501ef306SVadym Kochan if (err) 371501ef306SVadym Kochan goto err_port_init; 372501ef306SVadym Kochan 373501ef306SVadym Kochan INIT_DELAYED_WORK(&port->cached_hw_stats.caching_dw, 374501ef306SVadym Kochan &prestera_port_stats_update); 375501ef306SVadym Kochan 376501ef306SVadym Kochan prestera_port_list_add(port); 377501ef306SVadym Kochan 378501ef306SVadym Kochan err = register_netdev(dev); 379501ef306SVadym Kochan if (err) 380501ef306SVadym Kochan goto err_register_netdev; 381501ef306SVadym Kochan 38234dd1710SVadym Kochan prestera_devlink_port_set(port); 38334dd1710SVadym Kochan 384501ef306SVadym Kochan return 0; 385501ef306SVadym Kochan 386501ef306SVadym Kochan err_register_netdev: 387501ef306SVadym Kochan prestera_port_list_del(port); 388501ef306SVadym Kochan err_port_init: 38934dd1710SVadym Kochan prestera_devlink_port_unregister(port); 39034dd1710SVadym Kochan err_dl_port_register: 39134dd1710SVadym Kochan err_port_info_get: 392501ef306SVadym Kochan free_netdev(dev); 393501ef306SVadym Kochan return err; 394501ef306SVadym Kochan } 395501ef306SVadym Kochan 396501ef306SVadym Kochan static void prestera_port_destroy(struct prestera_port *port) 397501ef306SVadym Kochan { 398501ef306SVadym Kochan struct net_device *dev = port->dev; 399501ef306SVadym Kochan 400501ef306SVadym Kochan cancel_delayed_work_sync(&port->cached_hw_stats.caching_dw); 40134dd1710SVadym Kochan prestera_devlink_port_clear(port); 402501ef306SVadym Kochan unregister_netdev(dev); 403501ef306SVadym Kochan prestera_port_list_del(port); 40434dd1710SVadym Kochan prestera_devlink_port_unregister(port); 405501ef306SVadym Kochan free_netdev(dev); 406501ef306SVadym Kochan } 407501ef306SVadym Kochan 408501ef306SVadym Kochan static void prestera_destroy_ports(struct prestera_switch *sw) 409501ef306SVadym Kochan { 410501ef306SVadym Kochan struct prestera_port *port, *tmp; 411501ef306SVadym Kochan 412501ef306SVadym Kochan list_for_each_entry_safe(port, tmp, &sw->port_list, list) 413501ef306SVadym Kochan prestera_port_destroy(port); 414501ef306SVadym Kochan } 415501ef306SVadym Kochan 416501ef306SVadym Kochan static int prestera_create_ports(struct prestera_switch *sw) 417501ef306SVadym Kochan { 418501ef306SVadym Kochan struct prestera_port *port, *tmp; 419501ef306SVadym Kochan u32 port_idx; 420501ef306SVadym Kochan int err; 421501ef306SVadym Kochan 422501ef306SVadym Kochan for (port_idx = 0; port_idx < sw->port_count; port_idx++) { 423501ef306SVadym Kochan err = prestera_port_create(sw, port_idx); 424501ef306SVadym Kochan if (err) 425501ef306SVadym Kochan goto err_port_create; 426501ef306SVadym Kochan } 427501ef306SVadym Kochan 428501ef306SVadym Kochan return 0; 429501ef306SVadym Kochan 430501ef306SVadym Kochan err_port_create: 431501ef306SVadym Kochan list_for_each_entry_safe(port, tmp, &sw->port_list, list) 432501ef306SVadym Kochan prestera_port_destroy(port); 433501ef306SVadym Kochan 434501ef306SVadym Kochan return err; 435501ef306SVadym Kochan } 436501ef306SVadym Kochan 437501ef306SVadym Kochan static void prestera_port_handle_event(struct prestera_switch *sw, 438501ef306SVadym Kochan struct prestera_event *evt, void *arg) 439501ef306SVadym Kochan { 440501ef306SVadym Kochan struct delayed_work *caching_dw; 441501ef306SVadym Kochan struct prestera_port *port; 442501ef306SVadym Kochan 443501ef306SVadym Kochan port = prestera_find_port(sw, evt->port_evt.port_id); 444501ef306SVadym Kochan if (!port || !port->dev) 445501ef306SVadym Kochan return; 446501ef306SVadym Kochan 447501ef306SVadym Kochan caching_dw = &port->cached_hw_stats.caching_dw; 448501ef306SVadym Kochan 449501ef306SVadym Kochan if (evt->id == PRESTERA_PORT_EVENT_STATE_CHANGED) { 450501ef306SVadym Kochan if (evt->port_evt.data.oper_state) { 451501ef306SVadym Kochan netif_carrier_on(port->dev); 452501ef306SVadym Kochan if (!delayed_work_pending(caching_dw)) 453501ef306SVadym Kochan queue_delayed_work(prestera_wq, caching_dw, 0); 45433398048SVadym Kochan } else if (netif_running(port->dev) && 45533398048SVadym Kochan netif_carrier_ok(port->dev)) { 456501ef306SVadym Kochan netif_carrier_off(port->dev); 457501ef306SVadym Kochan if (delayed_work_pending(caching_dw)) 458501ef306SVadym Kochan cancel_delayed_work(caching_dw); 459501ef306SVadym Kochan } 460501ef306SVadym Kochan } 461501ef306SVadym Kochan } 462501ef306SVadym Kochan 463501ef306SVadym Kochan static int prestera_event_handlers_register(struct prestera_switch *sw) 464501ef306SVadym Kochan { 465501ef306SVadym Kochan return prestera_hw_event_handler_register(sw, PRESTERA_EVENT_TYPE_PORT, 466501ef306SVadym Kochan prestera_port_handle_event, 467501ef306SVadym Kochan NULL); 468501ef306SVadym Kochan } 469501ef306SVadym Kochan 470501ef306SVadym Kochan static void prestera_event_handlers_unregister(struct prestera_switch *sw) 471501ef306SVadym Kochan { 472501ef306SVadym Kochan prestera_hw_event_handler_unregister(sw, PRESTERA_EVENT_TYPE_PORT, 473501ef306SVadym Kochan prestera_port_handle_event); 474501ef306SVadym Kochan } 475501ef306SVadym Kochan 476501ef306SVadym Kochan static int prestera_switch_set_base_mac_addr(struct prestera_switch *sw) 477501ef306SVadym Kochan { 478501ef306SVadym Kochan struct device_node *base_mac_np; 479501ef306SVadym Kochan struct device_node *np; 48083216e39SMichael Walle int ret; 481501ef306SVadym Kochan 482501ef306SVadym Kochan np = of_find_compatible_node(NULL, NULL, "marvell,prestera"); 483501ef306SVadym Kochan base_mac_np = of_parse_phandle(np, "base-mac-provider", 0); 484501ef306SVadym Kochan 48583216e39SMichael Walle ret = of_get_mac_address(base_mac_np, sw->base_mac); 48683216e39SMichael Walle if (ret) { 487501ef306SVadym Kochan eth_random_addr(sw->base_mac); 488501ef306SVadym Kochan dev_info(prestera_dev(sw), "using random base mac address\n"); 489501ef306SVadym Kochan } 49083216e39SMichael Walle of_node_put(base_mac_np); 491501ef306SVadym Kochan 492501ef306SVadym Kochan return prestera_hw_switch_mac_set(sw, sw->base_mac); 493501ef306SVadym Kochan } 494501ef306SVadym Kochan 495255213caSSerhiy Boiko struct prestera_lag *prestera_lag_by_id(struct prestera_switch *sw, u16 id) 496255213caSSerhiy Boiko { 497255213caSSerhiy Boiko return id < sw->lag_max ? &sw->lags[id] : NULL; 498255213caSSerhiy Boiko } 499255213caSSerhiy Boiko 500255213caSSerhiy Boiko static struct prestera_lag *prestera_lag_by_dev(struct prestera_switch *sw, 501255213caSSerhiy Boiko struct net_device *dev) 502255213caSSerhiy Boiko { 503255213caSSerhiy Boiko struct prestera_lag *lag; 504255213caSSerhiy Boiko u16 id; 505255213caSSerhiy Boiko 506255213caSSerhiy Boiko for (id = 0; id < sw->lag_max; id++) { 507255213caSSerhiy Boiko lag = &sw->lags[id]; 508255213caSSerhiy Boiko if (lag->dev == dev) 509255213caSSerhiy Boiko return lag; 510255213caSSerhiy Boiko } 511255213caSSerhiy Boiko 512255213caSSerhiy Boiko return NULL; 513255213caSSerhiy Boiko } 514255213caSSerhiy Boiko 515255213caSSerhiy Boiko static struct prestera_lag *prestera_lag_create(struct prestera_switch *sw, 516255213caSSerhiy Boiko struct net_device *lag_dev) 517255213caSSerhiy Boiko { 518255213caSSerhiy Boiko struct prestera_lag *lag = NULL; 519255213caSSerhiy Boiko u16 id; 520255213caSSerhiy Boiko 521255213caSSerhiy Boiko for (id = 0; id < sw->lag_max; id++) { 522255213caSSerhiy Boiko lag = &sw->lags[id]; 523255213caSSerhiy Boiko if (!lag->dev) 524255213caSSerhiy Boiko break; 525255213caSSerhiy Boiko } 526255213caSSerhiy Boiko if (lag) { 527255213caSSerhiy Boiko INIT_LIST_HEAD(&lag->members); 528255213caSSerhiy Boiko lag->dev = lag_dev; 529255213caSSerhiy Boiko } 530255213caSSerhiy Boiko 531255213caSSerhiy Boiko return lag; 532255213caSSerhiy Boiko } 533255213caSSerhiy Boiko 534255213caSSerhiy Boiko static void prestera_lag_destroy(struct prestera_switch *sw, 535255213caSSerhiy Boiko struct prestera_lag *lag) 536255213caSSerhiy Boiko { 537255213caSSerhiy Boiko WARN_ON(!list_empty(&lag->members)); 538255213caSSerhiy Boiko lag->member_count = 0; 539255213caSSerhiy Boiko lag->dev = NULL; 540255213caSSerhiy Boiko } 541255213caSSerhiy Boiko 542255213caSSerhiy Boiko static int prestera_lag_port_add(struct prestera_port *port, 543255213caSSerhiy Boiko struct net_device *lag_dev) 544255213caSSerhiy Boiko { 545255213caSSerhiy Boiko struct prestera_switch *sw = port->sw; 546255213caSSerhiy Boiko struct prestera_lag *lag; 547255213caSSerhiy Boiko int err; 548255213caSSerhiy Boiko 549255213caSSerhiy Boiko lag = prestera_lag_by_dev(sw, lag_dev); 550255213caSSerhiy Boiko if (!lag) { 551255213caSSerhiy Boiko lag = prestera_lag_create(sw, lag_dev); 552255213caSSerhiy Boiko if (!lag) 553255213caSSerhiy Boiko return -ENOSPC; 554255213caSSerhiy Boiko } 555255213caSSerhiy Boiko 556255213caSSerhiy Boiko if (lag->member_count >= sw->lag_member_max) 557255213caSSerhiy Boiko return -ENOSPC; 558255213caSSerhiy Boiko 559255213caSSerhiy Boiko err = prestera_hw_lag_member_add(port, lag->lag_id); 560255213caSSerhiy Boiko if (err) { 561255213caSSerhiy Boiko if (!lag->member_count) 562255213caSSerhiy Boiko prestera_lag_destroy(sw, lag); 563255213caSSerhiy Boiko return err; 564255213caSSerhiy Boiko } 565255213caSSerhiy Boiko 566255213caSSerhiy Boiko list_add(&port->lag_member, &lag->members); 567255213caSSerhiy Boiko lag->member_count++; 568255213caSSerhiy Boiko port->lag = lag; 569255213caSSerhiy Boiko 570255213caSSerhiy Boiko return 0; 571255213caSSerhiy Boiko } 572255213caSSerhiy Boiko 573255213caSSerhiy Boiko static int prestera_lag_port_del(struct prestera_port *port) 574255213caSSerhiy Boiko { 575255213caSSerhiy Boiko struct prestera_switch *sw = port->sw; 576255213caSSerhiy Boiko struct prestera_lag *lag = port->lag; 577255213caSSerhiy Boiko int err; 578255213caSSerhiy Boiko 579255213caSSerhiy Boiko if (!lag || !lag->member_count) 580255213caSSerhiy Boiko return -EINVAL; 581255213caSSerhiy Boiko 582255213caSSerhiy Boiko err = prestera_hw_lag_member_del(port, lag->lag_id); 583255213caSSerhiy Boiko if (err) 584255213caSSerhiy Boiko return err; 585255213caSSerhiy Boiko 586255213caSSerhiy Boiko list_del(&port->lag_member); 587255213caSSerhiy Boiko lag->member_count--; 588255213caSSerhiy Boiko port->lag = NULL; 589255213caSSerhiy Boiko 590255213caSSerhiy Boiko if (netif_is_bridge_port(lag->dev)) { 591255213caSSerhiy Boiko struct net_device *br_dev; 592255213caSSerhiy Boiko 593255213caSSerhiy Boiko br_dev = netdev_master_upper_dev_get(lag->dev); 594255213caSSerhiy Boiko 595255213caSSerhiy Boiko prestera_bridge_port_leave(br_dev, port); 596255213caSSerhiy Boiko } 597255213caSSerhiy Boiko 598255213caSSerhiy Boiko if (!lag->member_count) 599255213caSSerhiy Boiko prestera_lag_destroy(sw, lag); 600255213caSSerhiy Boiko 601255213caSSerhiy Boiko return 0; 602255213caSSerhiy Boiko } 603255213caSSerhiy Boiko 604255213caSSerhiy Boiko bool prestera_port_is_lag_member(const struct prestera_port *port) 605255213caSSerhiy Boiko { 606255213caSSerhiy Boiko return !!port->lag; 607255213caSSerhiy Boiko } 608255213caSSerhiy Boiko 609255213caSSerhiy Boiko u16 prestera_port_lag_id(const struct prestera_port *port) 610255213caSSerhiy Boiko { 611255213caSSerhiy Boiko return port->lag->lag_id; 612255213caSSerhiy Boiko } 613255213caSSerhiy Boiko 614255213caSSerhiy Boiko static int prestera_lag_init(struct prestera_switch *sw) 615255213caSSerhiy Boiko { 616255213caSSerhiy Boiko u16 id; 617255213caSSerhiy Boiko 618255213caSSerhiy Boiko sw->lags = kcalloc(sw->lag_max, sizeof(*sw->lags), GFP_KERNEL); 619255213caSSerhiy Boiko if (!sw->lags) 620255213caSSerhiy Boiko return -ENOMEM; 621255213caSSerhiy Boiko 622255213caSSerhiy Boiko for (id = 0; id < sw->lag_max; id++) 623255213caSSerhiy Boiko sw->lags[id].lag_id = id; 624255213caSSerhiy Boiko 625255213caSSerhiy Boiko return 0; 626255213caSSerhiy Boiko } 627255213caSSerhiy Boiko 628255213caSSerhiy Boiko static void prestera_lag_fini(struct prestera_switch *sw) 629255213caSSerhiy Boiko { 630255213caSSerhiy Boiko u8 idx; 631255213caSSerhiy Boiko 632255213caSSerhiy Boiko for (idx = 0; idx < sw->lag_max; idx++) 633255213caSSerhiy Boiko WARN_ON(sw->lags[idx].member_count); 634255213caSSerhiy Boiko 635255213caSSerhiy Boiko kfree(sw->lags); 636255213caSSerhiy Boiko } 637255213caSSerhiy Boiko 638e1189d9aSVadym Kochan bool prestera_netdev_check(const struct net_device *dev) 639e1189d9aSVadym Kochan { 640e1189d9aSVadym Kochan return dev->netdev_ops == &prestera_netdev_ops; 641e1189d9aSVadym Kochan } 642e1189d9aSVadym Kochan 6438b0308feSDavid S. Miller static int prestera_lower_dev_walk(struct net_device *dev, 6448b0308feSDavid S. Miller struct netdev_nested_priv *priv) 645e1189d9aSVadym Kochan { 6468b0308feSDavid S. Miller struct prestera_port **pport = (struct prestera_port **)priv->data; 647e1189d9aSVadym Kochan 648e1189d9aSVadym Kochan if (prestera_netdev_check(dev)) { 649e1189d9aSVadym Kochan *pport = netdev_priv(dev); 650e1189d9aSVadym Kochan return 1; 651e1189d9aSVadym Kochan } 652e1189d9aSVadym Kochan 653e1189d9aSVadym Kochan return 0; 654e1189d9aSVadym Kochan } 655e1189d9aSVadym Kochan 656e1189d9aSVadym Kochan struct prestera_port *prestera_port_dev_lower_find(struct net_device *dev) 657e1189d9aSVadym Kochan { 658e1189d9aSVadym Kochan struct prestera_port *port = NULL; 6598b0308feSDavid S. Miller struct netdev_nested_priv priv = { 6608b0308feSDavid S. Miller .data = (void *)&port, 6618b0308feSDavid S. Miller }; 662e1189d9aSVadym Kochan 663e1189d9aSVadym Kochan if (prestera_netdev_check(dev)) 664e1189d9aSVadym Kochan return netdev_priv(dev); 665e1189d9aSVadym Kochan 6668b0308feSDavid S. Miller netdev_walk_all_lower_dev(dev, prestera_lower_dev_walk, &priv); 667e1189d9aSVadym Kochan 668e1189d9aSVadym Kochan return port; 669e1189d9aSVadym Kochan } 670e1189d9aSVadym Kochan 671255213caSSerhiy Boiko static int prestera_netdev_port_lower_event(struct net_device *dev, 672255213caSSerhiy Boiko unsigned long event, void *ptr) 673255213caSSerhiy Boiko { 674255213caSSerhiy Boiko struct netdev_notifier_changelowerstate_info *info = ptr; 675255213caSSerhiy Boiko struct netdev_lag_lower_state_info *lower_state_info; 676255213caSSerhiy Boiko struct prestera_port *port = netdev_priv(dev); 677255213caSSerhiy Boiko bool enabled; 678255213caSSerhiy Boiko 679255213caSSerhiy Boiko if (!netif_is_lag_port(dev)) 680255213caSSerhiy Boiko return 0; 681255213caSSerhiy Boiko if (!prestera_port_is_lag_member(port)) 682255213caSSerhiy Boiko return 0; 683255213caSSerhiy Boiko 684255213caSSerhiy Boiko lower_state_info = info->lower_state_info; 685255213caSSerhiy Boiko enabled = lower_state_info->link_up && lower_state_info->tx_enabled; 686255213caSSerhiy Boiko 687255213caSSerhiy Boiko return prestera_hw_lag_member_enable(port, port->lag->lag_id, enabled); 688255213caSSerhiy Boiko } 689255213caSSerhiy Boiko 690255213caSSerhiy Boiko static bool prestera_lag_master_check(struct net_device *lag_dev, 691255213caSSerhiy Boiko struct netdev_lag_upper_info *info, 692255213caSSerhiy Boiko struct netlink_ext_ack *ext_ack) 693255213caSSerhiy Boiko { 694255213caSSerhiy Boiko if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) { 695255213caSSerhiy Boiko NL_SET_ERR_MSG_MOD(ext_ack, "Unsupported LAG Tx type"); 696255213caSSerhiy Boiko return false; 697255213caSSerhiy Boiko } 698255213caSSerhiy Boiko 699255213caSSerhiy Boiko return true; 700255213caSSerhiy Boiko } 701255213caSSerhiy Boiko 702255213caSSerhiy Boiko static int prestera_netdev_port_event(struct net_device *lower, 703255213caSSerhiy Boiko struct net_device *dev, 704e1189d9aSVadym Kochan unsigned long event, void *ptr) 705e1189d9aSVadym Kochan { 7063d5048ccSVadym Kochan struct netdev_notifier_changeupper_info *info = ptr; 70782bbaa05SVadym Kochan struct prestera_port *port = netdev_priv(dev); 7083d5048ccSVadym Kochan struct netlink_ext_ack *extack; 7093d5048ccSVadym Kochan struct net_device *upper; 7103d5048ccSVadym Kochan 7113d5048ccSVadym Kochan extack = netdev_notifier_info_to_extack(&info->info); 7123d5048ccSVadym Kochan upper = info->upper_dev; 7133d5048ccSVadym Kochan 714e1189d9aSVadym Kochan switch (event) { 715e1189d9aSVadym Kochan case NETDEV_PRECHANGEUPPER: 716255213caSSerhiy Boiko if (!netif_is_bridge_master(upper) && 717255213caSSerhiy Boiko !netif_is_lag_master(upper)) { 7183d5048ccSVadym Kochan NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type"); 7193d5048ccSVadym Kochan return -EINVAL; 720e1189d9aSVadym Kochan } 7213d5048ccSVadym Kochan 7223d5048ccSVadym Kochan if (!info->linking) 7233d5048ccSVadym Kochan break; 7243d5048ccSVadym Kochan 7253d5048ccSVadym Kochan if (netdev_has_any_upper_dev(upper)) { 7263d5048ccSVadym Kochan NL_SET_ERR_MSG_MOD(extack, "Upper device is already enslaved"); 7273d5048ccSVadym Kochan return -EINVAL; 7283d5048ccSVadym Kochan } 729255213caSSerhiy Boiko 730255213caSSerhiy Boiko if (netif_is_lag_master(upper) && 731255213caSSerhiy Boiko !prestera_lag_master_check(upper, info->upper_info, extack)) 732255213caSSerhiy Boiko return -EOPNOTSUPP; 733255213caSSerhiy Boiko if (netif_is_lag_master(upper) && vlan_uses_dev(dev)) { 734255213caSSerhiy Boiko NL_SET_ERR_MSG_MOD(extack, 735255213caSSerhiy Boiko "Master device is a LAG master and port has a VLAN"); 736255213caSSerhiy Boiko return -EINVAL; 737255213caSSerhiy Boiko } 738255213caSSerhiy Boiko if (netif_is_lag_port(dev) && is_vlan_dev(upper) && 739255213caSSerhiy Boiko !netif_is_lag_master(vlan_dev_real_dev(upper))) { 740255213caSSerhiy Boiko NL_SET_ERR_MSG_MOD(extack, 741255213caSSerhiy Boiko "Can not put a VLAN on a LAG port"); 742255213caSSerhiy Boiko return -EINVAL; 743255213caSSerhiy Boiko } 7443d5048ccSVadym Kochan break; 7453d5048ccSVadym Kochan 7463d5048ccSVadym Kochan case NETDEV_CHANGEUPPER: 74782bbaa05SVadym Kochan if (netif_is_bridge_master(upper)) { 74882bbaa05SVadym Kochan if (info->linking) 7492f5dc00fSVladimir Oltean return prestera_bridge_port_join(upper, port, 7502f5dc00fSVladimir Oltean extack); 75182bbaa05SVadym Kochan else 75282bbaa05SVadym Kochan prestera_bridge_port_leave(upper, port); 753255213caSSerhiy Boiko } else if (netif_is_lag_master(upper)) { 754255213caSSerhiy Boiko if (info->linking) 755255213caSSerhiy Boiko return prestera_lag_port_add(port, upper); 756255213caSSerhiy Boiko else 757255213caSSerhiy Boiko prestera_lag_port_del(port); 75882bbaa05SVadym Kochan } 7593d5048ccSVadym Kochan break; 760255213caSSerhiy Boiko 761255213caSSerhiy Boiko case NETDEV_CHANGELOWERSTATE: 762255213caSSerhiy Boiko return prestera_netdev_port_lower_event(dev, event, ptr); 763255213caSSerhiy Boiko } 764255213caSSerhiy Boiko 765255213caSSerhiy Boiko return 0; 766255213caSSerhiy Boiko } 767255213caSSerhiy Boiko 768255213caSSerhiy Boiko static int prestera_netdevice_lag_event(struct net_device *lag_dev, 769255213caSSerhiy Boiko unsigned long event, void *ptr) 770255213caSSerhiy Boiko { 771255213caSSerhiy Boiko struct net_device *dev; 772255213caSSerhiy Boiko struct list_head *iter; 773255213caSSerhiy Boiko int err; 774255213caSSerhiy Boiko 775255213caSSerhiy Boiko netdev_for_each_lower_dev(lag_dev, dev, iter) { 776255213caSSerhiy Boiko if (prestera_netdev_check(dev)) { 777255213caSSerhiy Boiko err = prestera_netdev_port_event(lag_dev, dev, event, 778255213caSSerhiy Boiko ptr); 779255213caSSerhiy Boiko if (err) 780255213caSSerhiy Boiko return err; 781255213caSSerhiy Boiko } 7823d5048ccSVadym Kochan } 7833d5048ccSVadym Kochan 7843d5048ccSVadym Kochan return 0; 785e1189d9aSVadym Kochan } 786e1189d9aSVadym Kochan 787e1189d9aSVadym Kochan static int prestera_netdev_event_handler(struct notifier_block *nb, 788e1189d9aSVadym Kochan unsigned long event, void *ptr) 789e1189d9aSVadym Kochan { 790e1189d9aSVadym Kochan struct net_device *dev = netdev_notifier_info_to_dev(ptr); 791e1189d9aSVadym Kochan int err = 0; 792e1189d9aSVadym Kochan 793e1189d9aSVadym Kochan if (prestera_netdev_check(dev)) 794255213caSSerhiy Boiko err = prestera_netdev_port_event(dev, dev, event, ptr); 795255213caSSerhiy Boiko else if (netif_is_lag_master(dev)) 796255213caSSerhiy Boiko err = prestera_netdevice_lag_event(dev, event, ptr); 797e1189d9aSVadym Kochan 798e1189d9aSVadym Kochan return notifier_from_errno(err); 799e1189d9aSVadym Kochan } 800e1189d9aSVadym Kochan 801e1189d9aSVadym Kochan static int prestera_netdev_event_handler_register(struct prestera_switch *sw) 802e1189d9aSVadym Kochan { 803e1189d9aSVadym Kochan sw->netdev_nb.notifier_call = prestera_netdev_event_handler; 804e1189d9aSVadym Kochan 805e1189d9aSVadym Kochan return register_netdevice_notifier(&sw->netdev_nb); 806e1189d9aSVadym Kochan } 807e1189d9aSVadym Kochan 808e1189d9aSVadym Kochan static void prestera_netdev_event_handler_unregister(struct prestera_switch *sw) 809e1189d9aSVadym Kochan { 810e1189d9aSVadym Kochan unregister_netdevice_notifier(&sw->netdev_nb); 811e1189d9aSVadym Kochan } 812e1189d9aSVadym Kochan 813501ef306SVadym Kochan static int prestera_switch_init(struct prestera_switch *sw) 814501ef306SVadym Kochan { 815501ef306SVadym Kochan int err; 816501ef306SVadym Kochan 817501ef306SVadym Kochan err = prestera_hw_switch_init(sw); 818501ef306SVadym Kochan if (err) { 819501ef306SVadym Kochan dev_err(prestera_dev(sw), "Failed to init Switch device\n"); 820501ef306SVadym Kochan return err; 821501ef306SVadym Kochan } 822501ef306SVadym Kochan 823501ef306SVadym Kochan rwlock_init(&sw->port_list_lock); 824501ef306SVadym Kochan INIT_LIST_HEAD(&sw->port_list); 825501ef306SVadym Kochan 826501ef306SVadym Kochan err = prestera_switch_set_base_mac_addr(sw); 827501ef306SVadym Kochan if (err) 828501ef306SVadym Kochan return err; 829501ef306SVadym Kochan 830e1189d9aSVadym Kochan err = prestera_netdev_event_handler_register(sw); 831501ef306SVadym Kochan if (err) 832501ef306SVadym Kochan return err; 833501ef306SVadym Kochan 834e1189d9aSVadym Kochan err = prestera_switchdev_init(sw); 835e1189d9aSVadym Kochan if (err) 836e1189d9aSVadym Kochan goto err_swdev_register; 837e1189d9aSVadym Kochan 838e1189d9aSVadym Kochan err = prestera_rxtx_switch_init(sw); 839e1189d9aSVadym Kochan if (err) 840e1189d9aSVadym Kochan goto err_rxtx_register; 841e1189d9aSVadym Kochan 842501ef306SVadym Kochan err = prestera_event_handlers_register(sw); 843501ef306SVadym Kochan if (err) 844501ef306SVadym Kochan goto err_handlers_register; 845501ef306SVadym Kochan 8468b474a9fSSerhiy Boiko err = prestera_acl_init(sw); 8478b474a9fSSerhiy Boiko if (err) 8488b474a9fSSerhiy Boiko goto err_acl_init; 8498b474a9fSSerhiy Boiko 85013defa27SSerhiy Boiko err = prestera_span_init(sw); 85113defa27SSerhiy Boiko if (err) 85213defa27SSerhiy Boiko goto err_span_init; 85313defa27SSerhiy Boiko 8544beb0c24SLeon Romanovsky err = prestera_devlink_traps_register(sw); 85534dd1710SVadym Kochan if (err) 85634dd1710SVadym Kochan goto err_dl_register; 85734dd1710SVadym Kochan 858255213caSSerhiy Boiko err = prestera_lag_init(sw); 859255213caSSerhiy Boiko if (err) 860255213caSSerhiy Boiko goto err_lag_init; 861255213caSSerhiy Boiko 862501ef306SVadym Kochan err = prestera_create_ports(sw); 863501ef306SVadym Kochan if (err) 864501ef306SVadym Kochan goto err_ports_create; 865501ef306SVadym Kochan 8664beb0c24SLeon Romanovsky prestera_devlink_register(sw); 867501ef306SVadym Kochan return 0; 868501ef306SVadym Kochan 869501ef306SVadym Kochan err_ports_create: 870255213caSSerhiy Boiko prestera_lag_fini(sw); 871255213caSSerhiy Boiko err_lag_init: 8724beb0c24SLeon Romanovsky prestera_devlink_traps_unregister(sw); 87334dd1710SVadym Kochan err_dl_register: 87413defa27SSerhiy Boiko prestera_span_fini(sw); 87513defa27SSerhiy Boiko err_span_init: 8768b474a9fSSerhiy Boiko prestera_acl_fini(sw); 8778b474a9fSSerhiy Boiko err_acl_init: 878501ef306SVadym Kochan prestera_event_handlers_unregister(sw); 879501ef306SVadym Kochan err_handlers_register: 880501ef306SVadym Kochan prestera_rxtx_switch_fini(sw); 881e1189d9aSVadym Kochan err_rxtx_register: 882e1189d9aSVadym Kochan prestera_switchdev_fini(sw); 883e1189d9aSVadym Kochan err_swdev_register: 884e1189d9aSVadym Kochan prestera_netdev_event_handler_unregister(sw); 885501ef306SVadym Kochan prestera_hw_switch_fini(sw); 886501ef306SVadym Kochan 887501ef306SVadym Kochan return err; 888501ef306SVadym Kochan } 889501ef306SVadym Kochan 890501ef306SVadym Kochan static void prestera_switch_fini(struct prestera_switch *sw) 891501ef306SVadym Kochan { 8924beb0c24SLeon Romanovsky prestera_devlink_unregister(sw); 893501ef306SVadym Kochan prestera_destroy_ports(sw); 894255213caSSerhiy Boiko prestera_lag_fini(sw); 8954beb0c24SLeon Romanovsky prestera_devlink_traps_unregister(sw); 89613defa27SSerhiy Boiko prestera_span_fini(sw); 8978b474a9fSSerhiy Boiko prestera_acl_fini(sw); 898501ef306SVadym Kochan prestera_event_handlers_unregister(sw); 899501ef306SVadym Kochan prestera_rxtx_switch_fini(sw); 900e1189d9aSVadym Kochan prestera_switchdev_fini(sw); 901e1189d9aSVadym Kochan prestera_netdev_event_handler_unregister(sw); 902501ef306SVadym Kochan prestera_hw_switch_fini(sw); 903501ef306SVadym Kochan } 904501ef306SVadym Kochan 905501ef306SVadym Kochan int prestera_device_register(struct prestera_device *dev) 906501ef306SVadym Kochan { 907501ef306SVadym Kochan struct prestera_switch *sw; 908501ef306SVadym Kochan int err; 909501ef306SVadym Kochan 910919d13a7SLeon Romanovsky sw = prestera_devlink_alloc(dev); 911501ef306SVadym Kochan if (!sw) 912501ef306SVadym Kochan return -ENOMEM; 913501ef306SVadym Kochan 914501ef306SVadym Kochan dev->priv = sw; 915501ef306SVadym Kochan sw->dev = dev; 916501ef306SVadym Kochan 917501ef306SVadym Kochan err = prestera_switch_init(sw); 918501ef306SVadym Kochan if (err) { 91934dd1710SVadym Kochan prestera_devlink_free(sw); 920501ef306SVadym Kochan return err; 921501ef306SVadym Kochan } 922501ef306SVadym Kochan 923501ef306SVadym Kochan return 0; 924501ef306SVadym Kochan } 925501ef306SVadym Kochan EXPORT_SYMBOL(prestera_device_register); 926501ef306SVadym Kochan 927501ef306SVadym Kochan void prestera_device_unregister(struct prestera_device *dev) 928501ef306SVadym Kochan { 929501ef306SVadym Kochan struct prestera_switch *sw = dev->priv; 930501ef306SVadym Kochan 931501ef306SVadym Kochan prestera_switch_fini(sw); 93234dd1710SVadym Kochan prestera_devlink_free(sw); 933501ef306SVadym Kochan } 934501ef306SVadym Kochan EXPORT_SYMBOL(prestera_device_unregister); 935501ef306SVadym Kochan 936501ef306SVadym Kochan static int __init prestera_module_init(void) 937501ef306SVadym Kochan { 938501ef306SVadym Kochan prestera_wq = alloc_workqueue("prestera", 0, 0); 939501ef306SVadym Kochan if (!prestera_wq) 940501ef306SVadym Kochan return -ENOMEM; 941501ef306SVadym Kochan 942501ef306SVadym Kochan return 0; 943501ef306SVadym Kochan } 944501ef306SVadym Kochan 945501ef306SVadym Kochan static void __exit prestera_module_exit(void) 946501ef306SVadym Kochan { 947501ef306SVadym Kochan destroy_workqueue(prestera_wq); 948501ef306SVadym Kochan } 949501ef306SVadym Kochan 950501ef306SVadym Kochan module_init(prestera_module_init); 951501ef306SVadym Kochan module_exit(prestera_module_exit); 952501ef306SVadym Kochan 953501ef306SVadym Kochan MODULE_LICENSE("Dual BSD/GPL"); 954501ef306SVadym Kochan MODULE_DESCRIPTION("Marvell Prestera switch driver"); 955