18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 58c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 68c2ecf20Sopenharmony_ci#include <linux/list.h> 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/netdev_features.h> 98c2ecf20Sopenharmony_ci#include <linux/of.h> 108c2ecf20Sopenharmony_ci#include <linux/of_net.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "prestera.h" 138c2ecf20Sopenharmony_ci#include "prestera_hw.h" 148c2ecf20Sopenharmony_ci#include "prestera_rxtx.h" 158c2ecf20Sopenharmony_ci#include "prestera_devlink.h" 168c2ecf20Sopenharmony_ci#include "prestera_ethtool.h" 178c2ecf20Sopenharmony_ci#include "prestera_switchdev.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define PRESTERA_MTU_DEFAULT 1536 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define PRESTERA_STATS_DELAY_MS 1000 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define PRESTERA_MAC_ADDR_NUM_MAX 255 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic struct workqueue_struct *prestera_wq; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ciint prestera_port_pvid_set(struct prestera_port *port, u16 vid) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci enum prestera_accept_frm_type frm_type; 308c2ecf20Sopenharmony_ci int err; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci frm_type = PRESTERA_ACCEPT_FRAME_TYPE_TAGGED; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci if (vid) { 358c2ecf20Sopenharmony_ci err = prestera_hw_vlan_port_vid_set(port, vid); 368c2ecf20Sopenharmony_ci if (err) 378c2ecf20Sopenharmony_ci return err; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci frm_type = PRESTERA_ACCEPT_FRAME_TYPE_ALL; 408c2ecf20Sopenharmony_ci } 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci err = prestera_hw_port_accept_frm_type(port, frm_type); 438c2ecf20Sopenharmony_ci if (err && frm_type == PRESTERA_ACCEPT_FRAME_TYPE_ALL) 448c2ecf20Sopenharmony_ci prestera_hw_vlan_port_vid_set(port, port->pvid); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci port->pvid = vid; 478c2ecf20Sopenharmony_ci return 0; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct prestera_port *prestera_port_find_by_hwid(struct prestera_switch *sw, 518c2ecf20Sopenharmony_ci u32 dev_id, u32 hw_id) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci struct prestera_port *port = NULL, *tmp; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci read_lock(&sw->port_list_lock); 568c2ecf20Sopenharmony_ci list_for_each_entry(tmp, &sw->port_list, list) { 578c2ecf20Sopenharmony_ci if (tmp->dev_id == dev_id && tmp->hw_id == hw_id) { 588c2ecf20Sopenharmony_ci port = tmp; 598c2ecf20Sopenharmony_ci break; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci read_unlock(&sw->port_list_lock); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci return port; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistruct prestera_port *prestera_find_port(struct prestera_switch *sw, u32 id) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct prestera_port *port = NULL, *tmp; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci read_lock(&sw->port_list_lock); 728c2ecf20Sopenharmony_ci list_for_each_entry(tmp, &sw->port_list, list) { 738c2ecf20Sopenharmony_ci if (tmp->id == id) { 748c2ecf20Sopenharmony_ci port = tmp; 758c2ecf20Sopenharmony_ci break; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci read_unlock(&sw->port_list_lock); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return port; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int prestera_port_open(struct net_device *dev) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 868c2ecf20Sopenharmony_ci int err; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci err = prestera_hw_port_state_set(port, true); 898c2ecf20Sopenharmony_ci if (err) 908c2ecf20Sopenharmony_ci return err; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci netif_start_queue(dev); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return 0; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic int prestera_port_close(struct net_device *dev) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 1008c2ecf20Sopenharmony_ci int err; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci netif_stop_queue(dev); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci err = prestera_hw_port_state_set(port, false); 1058c2ecf20Sopenharmony_ci if (err) 1068c2ecf20Sopenharmony_ci return err; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return 0; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic netdev_tx_t prestera_port_xmit(struct sk_buff *skb, 1128c2ecf20Sopenharmony_ci struct net_device *dev) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci return prestera_rxtx_xmit(netdev_priv(dev), skb); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int prestera_is_valid_mac_addr(struct prestera_port *port, u8 *addr) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(addr)) 1208c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* firmware requires that port's MAC address contains first 5 bytes 1238c2ecf20Sopenharmony_ci * of the base MAC address 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_ci if (memcmp(port->sw->base_mac, addr, ETH_ALEN - 1)) 1268c2ecf20Sopenharmony_ci return -EINVAL; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return 0; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic int prestera_port_set_mac_address(struct net_device *dev, void *p) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 1348c2ecf20Sopenharmony_ci struct sockaddr *addr = p; 1358c2ecf20Sopenharmony_ci int err; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci err = prestera_is_valid_mac_addr(port, addr->sa_data); 1388c2ecf20Sopenharmony_ci if (err) 1398c2ecf20Sopenharmony_ci return err; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci err = prestera_hw_port_mac_set(port, addr->sa_data); 1428c2ecf20Sopenharmony_ci if (err) 1438c2ecf20Sopenharmony_ci return err; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci ether_addr_copy(dev->dev_addr, addr->sa_data); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return 0; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int prestera_port_change_mtu(struct net_device *dev, int mtu) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 1538c2ecf20Sopenharmony_ci int err; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci err = prestera_hw_port_mtu_set(port, mtu); 1568c2ecf20Sopenharmony_ci if (err) 1578c2ecf20Sopenharmony_ci return err; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci dev->mtu = mtu; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci return 0; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic void prestera_port_get_stats64(struct net_device *dev, 1658c2ecf20Sopenharmony_ci struct rtnl_link_stats64 *stats) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 1688c2ecf20Sopenharmony_ci struct prestera_port_stats *port_stats = &port->cached_hw_stats.stats; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci stats->rx_packets = port_stats->broadcast_frames_received + 1718c2ecf20Sopenharmony_ci port_stats->multicast_frames_received + 1728c2ecf20Sopenharmony_ci port_stats->unicast_frames_received; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci stats->tx_packets = port_stats->broadcast_frames_sent + 1758c2ecf20Sopenharmony_ci port_stats->multicast_frames_sent + 1768c2ecf20Sopenharmony_ci port_stats->unicast_frames_sent; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci stats->rx_bytes = port_stats->good_octets_received; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci stats->tx_bytes = port_stats->good_octets_sent; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci stats->rx_errors = port_stats->rx_error_frame_received; 1838c2ecf20Sopenharmony_ci stats->tx_errors = port_stats->mac_trans_error; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci stats->rx_dropped = port_stats->buffer_overrun; 1868c2ecf20Sopenharmony_ci stats->tx_dropped = 0; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci stats->multicast = port_stats->multicast_frames_received; 1898c2ecf20Sopenharmony_ci stats->collisions = port_stats->excessive_collision; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci stats->rx_crc_errors = port_stats->bad_crc; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic void prestera_port_get_hw_stats(struct prestera_port *port) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci prestera_hw_port_stats_get(port, &port->cached_hw_stats.stats); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic void prestera_port_stats_update(struct work_struct *work) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct prestera_port *port = 2028c2ecf20Sopenharmony_ci container_of(work, struct prestera_port, 2038c2ecf20Sopenharmony_ci cached_hw_stats.caching_dw.work); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci prestera_port_get_hw_stats(port); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci queue_delayed_work(prestera_wq, &port->cached_hw_stats.caching_dw, 2088c2ecf20Sopenharmony_ci msecs_to_jiffies(PRESTERA_STATS_DELAY_MS)); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic const struct net_device_ops prestera_netdev_ops = { 2128c2ecf20Sopenharmony_ci .ndo_open = prestera_port_open, 2138c2ecf20Sopenharmony_ci .ndo_stop = prestera_port_close, 2148c2ecf20Sopenharmony_ci .ndo_start_xmit = prestera_port_xmit, 2158c2ecf20Sopenharmony_ci .ndo_change_mtu = prestera_port_change_mtu, 2168c2ecf20Sopenharmony_ci .ndo_get_stats64 = prestera_port_get_stats64, 2178c2ecf20Sopenharmony_ci .ndo_set_mac_address = prestera_port_set_mac_address, 2188c2ecf20Sopenharmony_ci .ndo_get_devlink_port = prestera_devlink_get_port, 2198c2ecf20Sopenharmony_ci}; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ciint prestera_port_autoneg_set(struct prestera_port *port, bool enable, 2228c2ecf20Sopenharmony_ci u64 adver_link_modes, u8 adver_fec) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci bool refresh = false; 2258c2ecf20Sopenharmony_ci u64 link_modes; 2268c2ecf20Sopenharmony_ci int err; 2278c2ecf20Sopenharmony_ci u8 fec; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (port->caps.type != PRESTERA_PORT_TYPE_TP) 2308c2ecf20Sopenharmony_ci return enable ? -EINVAL : 0; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (!enable) 2338c2ecf20Sopenharmony_ci goto set_autoneg; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci link_modes = port->caps.supp_link_modes & adver_link_modes; 2368c2ecf20Sopenharmony_ci fec = port->caps.supp_fec & adver_fec; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (!link_modes && !fec) 2398c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (link_modes && port->adver_link_modes != link_modes) { 2428c2ecf20Sopenharmony_ci port->adver_link_modes = link_modes; 2438c2ecf20Sopenharmony_ci refresh = true; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (fec && port->adver_fec != fec) { 2478c2ecf20Sopenharmony_ci port->adver_fec = fec; 2488c2ecf20Sopenharmony_ci refresh = true; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ciset_autoneg: 2528c2ecf20Sopenharmony_ci if (port->autoneg == enable && !refresh) 2538c2ecf20Sopenharmony_ci return 0; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci err = prestera_hw_port_autoneg_set(port, enable, port->adver_link_modes, 2568c2ecf20Sopenharmony_ci port->adver_fec); 2578c2ecf20Sopenharmony_ci if (err) 2588c2ecf20Sopenharmony_ci return err; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci port->autoneg = enable; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return 0; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic void prestera_port_list_add(struct prestera_port *port) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci write_lock(&port->sw->port_list_lock); 2688c2ecf20Sopenharmony_ci list_add(&port->list, &port->sw->port_list); 2698c2ecf20Sopenharmony_ci write_unlock(&port->sw->port_list_lock); 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic void prestera_port_list_del(struct prestera_port *port) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci write_lock(&port->sw->port_list_lock); 2758c2ecf20Sopenharmony_ci list_del(&port->list); 2768c2ecf20Sopenharmony_ci write_unlock(&port->sw->port_list_lock); 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic int prestera_port_create(struct prestera_switch *sw, u32 id) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci struct prestera_port *port; 2828c2ecf20Sopenharmony_ci struct net_device *dev; 2838c2ecf20Sopenharmony_ci int err; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci dev = alloc_etherdev(sizeof(*port)); 2868c2ecf20Sopenharmony_ci if (!dev) 2878c2ecf20Sopenharmony_ci return -ENOMEM; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci port = netdev_priv(dev); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&port->vlans_list); 2928c2ecf20Sopenharmony_ci port->pvid = PRESTERA_DEFAULT_VID; 2938c2ecf20Sopenharmony_ci port->dev = dev; 2948c2ecf20Sopenharmony_ci port->id = id; 2958c2ecf20Sopenharmony_ci port->sw = sw; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci err = prestera_hw_port_info_get(port, &port->dev_id, &port->hw_id, 2988c2ecf20Sopenharmony_ci &port->fp_id); 2998c2ecf20Sopenharmony_ci if (err) { 3008c2ecf20Sopenharmony_ci dev_err(prestera_dev(sw), "Failed to get port(%u) info\n", id); 3018c2ecf20Sopenharmony_ci goto err_port_info_get; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci err = prestera_devlink_port_register(port); 3058c2ecf20Sopenharmony_ci if (err) 3068c2ecf20Sopenharmony_ci goto err_dl_port_register; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci dev->features |= NETIF_F_NETNS_LOCAL; 3098c2ecf20Sopenharmony_ci dev->netdev_ops = &prestera_netdev_ops; 3108c2ecf20Sopenharmony_ci dev->ethtool_ops = &prestera_ethtool_ops; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci netif_carrier_off(dev); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci dev->mtu = min_t(unsigned int, sw->mtu_max, PRESTERA_MTU_DEFAULT); 3158c2ecf20Sopenharmony_ci dev->min_mtu = sw->mtu_min; 3168c2ecf20Sopenharmony_ci dev->max_mtu = sw->mtu_max; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci err = prestera_hw_port_mtu_set(port, dev->mtu); 3198c2ecf20Sopenharmony_ci if (err) { 3208c2ecf20Sopenharmony_ci dev_err(prestera_dev(sw), "Failed to set port(%u) mtu(%d)\n", 3218c2ecf20Sopenharmony_ci id, dev->mtu); 3228c2ecf20Sopenharmony_ci goto err_port_init; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (port->fp_id >= PRESTERA_MAC_ADDR_NUM_MAX) { 3268c2ecf20Sopenharmony_ci err = -EINVAL; 3278c2ecf20Sopenharmony_ci goto err_port_init; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* firmware requires that port's MAC address consist of the first 3318c2ecf20Sopenharmony_ci * 5 bytes of the base MAC address 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_ci memcpy(dev->dev_addr, sw->base_mac, dev->addr_len - 1); 3348c2ecf20Sopenharmony_ci dev->dev_addr[dev->addr_len - 1] = port->fp_id; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci err = prestera_hw_port_mac_set(port, dev->dev_addr); 3378c2ecf20Sopenharmony_ci if (err) { 3388c2ecf20Sopenharmony_ci dev_err(prestera_dev(sw), "Failed to set port(%u) mac addr\n", id); 3398c2ecf20Sopenharmony_ci goto err_port_init; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci err = prestera_hw_port_cap_get(port, &port->caps); 3438c2ecf20Sopenharmony_ci if (err) { 3448c2ecf20Sopenharmony_ci dev_err(prestera_dev(sw), "Failed to get port(%u) caps\n", id); 3458c2ecf20Sopenharmony_ci goto err_port_init; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci port->adver_fec = BIT(PRESTERA_PORT_FEC_OFF); 3498c2ecf20Sopenharmony_ci prestera_port_autoneg_set(port, true, port->caps.supp_link_modes, 3508c2ecf20Sopenharmony_ci port->caps.supp_fec); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci err = prestera_hw_port_state_set(port, false); 3538c2ecf20Sopenharmony_ci if (err) { 3548c2ecf20Sopenharmony_ci dev_err(prestera_dev(sw), "Failed to set port(%u) down\n", id); 3558c2ecf20Sopenharmony_ci goto err_port_init; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci err = prestera_rxtx_port_init(port); 3598c2ecf20Sopenharmony_ci if (err) 3608c2ecf20Sopenharmony_ci goto err_port_init; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&port->cached_hw_stats.caching_dw, 3638c2ecf20Sopenharmony_ci &prestera_port_stats_update); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci prestera_port_list_add(port); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci err = register_netdev(dev); 3688c2ecf20Sopenharmony_ci if (err) 3698c2ecf20Sopenharmony_ci goto err_register_netdev; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci prestera_devlink_port_set(port); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci return 0; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cierr_register_netdev: 3768c2ecf20Sopenharmony_ci prestera_port_list_del(port); 3778c2ecf20Sopenharmony_cierr_port_init: 3788c2ecf20Sopenharmony_ci prestera_devlink_port_unregister(port); 3798c2ecf20Sopenharmony_cierr_dl_port_register: 3808c2ecf20Sopenharmony_cierr_port_info_get: 3818c2ecf20Sopenharmony_ci free_netdev(dev); 3828c2ecf20Sopenharmony_ci return err; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic void prestera_port_destroy(struct prestera_port *port) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci struct net_device *dev = port->dev; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&port->cached_hw_stats.caching_dw); 3908c2ecf20Sopenharmony_ci prestera_devlink_port_clear(port); 3918c2ecf20Sopenharmony_ci unregister_netdev(dev); 3928c2ecf20Sopenharmony_ci prestera_port_list_del(port); 3938c2ecf20Sopenharmony_ci prestera_devlink_port_unregister(port); 3948c2ecf20Sopenharmony_ci free_netdev(dev); 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic void prestera_destroy_ports(struct prestera_switch *sw) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci struct prestera_port *port, *tmp; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci list_for_each_entry_safe(port, tmp, &sw->port_list, list) 4028c2ecf20Sopenharmony_ci prestera_port_destroy(port); 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic int prestera_create_ports(struct prestera_switch *sw) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci struct prestera_port *port, *tmp; 4088c2ecf20Sopenharmony_ci u32 port_idx; 4098c2ecf20Sopenharmony_ci int err; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci for (port_idx = 0; port_idx < sw->port_count; port_idx++) { 4128c2ecf20Sopenharmony_ci err = prestera_port_create(sw, port_idx); 4138c2ecf20Sopenharmony_ci if (err) 4148c2ecf20Sopenharmony_ci goto err_port_create; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return 0; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cierr_port_create: 4208c2ecf20Sopenharmony_ci list_for_each_entry_safe(port, tmp, &sw->port_list, list) 4218c2ecf20Sopenharmony_ci prestera_port_destroy(port); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci return err; 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic void prestera_port_handle_event(struct prestera_switch *sw, 4278c2ecf20Sopenharmony_ci struct prestera_event *evt, void *arg) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci struct delayed_work *caching_dw; 4308c2ecf20Sopenharmony_ci struct prestera_port *port; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci port = prestera_find_port(sw, evt->port_evt.port_id); 4338c2ecf20Sopenharmony_ci if (!port || !port->dev) 4348c2ecf20Sopenharmony_ci return; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci caching_dw = &port->cached_hw_stats.caching_dw; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (evt->id == PRESTERA_PORT_EVENT_STATE_CHANGED) { 4398c2ecf20Sopenharmony_ci if (evt->port_evt.data.oper_state) { 4408c2ecf20Sopenharmony_ci netif_carrier_on(port->dev); 4418c2ecf20Sopenharmony_ci if (!delayed_work_pending(caching_dw)) 4428c2ecf20Sopenharmony_ci queue_delayed_work(prestera_wq, caching_dw, 0); 4438c2ecf20Sopenharmony_ci } else if (netif_running(port->dev) && 4448c2ecf20Sopenharmony_ci netif_carrier_ok(port->dev)) { 4458c2ecf20Sopenharmony_ci netif_carrier_off(port->dev); 4468c2ecf20Sopenharmony_ci if (delayed_work_pending(caching_dw)) 4478c2ecf20Sopenharmony_ci cancel_delayed_work(caching_dw); 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cistatic int prestera_event_handlers_register(struct prestera_switch *sw) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci return prestera_hw_event_handler_register(sw, PRESTERA_EVENT_TYPE_PORT, 4558c2ecf20Sopenharmony_ci prestera_port_handle_event, 4568c2ecf20Sopenharmony_ci NULL); 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic void prestera_event_handlers_unregister(struct prestera_switch *sw) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci prestera_hw_event_handler_unregister(sw, PRESTERA_EVENT_TYPE_PORT, 4628c2ecf20Sopenharmony_ci prestera_port_handle_event); 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic int prestera_switch_set_base_mac_addr(struct prestera_switch *sw) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci struct device_node *base_mac_np; 4688c2ecf20Sopenharmony_ci struct device_node *np; 4698c2ecf20Sopenharmony_ci const char *base_mac; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "marvell,prestera"); 4728c2ecf20Sopenharmony_ci base_mac_np = of_parse_phandle(np, "base-mac-provider", 0); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci base_mac = of_get_mac_address(base_mac_np); 4758c2ecf20Sopenharmony_ci of_node_put(base_mac_np); 4768c2ecf20Sopenharmony_ci if (!IS_ERR(base_mac)) 4778c2ecf20Sopenharmony_ci ether_addr_copy(sw->base_mac, base_mac); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(sw->base_mac)) { 4808c2ecf20Sopenharmony_ci eth_random_addr(sw->base_mac); 4818c2ecf20Sopenharmony_ci dev_info(prestera_dev(sw), "using random base mac address\n"); 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci return prestera_hw_switch_mac_set(sw, sw->base_mac); 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cibool prestera_netdev_check(const struct net_device *dev) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci return dev->netdev_ops == &prestera_netdev_ops; 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic int prestera_lower_dev_walk(struct net_device *dev, 4938c2ecf20Sopenharmony_ci struct netdev_nested_priv *priv) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci struct prestera_port **pport = (struct prestera_port **)priv->data; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (prestera_netdev_check(dev)) { 4988c2ecf20Sopenharmony_ci *pport = netdev_priv(dev); 4998c2ecf20Sopenharmony_ci return 1; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci return 0; 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistruct prestera_port *prestera_port_dev_lower_find(struct net_device *dev) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci struct prestera_port *port = NULL; 5088c2ecf20Sopenharmony_ci struct netdev_nested_priv priv = { 5098c2ecf20Sopenharmony_ci .data = (void *)&port, 5108c2ecf20Sopenharmony_ci }; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (prestera_netdev_check(dev)) 5138c2ecf20Sopenharmony_ci return netdev_priv(dev); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci netdev_walk_all_lower_dev(dev, prestera_lower_dev_walk, &priv); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci return port; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic int prestera_netdev_port_event(struct net_device *dev, 5218c2ecf20Sopenharmony_ci unsigned long event, void *ptr) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci switch (event) { 5248c2ecf20Sopenharmony_ci case NETDEV_PRECHANGEUPPER: 5258c2ecf20Sopenharmony_ci case NETDEV_CHANGEUPPER: 5268c2ecf20Sopenharmony_ci return prestera_bridge_port_event(dev, event, ptr); 5278c2ecf20Sopenharmony_ci default: 5288c2ecf20Sopenharmony_ci return 0; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic int prestera_netdev_event_handler(struct notifier_block *nb, 5338c2ecf20Sopenharmony_ci unsigned long event, void *ptr) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 5368c2ecf20Sopenharmony_ci int err = 0; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci if (prestera_netdev_check(dev)) 5398c2ecf20Sopenharmony_ci err = prestera_netdev_port_event(dev, event, ptr); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci return notifier_from_errno(err); 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cistatic int prestera_netdev_event_handler_register(struct prestera_switch *sw) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci sw->netdev_nb.notifier_call = prestera_netdev_event_handler; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci return register_netdevice_notifier(&sw->netdev_nb); 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cistatic void prestera_netdev_event_handler_unregister(struct prestera_switch *sw) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&sw->netdev_nb); 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cistatic int prestera_switch_init(struct prestera_switch *sw) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci int err; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci err = prestera_hw_switch_init(sw); 5618c2ecf20Sopenharmony_ci if (err) { 5628c2ecf20Sopenharmony_ci dev_err(prestera_dev(sw), "Failed to init Switch device\n"); 5638c2ecf20Sopenharmony_ci return err; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci rwlock_init(&sw->port_list_lock); 5678c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sw->port_list); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci err = prestera_switch_set_base_mac_addr(sw); 5708c2ecf20Sopenharmony_ci if (err) 5718c2ecf20Sopenharmony_ci return err; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci err = prestera_netdev_event_handler_register(sw); 5748c2ecf20Sopenharmony_ci if (err) 5758c2ecf20Sopenharmony_ci return err; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci err = prestera_switchdev_init(sw); 5788c2ecf20Sopenharmony_ci if (err) 5798c2ecf20Sopenharmony_ci goto err_swdev_register; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci err = prestera_rxtx_switch_init(sw); 5828c2ecf20Sopenharmony_ci if (err) 5838c2ecf20Sopenharmony_ci goto err_rxtx_register; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci err = prestera_event_handlers_register(sw); 5868c2ecf20Sopenharmony_ci if (err) 5878c2ecf20Sopenharmony_ci goto err_handlers_register; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci err = prestera_devlink_register(sw); 5908c2ecf20Sopenharmony_ci if (err) 5918c2ecf20Sopenharmony_ci goto err_dl_register; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci err = prestera_create_ports(sw); 5948c2ecf20Sopenharmony_ci if (err) 5958c2ecf20Sopenharmony_ci goto err_ports_create; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci return 0; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cierr_ports_create: 6008c2ecf20Sopenharmony_ci prestera_devlink_unregister(sw); 6018c2ecf20Sopenharmony_cierr_dl_register: 6028c2ecf20Sopenharmony_ci prestera_event_handlers_unregister(sw); 6038c2ecf20Sopenharmony_cierr_handlers_register: 6048c2ecf20Sopenharmony_ci prestera_rxtx_switch_fini(sw); 6058c2ecf20Sopenharmony_cierr_rxtx_register: 6068c2ecf20Sopenharmony_ci prestera_switchdev_fini(sw); 6078c2ecf20Sopenharmony_cierr_swdev_register: 6088c2ecf20Sopenharmony_ci prestera_netdev_event_handler_unregister(sw); 6098c2ecf20Sopenharmony_ci prestera_hw_switch_fini(sw); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci return err; 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic void prestera_switch_fini(struct prestera_switch *sw) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci prestera_destroy_ports(sw); 6178c2ecf20Sopenharmony_ci prestera_devlink_unregister(sw); 6188c2ecf20Sopenharmony_ci prestera_event_handlers_unregister(sw); 6198c2ecf20Sopenharmony_ci prestera_rxtx_switch_fini(sw); 6208c2ecf20Sopenharmony_ci prestera_switchdev_fini(sw); 6218c2ecf20Sopenharmony_ci prestera_netdev_event_handler_unregister(sw); 6228c2ecf20Sopenharmony_ci prestera_hw_switch_fini(sw); 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ciint prestera_device_register(struct prestera_device *dev) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci struct prestera_switch *sw; 6288c2ecf20Sopenharmony_ci int err; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci sw = prestera_devlink_alloc(); 6318c2ecf20Sopenharmony_ci if (!sw) 6328c2ecf20Sopenharmony_ci return -ENOMEM; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci dev->priv = sw; 6358c2ecf20Sopenharmony_ci sw->dev = dev; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci err = prestera_switch_init(sw); 6388c2ecf20Sopenharmony_ci if (err) { 6398c2ecf20Sopenharmony_ci prestera_devlink_free(sw); 6408c2ecf20Sopenharmony_ci return err; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci return 0; 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(prestera_device_register); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_civoid prestera_device_unregister(struct prestera_device *dev) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci struct prestera_switch *sw = dev->priv; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci prestera_switch_fini(sw); 6528c2ecf20Sopenharmony_ci prestera_devlink_free(sw); 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_ciEXPORT_SYMBOL(prestera_device_unregister); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_cistatic int __init prestera_module_init(void) 6578c2ecf20Sopenharmony_ci{ 6588c2ecf20Sopenharmony_ci prestera_wq = alloc_workqueue("prestera", 0, 0); 6598c2ecf20Sopenharmony_ci if (!prestera_wq) 6608c2ecf20Sopenharmony_ci return -ENOMEM; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci return 0; 6638c2ecf20Sopenharmony_ci} 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_cistatic void __exit prestera_module_exit(void) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci destroy_workqueue(prestera_wq); 6688c2ecf20Sopenharmony_ci} 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_cimodule_init(prestera_module_init); 6718c2ecf20Sopenharmony_cimodule_exit(prestera_module_exit); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 6748c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Marvell Prestera switch driver"); 675