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" 10f3cad261SSteen Hegelund 11f3cad261SSteen Hegelund /* The IFH bit position of the first VSTAX bit. This is because the 12f3cad261SSteen Hegelund * VSTAX bit positions in Data sheet is starting from zero. 13f3cad261SSteen Hegelund */ 14f3cad261SSteen Hegelund #define VSTAX 73 15f3cad261SSteen Hegelund 16*6387f65eSJakub Kicinski #define ifh_encode_bitfield(ifh, value, pos, _width) \ 17*6387f65eSJakub Kicinski ({ \ 18*6387f65eSJakub Kicinski u32 width = (_width); \ 19*6387f65eSJakub Kicinski \ 20*6387f65eSJakub Kicinski /* Max width is 5 bytes - 40 bits. In worst case this will 21*6387f65eSJakub Kicinski * spread over 6 bytes - 48 bits 22*6387f65eSJakub Kicinski */ \ 23*6387f65eSJakub Kicinski compiletime_assert(width <= 40, \ 24*6387f65eSJakub Kicinski "Unsupported width, must be <= 40"); \ 25*6387f65eSJakub Kicinski __ifh_encode_bitfield((ifh), (value), (pos), width); \ 26*6387f65eSJakub Kicinski }) 27*6387f65eSJakub Kicinski 28*6387f65eSJakub Kicinski static void __ifh_encode_bitfield(void *ifh, u64 value, u32 pos, u32 width) 29f3cad261SSteen Hegelund { 30f3cad261SSteen Hegelund u8 *ifh_hdr = ifh; 31f3cad261SSteen Hegelund /* Calculate the Start IFH byte position of this IFH bit position */ 32f3cad261SSteen Hegelund u32 byte = (35 - (pos / 8)); 33f3cad261SSteen Hegelund /* Calculate the Start bit position in the Start IFH byte */ 34f3cad261SSteen Hegelund u32 bit = (pos % 8); 35f3cad261SSteen Hegelund u64 encode = GENMASK(bit + width - 1, bit) & (value << bit); 36f3cad261SSteen Hegelund 37f3cad261SSteen Hegelund /* The b0-b7 goes into the start IFH byte */ 38f3cad261SSteen Hegelund if (encode & 0xFF) 39f3cad261SSteen Hegelund ifh_hdr[byte] |= (u8)((encode & 0xFF)); 40f3cad261SSteen Hegelund /* The b8-b15 goes into the next IFH byte */ 41f3cad261SSteen Hegelund if (encode & 0xFF00) 42f3cad261SSteen Hegelund ifh_hdr[byte - 1] |= (u8)((encode & 0xFF00) >> 8); 43f3cad261SSteen Hegelund /* The b16-b23 goes into the next IFH byte */ 44f3cad261SSteen Hegelund if (encode & 0xFF0000) 45f3cad261SSteen Hegelund ifh_hdr[byte - 2] |= (u8)((encode & 0xFF0000) >> 16); 46f3cad261SSteen Hegelund /* The b24-b31 goes into the next IFH byte */ 47f3cad261SSteen Hegelund if (encode & 0xFF000000) 48f3cad261SSteen Hegelund ifh_hdr[byte - 3] |= (u8)((encode & 0xFF000000) >> 24); 49f3cad261SSteen Hegelund /* The b32-b39 goes into the next IFH byte */ 50f3cad261SSteen Hegelund if (encode & 0xFF00000000) 51f3cad261SSteen Hegelund ifh_hdr[byte - 4] |= (u8)((encode & 0xFF00000000) >> 32); 52f3cad261SSteen Hegelund /* The b40-b47 goes into the next IFH byte */ 53f3cad261SSteen Hegelund if (encode & 0xFF0000000000) 54f3cad261SSteen Hegelund ifh_hdr[byte - 5] |= (u8)((encode & 0xFF0000000000) >> 40); 55f3cad261SSteen Hegelund } 56f3cad261SSteen Hegelund 57f3cad261SSteen Hegelund static void sparx5_set_port_ifh(void *ifh_hdr, u16 portno) 58f3cad261SSteen Hegelund { 59f3cad261SSteen Hegelund /* VSTAX.RSV = 1. MSBit must be 1 */ 60f3cad261SSteen Hegelund ifh_encode_bitfield(ifh_hdr, 1, VSTAX + 79, 1); 61f3cad261SSteen Hegelund /* VSTAX.INGR_DROP_MODE = Enable. Don't make head-of-line blocking */ 62f3cad261SSteen Hegelund ifh_encode_bitfield(ifh_hdr, 1, VSTAX + 55, 1); 63f3cad261SSteen Hegelund /* MISC.CPU_MASK/DPORT = Destination port */ 64f3cad261SSteen Hegelund ifh_encode_bitfield(ifh_hdr, portno, 29, 8); 65f3cad261SSteen Hegelund /* MISC.PIPELINE_PT */ 66f3cad261SSteen Hegelund ifh_encode_bitfield(ifh_hdr, 16, 37, 5); 67f3cad261SSteen Hegelund /* MISC.PIPELINE_ACT */ 68f3cad261SSteen Hegelund ifh_encode_bitfield(ifh_hdr, 1, 42, 3); 69f3cad261SSteen Hegelund /* FWD.SRC_PORT = CPU */ 70f3cad261SSteen Hegelund ifh_encode_bitfield(ifh_hdr, SPX5_PORT_CPU, 46, 7); 71f3cad261SSteen Hegelund /* FWD.SFLOW_ID (disable SFlow sampling) */ 72f3cad261SSteen Hegelund ifh_encode_bitfield(ifh_hdr, 124, 57, 7); 73f3cad261SSteen Hegelund /* FWD.UPDATE_FCS = Enable. Enforce update of FCS. */ 74f3cad261SSteen Hegelund ifh_encode_bitfield(ifh_hdr, 1, 67, 1); 75f3cad261SSteen Hegelund } 76f3cad261SSteen Hegelund 77f3cad261SSteen Hegelund static int sparx5_port_open(struct net_device *ndev) 78f3cad261SSteen Hegelund { 79f3cad261SSteen Hegelund struct sparx5_port *port = netdev_priv(ndev); 80f3cad261SSteen Hegelund int err = 0; 81f3cad261SSteen Hegelund 82946e7fd5SSteen Hegelund sparx5_port_enable(port, true); 83f3cad261SSteen Hegelund err = phylink_of_phy_connect(port->phylink, port->of_node, 0); 84f3cad261SSteen Hegelund if (err) { 85f3cad261SSteen Hegelund netdev_err(ndev, "Could not attach to PHY\n"); 86f3cad261SSteen Hegelund return err; 87f3cad261SSteen Hegelund } 88f3cad261SSteen Hegelund 89f3cad261SSteen Hegelund phylink_start(port->phylink); 90f3cad261SSteen Hegelund 91f3cad261SSteen Hegelund if (!ndev->phydev) { 92f3cad261SSteen Hegelund /* power up serdes */ 93f3cad261SSteen Hegelund port->conf.power_down = false; 94946e7fd5SSteen Hegelund if (port->conf.serdes_reset) 95946e7fd5SSteen Hegelund err = sparx5_serdes_set(port->sparx5, port, &port->conf); 96946e7fd5SSteen Hegelund else 97f3cad261SSteen Hegelund err = phy_power_on(port->serdes); 98f3cad261SSteen Hegelund if (err) 99f3cad261SSteen Hegelund netdev_err(ndev, "%s failed\n", __func__); 100f3cad261SSteen Hegelund } 101f3cad261SSteen Hegelund 102f3cad261SSteen Hegelund return err; 103f3cad261SSteen Hegelund } 104f3cad261SSteen Hegelund 105f3cad261SSteen Hegelund static int sparx5_port_stop(struct net_device *ndev) 106f3cad261SSteen Hegelund { 107f3cad261SSteen Hegelund struct sparx5_port *port = netdev_priv(ndev); 108f3cad261SSteen Hegelund int err = 0; 109f3cad261SSteen Hegelund 110946e7fd5SSteen Hegelund sparx5_port_enable(port, false); 111f3cad261SSteen Hegelund phylink_stop(port->phylink); 112f3cad261SSteen Hegelund phylink_disconnect_phy(port->phylink); 113f3cad261SSteen Hegelund 114f3cad261SSteen Hegelund if (!ndev->phydev) { 115946e7fd5SSteen Hegelund /* power down serdes */ 116f3cad261SSteen Hegelund port->conf.power_down = true; 117946e7fd5SSteen Hegelund if (port->conf.serdes_reset) 118946e7fd5SSteen Hegelund err = sparx5_serdes_set(port->sparx5, port, &port->conf); 119946e7fd5SSteen Hegelund else 120f3cad261SSteen Hegelund err = phy_power_off(port->serdes); 121f3cad261SSteen Hegelund if (err) 122f3cad261SSteen Hegelund netdev_err(ndev, "%s failed\n", __func__); 123f3cad261SSteen Hegelund } 124f3cad261SSteen Hegelund return 0; 125f3cad261SSteen Hegelund } 126f3cad261SSteen Hegelund 127d6fce514SSteen Hegelund static void sparx5_set_rx_mode(struct net_device *dev) 128d6fce514SSteen Hegelund { 129d6fce514SSteen Hegelund struct sparx5_port *port = netdev_priv(dev); 130d6fce514SSteen Hegelund struct sparx5 *sparx5 = port->sparx5; 131d6fce514SSteen Hegelund 132d6fce514SSteen Hegelund if (!test_bit(port->portno, sparx5->bridge_mask)) 133d6fce514SSteen Hegelund __dev_mc_sync(dev, sparx5_mc_sync, sparx5_mc_unsync); 134d6fce514SSteen Hegelund } 135d6fce514SSteen Hegelund 136f3cad261SSteen Hegelund static int sparx5_port_get_phys_port_name(struct net_device *dev, 137f3cad261SSteen Hegelund char *buf, size_t len) 138f3cad261SSteen Hegelund { 139f3cad261SSteen Hegelund struct sparx5_port *port = netdev_priv(dev); 140f3cad261SSteen Hegelund int ret; 141f3cad261SSteen Hegelund 142f3cad261SSteen Hegelund ret = snprintf(buf, len, "p%d", port->portno); 143f3cad261SSteen Hegelund if (ret >= len) 144f3cad261SSteen Hegelund return -EINVAL; 145f3cad261SSteen Hegelund 146f3cad261SSteen Hegelund return 0; 147f3cad261SSteen Hegelund } 148f3cad261SSteen Hegelund 149f3cad261SSteen Hegelund static int sparx5_set_mac_address(struct net_device *dev, void *p) 150f3cad261SSteen Hegelund { 151b37a1baeSSteen Hegelund struct sparx5_port *port = netdev_priv(dev); 152b37a1baeSSteen Hegelund struct sparx5 *sparx5 = port->sparx5; 153f3cad261SSteen Hegelund const struct sockaddr *addr = p; 154f3cad261SSteen Hegelund 155f3cad261SSteen Hegelund if (!is_valid_ether_addr(addr->sa_data)) 156f3cad261SSteen Hegelund return -EADDRNOTAVAIL; 157f3cad261SSteen Hegelund 158b37a1baeSSteen Hegelund /* Remove current */ 159b37a1baeSSteen Hegelund sparx5_mact_forget(sparx5, dev->dev_addr, port->pvid); 160b37a1baeSSteen Hegelund 161b37a1baeSSteen Hegelund /* Add new */ 162b37a1baeSSteen Hegelund sparx5_mact_learn(sparx5, PGID_CPU, addr->sa_data, port->pvid); 163b37a1baeSSteen Hegelund 164f3cad261SSteen Hegelund /* Record the address */ 165f3cad261SSteen Hegelund ether_addr_copy(dev->dev_addr, addr->sa_data); 166f3cad261SSteen Hegelund 167f3cad261SSteen Hegelund return 0; 168f3cad261SSteen Hegelund } 169f3cad261SSteen Hegelund 170b37a1baeSSteen Hegelund static int sparx5_get_port_parent_id(struct net_device *dev, 171b37a1baeSSteen Hegelund struct netdev_phys_item_id *ppid) 172b37a1baeSSteen Hegelund { 173b37a1baeSSteen Hegelund struct sparx5_port *sparx5_port = netdev_priv(dev); 174b37a1baeSSteen Hegelund struct sparx5 *sparx5 = sparx5_port->sparx5; 175b37a1baeSSteen Hegelund 176b37a1baeSSteen Hegelund ppid->id_len = sizeof(sparx5->base_mac); 177b37a1baeSSteen Hegelund memcpy(&ppid->id, &sparx5->base_mac, ppid->id_len); 178b37a1baeSSteen Hegelund 179b37a1baeSSteen Hegelund return 0; 180b37a1baeSSteen Hegelund } 181b37a1baeSSteen Hegelund 182f3cad261SSteen Hegelund static const struct net_device_ops sparx5_port_netdev_ops = { 183f3cad261SSteen Hegelund .ndo_open = sparx5_port_open, 184f3cad261SSteen Hegelund .ndo_stop = sparx5_port_stop, 185f3cad261SSteen Hegelund .ndo_start_xmit = sparx5_port_xmit_impl, 186d6fce514SSteen Hegelund .ndo_set_rx_mode = sparx5_set_rx_mode, 187f3cad261SSteen Hegelund .ndo_get_phys_port_name = sparx5_port_get_phys_port_name, 188f3cad261SSteen Hegelund .ndo_set_mac_address = sparx5_set_mac_address, 189f3cad261SSteen Hegelund .ndo_validate_addr = eth_validate_addr, 190af4b1102SSteen Hegelund .ndo_get_stats64 = sparx5_get_stats64, 191b37a1baeSSteen Hegelund .ndo_get_port_parent_id = sparx5_get_port_parent_id, 192f3cad261SSteen Hegelund }; 193f3cad261SSteen Hegelund 194f3cad261SSteen Hegelund bool sparx5_netdevice_check(const struct net_device *dev) 195f3cad261SSteen Hegelund { 196f3cad261SSteen Hegelund return dev && (dev->netdev_ops == &sparx5_port_netdev_ops); 197f3cad261SSteen Hegelund } 198f3cad261SSteen Hegelund 199f3cad261SSteen Hegelund struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno) 200f3cad261SSteen Hegelund { 201f3cad261SSteen Hegelund struct sparx5_port *spx5_port; 202f3cad261SSteen Hegelund struct net_device *ndev; 203f3cad261SSteen Hegelund u64 val; 204f3cad261SSteen Hegelund 205f3cad261SSteen Hegelund ndev = devm_alloc_etherdev(sparx5->dev, sizeof(struct sparx5_port)); 206f3cad261SSteen Hegelund if (!ndev) 207f3cad261SSteen Hegelund return ERR_PTR(-ENOMEM); 208f3cad261SSteen Hegelund 209f3cad261SSteen Hegelund SET_NETDEV_DEV(ndev, sparx5->dev); 210f3cad261SSteen Hegelund spx5_port = netdev_priv(ndev); 211f3cad261SSteen Hegelund spx5_port->ndev = ndev; 212f3cad261SSteen Hegelund spx5_port->sparx5 = sparx5; 213f3cad261SSteen Hegelund spx5_port->portno = portno; 214f3cad261SSteen Hegelund sparx5_set_port_ifh(spx5_port->ifh, portno); 215f3cad261SSteen Hegelund 216f3cad261SSteen Hegelund ndev->netdev_ops = &sparx5_port_netdev_ops; 217af4b1102SSteen Hegelund ndev->ethtool_ops = &sparx5_ethtool_ops; 218f3cad261SSteen Hegelund 219f3cad261SSteen Hegelund val = ether_addr_to_u64(sparx5->base_mac) + portno + 1; 220f3cad261SSteen Hegelund u64_to_ether_addr(val, ndev->dev_addr); 221f3cad261SSteen Hegelund 222f3cad261SSteen Hegelund return ndev; 223f3cad261SSteen Hegelund } 224f3cad261SSteen Hegelund 225f3cad261SSteen Hegelund int sparx5_register_netdevs(struct sparx5 *sparx5) 226f3cad261SSteen Hegelund { 227f3cad261SSteen Hegelund int portno; 228f3cad261SSteen Hegelund int err; 229f3cad261SSteen Hegelund 230f3cad261SSteen Hegelund for (portno = 0; portno < SPX5_PORTS; portno++) 231f3cad261SSteen Hegelund if (sparx5->ports[portno]) { 232f3cad261SSteen Hegelund err = register_netdev(sparx5->ports[portno]->ndev); 233f3cad261SSteen Hegelund if (err) { 234f3cad261SSteen Hegelund dev_err(sparx5->dev, 235f3cad261SSteen Hegelund "port: %02u: netdev registration failed\n", 236f3cad261SSteen Hegelund portno); 237f3cad261SSteen Hegelund return err; 238f3cad261SSteen Hegelund } 239f3cad261SSteen Hegelund sparx5_port_inj_timer_setup(sparx5->ports[portno]); 240f3cad261SSteen Hegelund } 241f3cad261SSteen Hegelund return 0; 242f3cad261SSteen Hegelund } 243f3cad261SSteen Hegelund 244f3cad261SSteen Hegelund void sparx5_destroy_netdevs(struct sparx5 *sparx5) 245f3cad261SSteen Hegelund { 246f3cad261SSteen Hegelund struct sparx5_port *port; 247f3cad261SSteen Hegelund int portno; 248f3cad261SSteen Hegelund 249f3cad261SSteen Hegelund for (portno = 0; portno < SPX5_PORTS; portno++) { 250f3cad261SSteen Hegelund port = sparx5->ports[portno]; 251f3cad261SSteen Hegelund if (port && port->phylink) { 252f3cad261SSteen Hegelund /* Disconnect the phy */ 253f3cad261SSteen Hegelund rtnl_lock(); 254f3cad261SSteen Hegelund sparx5_port_stop(port->ndev); 255f3cad261SSteen Hegelund phylink_disconnect_phy(port->phylink); 256f3cad261SSteen Hegelund rtnl_unlock(); 257f3cad261SSteen Hegelund phylink_destroy(port->phylink); 258f3cad261SSteen Hegelund port->phylink = NULL; 259f3cad261SSteen Hegelund } 260f3cad261SSteen Hegelund } 261f3cad261SSteen Hegelund } 262f3cad261SSteen Hegelund 263f3cad261SSteen Hegelund void sparx5_unregister_netdevs(struct sparx5 *sparx5) 264f3cad261SSteen Hegelund { 265f3cad261SSteen Hegelund int portno; 266f3cad261SSteen Hegelund 267f3cad261SSteen Hegelund for (portno = 0; portno < SPX5_PORTS; portno++) 268f3cad261SSteen Hegelund if (sparx5->ports[portno]) 269f3cad261SSteen Hegelund unregister_netdev(sparx5->ports[portno]->ndev); 270f3cad261SSteen Hegelund } 271f3cad261SSteen Hegelund 272