1f3cad261SSteen Hegelund // SPDX-License-Identifier: GPL-2.0+ 2f3cad261SSteen Hegelund /* Microchip Sparx5 Switch driver 3f3cad261SSteen Hegelund * 4f3cad261SSteen Hegelund * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries. 5f3cad261SSteen Hegelund */ 6f3cad261SSteen Hegelund 7f3cad261SSteen Hegelund #include "sparx5_main_regs.h" 8f3cad261SSteen Hegelund #include "sparx5_main.h" 9946e7fd5SSteen Hegelund #include "sparx5_port.h" 1065ec1bbeSDaniel Machon #include "sparx5_tc.h" 11f3cad261SSteen Hegelund 12f3cad261SSteen Hegelund /* The IFH bit position of the first VSTAX bit. This is because the 13f3cad261SSteen Hegelund * VSTAX bit positions in Data sheet is starting from zero. 14f3cad261SSteen Hegelund */ 15f3cad261SSteen Hegelund #define VSTAX 73 16f3cad261SSteen Hegelund 176387f65eSJakub Kicinski #define ifh_encode_bitfield(ifh, value, pos, _width) \ 186387f65eSJakub Kicinski ({ \ 196387f65eSJakub Kicinski u32 width = (_width); \ 206387f65eSJakub Kicinski \ 216387f65eSJakub Kicinski /* Max width is 5 bytes - 40 bits. In worst case this will 226387f65eSJakub Kicinski * spread over 6 bytes - 48 bits 236387f65eSJakub Kicinski */ \ 246387f65eSJakub Kicinski compiletime_assert(width <= 40, \ 256387f65eSJakub Kicinski "Unsupported width, must be <= 40"); \ 266387f65eSJakub Kicinski __ifh_encode_bitfield((ifh), (value), (pos), width); \ 276387f65eSJakub Kicinski }) 286387f65eSJakub Kicinski 296387f65eSJakub Kicinski static void __ifh_encode_bitfield(void *ifh, u64 value, u32 pos, u32 width) 30f3cad261SSteen Hegelund { 31f3cad261SSteen Hegelund u8 *ifh_hdr = ifh; 32f3cad261SSteen Hegelund /* Calculate the Start IFH byte position of this IFH bit position */ 33f3cad261SSteen Hegelund u32 byte = (35 - (pos / 8)); 34f3cad261SSteen Hegelund /* Calculate the Start bit position in the Start IFH byte */ 35f3cad261SSteen Hegelund u32 bit = (pos % 8); 36f41e57afSArnd Bergmann u64 encode = GENMASK_ULL(bit + width - 1, bit) & (value << bit); 37f3cad261SSteen Hegelund 38f3cad261SSteen Hegelund /* The b0-b7 goes into the start IFH byte */ 39f3cad261SSteen Hegelund if (encode & 0xFF) 40f3cad261SSteen Hegelund ifh_hdr[byte] |= (u8)((encode & 0xFF)); 41f3cad261SSteen Hegelund /* The b8-b15 goes into the next IFH byte */ 42f3cad261SSteen Hegelund if (encode & 0xFF00) 43f3cad261SSteen Hegelund ifh_hdr[byte - 1] |= (u8)((encode & 0xFF00) >> 8); 44f3cad261SSteen Hegelund /* The b16-b23 goes into the next IFH byte */ 45f3cad261SSteen Hegelund if (encode & 0xFF0000) 46f3cad261SSteen Hegelund ifh_hdr[byte - 2] |= (u8)((encode & 0xFF0000) >> 16); 47f3cad261SSteen Hegelund /* The b24-b31 goes into the next IFH byte */ 48f3cad261SSteen Hegelund if (encode & 0xFF000000) 49f3cad261SSteen Hegelund ifh_hdr[byte - 3] |= (u8)((encode & 0xFF000000) >> 24); 50f3cad261SSteen Hegelund /* The b32-b39 goes into the next IFH byte */ 51f3cad261SSteen Hegelund if (encode & 0xFF00000000) 52f3cad261SSteen Hegelund ifh_hdr[byte - 4] |= (u8)((encode & 0xFF00000000) >> 32); 53f3cad261SSteen Hegelund /* The b40-b47 goes into the next IFH byte */ 54f3cad261SSteen Hegelund if (encode & 0xFF0000000000) 55f3cad261SSteen Hegelund ifh_hdr[byte - 5] |= (u8)((encode & 0xFF0000000000) >> 40); 56f3cad261SSteen Hegelund } 57f3cad261SSteen Hegelund 588f68f53aSHoratiu Vultur void sparx5_set_port_ifh(void *ifh_hdr, u16 portno) 59f3cad261SSteen Hegelund { 60f3cad261SSteen Hegelund /* VSTAX.RSV = 1. MSBit must be 1 */ 61f3cad261SSteen Hegelund ifh_encode_bitfield(ifh_hdr, 1, VSTAX + 79, 1); 62f3cad261SSteen Hegelund /* VSTAX.INGR_DROP_MODE = Enable. Don't make head-of-line blocking */ 63f3cad261SSteen Hegelund ifh_encode_bitfield(ifh_hdr, 1, VSTAX + 55, 1); 64f3cad261SSteen Hegelund /* MISC.CPU_MASK/DPORT = Destination port */ 65f3cad261SSteen Hegelund ifh_encode_bitfield(ifh_hdr, portno, 29, 8); 66f3cad261SSteen Hegelund /* MISC.PIPELINE_PT */ 67f3cad261SSteen Hegelund ifh_encode_bitfield(ifh_hdr, 16, 37, 5); 68f3cad261SSteen Hegelund /* MISC.PIPELINE_ACT */ 69f3cad261SSteen Hegelund ifh_encode_bitfield(ifh_hdr, 1, 42, 3); 70f3cad261SSteen Hegelund /* FWD.SRC_PORT = CPU */ 71f3cad261SSteen Hegelund ifh_encode_bitfield(ifh_hdr, SPX5_PORT_CPU, 46, 7); 72f3cad261SSteen Hegelund /* FWD.SFLOW_ID (disable SFlow sampling) */ 73f3cad261SSteen Hegelund ifh_encode_bitfield(ifh_hdr, 124, 57, 7); 74f3cad261SSteen Hegelund /* FWD.UPDATE_FCS = Enable. Enforce update of FCS. */ 75f3cad261SSteen Hegelund ifh_encode_bitfield(ifh_hdr, 1, 67, 1); 76f3cad261SSteen Hegelund } 77f3cad261SSteen Hegelund 7870dfe25cSHoratiu Vultur void sparx5_set_port_ifh_rew_op(void *ifh_hdr, u32 rew_op) 7970dfe25cSHoratiu Vultur { 8070dfe25cSHoratiu Vultur ifh_encode_bitfield(ifh_hdr, rew_op, VSTAX + 32, 10); 8170dfe25cSHoratiu Vultur } 8270dfe25cSHoratiu Vultur 8370dfe25cSHoratiu Vultur void sparx5_set_port_ifh_pdu_type(void *ifh_hdr, u32 pdu_type) 8470dfe25cSHoratiu Vultur { 8570dfe25cSHoratiu Vultur ifh_encode_bitfield(ifh_hdr, pdu_type, 191, 4); 8670dfe25cSHoratiu Vultur } 8770dfe25cSHoratiu Vultur 8870dfe25cSHoratiu Vultur void sparx5_set_port_ifh_pdu_w16_offset(void *ifh_hdr, u32 pdu_w16_offset) 8970dfe25cSHoratiu Vultur { 9070dfe25cSHoratiu Vultur ifh_encode_bitfield(ifh_hdr, pdu_w16_offset, 195, 6); 9170dfe25cSHoratiu Vultur } 9270dfe25cSHoratiu Vultur 9370dfe25cSHoratiu Vultur void sparx5_set_port_ifh_timestamp(void *ifh_hdr, u64 timestamp) 9470dfe25cSHoratiu Vultur { 9570dfe25cSHoratiu Vultur ifh_encode_bitfield(ifh_hdr, timestamp, 232, 40); 9670dfe25cSHoratiu Vultur } 9770dfe25cSHoratiu Vultur 98f3cad261SSteen Hegelund static int sparx5_port_open(struct net_device *ndev) 99f3cad261SSteen Hegelund { 100f3cad261SSteen Hegelund struct sparx5_port *port = netdev_priv(ndev); 101f3cad261SSteen Hegelund int err = 0; 102f3cad261SSteen Hegelund 103946e7fd5SSteen Hegelund sparx5_port_enable(port, true); 104f3cad261SSteen Hegelund err = phylink_of_phy_connect(port->phylink, port->of_node, 0); 105f3cad261SSteen Hegelund if (err) { 106f3cad261SSteen Hegelund netdev_err(ndev, "Could not attach to PHY\n"); 107*4305fe23SLiu Jian goto err_connect; 108f3cad261SSteen Hegelund } 109f3cad261SSteen Hegelund 110f3cad261SSteen Hegelund phylink_start(port->phylink); 111f3cad261SSteen Hegelund 112f3cad261SSteen Hegelund if (!ndev->phydev) { 113f3cad261SSteen Hegelund /* power up serdes */ 114f3cad261SSteen Hegelund port->conf.power_down = false; 115946e7fd5SSteen Hegelund if (port->conf.serdes_reset) 116946e7fd5SSteen Hegelund err = sparx5_serdes_set(port->sparx5, port, &port->conf); 117946e7fd5SSteen Hegelund else 118f3cad261SSteen Hegelund err = phy_power_on(port->serdes); 119*4305fe23SLiu Jian if (err) { 120f3cad261SSteen Hegelund netdev_err(ndev, "%s failed\n", __func__); 121*4305fe23SLiu Jian goto out_power; 122f3cad261SSteen Hegelund } 123*4305fe23SLiu Jian } 124*4305fe23SLiu Jian 125*4305fe23SLiu Jian return 0; 126*4305fe23SLiu Jian 127*4305fe23SLiu Jian out_power: 128*4305fe23SLiu Jian phylink_stop(port->phylink); 129*4305fe23SLiu Jian phylink_disconnect_phy(port->phylink); 130*4305fe23SLiu Jian err_connect: 131*4305fe23SLiu Jian sparx5_port_enable(port, false); 132f3cad261SSteen Hegelund 133f3cad261SSteen Hegelund return err; 134f3cad261SSteen Hegelund } 135f3cad261SSteen Hegelund 136f3cad261SSteen Hegelund static int sparx5_port_stop(struct net_device *ndev) 137f3cad261SSteen Hegelund { 138f3cad261SSteen Hegelund struct sparx5_port *port = netdev_priv(ndev); 139f3cad261SSteen Hegelund int err = 0; 140f3cad261SSteen Hegelund 141946e7fd5SSteen Hegelund sparx5_port_enable(port, false); 142f3cad261SSteen Hegelund phylink_stop(port->phylink); 143f3cad261SSteen Hegelund phylink_disconnect_phy(port->phylink); 144f3cad261SSteen Hegelund 145f3cad261SSteen Hegelund if (!ndev->phydev) { 146946e7fd5SSteen Hegelund /* power down serdes */ 147f3cad261SSteen Hegelund port->conf.power_down = true; 148946e7fd5SSteen Hegelund if (port->conf.serdes_reset) 149946e7fd5SSteen Hegelund err = sparx5_serdes_set(port->sparx5, port, &port->conf); 150946e7fd5SSteen Hegelund else 151f3cad261SSteen Hegelund err = phy_power_off(port->serdes); 152f3cad261SSteen Hegelund if (err) 153f3cad261SSteen Hegelund netdev_err(ndev, "%s failed\n", __func__); 154f3cad261SSteen Hegelund } 155f3cad261SSteen Hegelund return 0; 156f3cad261SSteen Hegelund } 157f3cad261SSteen Hegelund 158d6fce514SSteen Hegelund static void sparx5_set_rx_mode(struct net_device *dev) 159d6fce514SSteen Hegelund { 160d6fce514SSteen Hegelund struct sparx5_port *port = netdev_priv(dev); 161d6fce514SSteen Hegelund struct sparx5 *sparx5 = port->sparx5; 162d6fce514SSteen Hegelund 163d6fce514SSteen Hegelund if (!test_bit(port->portno, sparx5->bridge_mask)) 164d6fce514SSteen Hegelund __dev_mc_sync(dev, sparx5_mc_sync, sparx5_mc_unsync); 165d6fce514SSteen Hegelund } 166d6fce514SSteen Hegelund 167f3cad261SSteen Hegelund static int sparx5_port_get_phys_port_name(struct net_device *dev, 168f3cad261SSteen Hegelund char *buf, size_t len) 169f3cad261SSteen Hegelund { 170f3cad261SSteen Hegelund struct sparx5_port *port = netdev_priv(dev); 171f3cad261SSteen Hegelund int ret; 172f3cad261SSteen Hegelund 173f3cad261SSteen Hegelund ret = snprintf(buf, len, "p%d", port->portno); 174f3cad261SSteen Hegelund if (ret >= len) 175f3cad261SSteen Hegelund return -EINVAL; 176f3cad261SSteen Hegelund 177f3cad261SSteen Hegelund return 0; 178f3cad261SSteen Hegelund } 179f3cad261SSteen Hegelund 180f3cad261SSteen Hegelund static int sparx5_set_mac_address(struct net_device *dev, void *p) 181f3cad261SSteen Hegelund { 182b37a1baeSSteen Hegelund struct sparx5_port *port = netdev_priv(dev); 183b37a1baeSSteen Hegelund struct sparx5 *sparx5 = port->sparx5; 184f3cad261SSteen Hegelund const struct sockaddr *addr = p; 185f3cad261SSteen Hegelund 186f3cad261SSteen Hegelund if (!is_valid_ether_addr(addr->sa_data)) 187f3cad261SSteen Hegelund return -EADDRNOTAVAIL; 188f3cad261SSteen Hegelund 189b37a1baeSSteen Hegelund /* Remove current */ 190b37a1baeSSteen Hegelund sparx5_mact_forget(sparx5, dev->dev_addr, port->pvid); 191b37a1baeSSteen Hegelund 192b37a1baeSSteen Hegelund /* Add new */ 193b37a1baeSSteen Hegelund sparx5_mact_learn(sparx5, PGID_CPU, addr->sa_data, port->pvid); 194b37a1baeSSteen Hegelund 195f3cad261SSteen Hegelund /* Record the address */ 196f3956ebbSJakub Kicinski eth_hw_addr_set(dev, addr->sa_data); 197f3cad261SSteen Hegelund 198f3cad261SSteen Hegelund return 0; 199f3cad261SSteen Hegelund } 200f3cad261SSteen Hegelund 201b37a1baeSSteen Hegelund static int sparx5_get_port_parent_id(struct net_device *dev, 202b37a1baeSSteen Hegelund struct netdev_phys_item_id *ppid) 203b37a1baeSSteen Hegelund { 204b37a1baeSSteen Hegelund struct sparx5_port *sparx5_port = netdev_priv(dev); 205b37a1baeSSteen Hegelund struct sparx5 *sparx5 = sparx5_port->sparx5; 206b37a1baeSSteen Hegelund 207b37a1baeSSteen Hegelund ppid->id_len = sizeof(sparx5->base_mac); 208b37a1baeSSteen Hegelund memcpy(&ppid->id, &sparx5->base_mac, ppid->id_len); 209b37a1baeSSteen Hegelund 210b37a1baeSSteen Hegelund return 0; 211b37a1baeSSteen Hegelund } 212b37a1baeSSteen Hegelund 213589a07b8SHoratiu Vultur static int sparx5_port_ioctl(struct net_device *dev, struct ifreq *ifr, 214589a07b8SHoratiu Vultur int cmd) 215589a07b8SHoratiu Vultur { 216589a07b8SHoratiu Vultur struct sparx5_port *sparx5_port = netdev_priv(dev); 217589a07b8SHoratiu Vultur struct sparx5 *sparx5 = sparx5_port->sparx5; 218589a07b8SHoratiu Vultur 219589a07b8SHoratiu Vultur if (!phy_has_hwtstamp(dev->phydev) && sparx5->ptp) { 220589a07b8SHoratiu Vultur switch (cmd) { 221589a07b8SHoratiu Vultur case SIOCSHWTSTAMP: 222589a07b8SHoratiu Vultur return sparx5_ptp_hwtstamp_set(sparx5_port, ifr); 223589a07b8SHoratiu Vultur case SIOCGHWTSTAMP: 224589a07b8SHoratiu Vultur return sparx5_ptp_hwtstamp_get(sparx5_port, ifr); 225589a07b8SHoratiu Vultur } 226589a07b8SHoratiu Vultur } 227589a07b8SHoratiu Vultur 228589a07b8SHoratiu Vultur return phy_mii_ioctl(dev->phydev, ifr, cmd); 229589a07b8SHoratiu Vultur } 230589a07b8SHoratiu Vultur 231f3cad261SSteen Hegelund static const struct net_device_ops sparx5_port_netdev_ops = { 232f3cad261SSteen Hegelund .ndo_open = sparx5_port_open, 233f3cad261SSteen Hegelund .ndo_stop = sparx5_port_stop, 234f3cad261SSteen Hegelund .ndo_start_xmit = sparx5_port_xmit_impl, 235d6fce514SSteen Hegelund .ndo_set_rx_mode = sparx5_set_rx_mode, 236f3cad261SSteen Hegelund .ndo_get_phys_port_name = sparx5_port_get_phys_port_name, 237f3cad261SSteen Hegelund .ndo_set_mac_address = sparx5_set_mac_address, 238f3cad261SSteen Hegelund .ndo_validate_addr = eth_validate_addr, 239af4b1102SSteen Hegelund .ndo_get_stats64 = sparx5_get_stats64, 240b37a1baeSSteen Hegelund .ndo_get_port_parent_id = sparx5_get_port_parent_id, 241589a07b8SHoratiu Vultur .ndo_eth_ioctl = sparx5_port_ioctl, 24265ec1bbeSDaniel Machon .ndo_setup_tc = sparx5_port_setup_tc, 243f3cad261SSteen Hegelund }; 244f3cad261SSteen Hegelund 245f3cad261SSteen Hegelund bool sparx5_netdevice_check(const struct net_device *dev) 246f3cad261SSteen Hegelund { 247f3cad261SSteen Hegelund return dev && (dev->netdev_ops == &sparx5_port_netdev_ops); 248f3cad261SSteen Hegelund } 249f3cad261SSteen Hegelund 250f3cad261SSteen Hegelund struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno) 251f3cad261SSteen Hegelund { 252f3cad261SSteen Hegelund struct sparx5_port *spx5_port; 253f3cad261SSteen Hegelund struct net_device *ndev; 254f3cad261SSteen Hegelund 255ab0e493eSDaniel Machon ndev = devm_alloc_etherdev_mqs(sparx5->dev, sizeof(struct sparx5_port), 256ab0e493eSDaniel Machon SPX5_PRIOS, 1); 257f3cad261SSteen Hegelund if (!ndev) 258f3cad261SSteen Hegelund return ERR_PTR(-ENOMEM); 259f3cad261SSteen Hegelund 260ab0e493eSDaniel Machon ndev->hw_features |= NETIF_F_HW_TC; 261ab0e493eSDaniel Machon ndev->features |= NETIF_F_HW_TC; 262ab0e493eSDaniel Machon 263f3cad261SSteen Hegelund SET_NETDEV_DEV(ndev, sparx5->dev); 264f3cad261SSteen Hegelund spx5_port = netdev_priv(ndev); 265f3cad261SSteen Hegelund spx5_port->ndev = ndev; 266f3cad261SSteen Hegelund spx5_port->sparx5 = sparx5; 267f3cad261SSteen Hegelund spx5_port->portno = portno; 268f3cad261SSteen Hegelund 269f3cad261SSteen Hegelund ndev->netdev_ops = &sparx5_port_netdev_ops; 270af4b1102SSteen Hegelund ndev->ethtool_ops = &sparx5_ethtool_ops; 271f3cad261SSteen Hegelund 27207a7ec9bSJakub Kicinski eth_hw_addr_gen(ndev, sparx5->base_mac, portno + 1); 273f3cad261SSteen Hegelund 274f3cad261SSteen Hegelund return ndev; 275f3cad261SSteen Hegelund } 276f3cad261SSteen Hegelund 277f3cad261SSteen Hegelund int sparx5_register_netdevs(struct sparx5 *sparx5) 278f3cad261SSteen Hegelund { 279f3cad261SSteen Hegelund int portno; 280f3cad261SSteen Hegelund int err; 281f3cad261SSteen Hegelund 282f3cad261SSteen Hegelund for (portno = 0; portno < SPX5_PORTS; portno++) 283f3cad261SSteen Hegelund if (sparx5->ports[portno]) { 284f3cad261SSteen Hegelund err = register_netdev(sparx5->ports[portno]->ndev); 285f3cad261SSteen Hegelund if (err) { 286f3cad261SSteen Hegelund dev_err(sparx5->dev, 287f3cad261SSteen Hegelund "port: %02u: netdev registration failed\n", 288f3cad261SSteen Hegelund portno); 289f3cad261SSteen Hegelund return err; 290f3cad261SSteen Hegelund } 291f3cad261SSteen Hegelund sparx5_port_inj_timer_setup(sparx5->ports[portno]); 292f3cad261SSteen Hegelund } 293f3cad261SSteen Hegelund return 0; 294f3cad261SSteen Hegelund } 295f3cad261SSteen Hegelund 296f3cad261SSteen Hegelund void sparx5_destroy_netdevs(struct sparx5 *sparx5) 297f3cad261SSteen Hegelund { 298f3cad261SSteen Hegelund struct sparx5_port *port; 299f3cad261SSteen Hegelund int portno; 300f3cad261SSteen Hegelund 301f3cad261SSteen Hegelund for (portno = 0; portno < SPX5_PORTS; portno++) { 302f3cad261SSteen Hegelund port = sparx5->ports[portno]; 303f3cad261SSteen Hegelund if (port && port->phylink) { 304f3cad261SSteen Hegelund /* Disconnect the phy */ 305f3cad261SSteen Hegelund rtnl_lock(); 306f3cad261SSteen Hegelund sparx5_port_stop(port->ndev); 307f3cad261SSteen Hegelund phylink_disconnect_phy(port->phylink); 308f3cad261SSteen Hegelund rtnl_unlock(); 309f3cad261SSteen Hegelund phylink_destroy(port->phylink); 310f3cad261SSteen Hegelund port->phylink = NULL; 311f3cad261SSteen Hegelund } 312f3cad261SSteen Hegelund } 313f3cad261SSteen Hegelund } 314f3cad261SSteen Hegelund 315f3cad261SSteen Hegelund void sparx5_unregister_netdevs(struct sparx5 *sparx5) 316f3cad261SSteen Hegelund { 317f3cad261SSteen Hegelund int portno; 318f3cad261SSteen Hegelund 319f3cad261SSteen Hegelund for (portno = 0; portno < SPX5_PORTS; portno++) 320f3cad261SSteen Hegelund if (sparx5->ports[portno]) 321f3cad261SSteen Hegelund unregister_netdev(sparx5->ports[portno]->ndev); 322f3cad261SSteen Hegelund } 323f3cad261SSteen Hegelund 324