162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <linux/module.h> 462306a36Sopenharmony_ci#include <linux/if_bridge.h> 562306a36Sopenharmony_ci#include <linux/if_vlan.h> 662306a36Sopenharmony_ci#include <linux/iopoll.h> 762306a36Sopenharmony_ci#include <linux/ip.h> 862306a36Sopenharmony_ci#include <linux/of.h> 962306a36Sopenharmony_ci#include <linux/of_net.h> 1062306a36Sopenharmony_ci#include <linux/phy/phy.h> 1162306a36Sopenharmony_ci#include <linux/platform_device.h> 1262306a36Sopenharmony_ci#include <linux/reset.h> 1362306a36Sopenharmony_ci#include <net/addrconf.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "lan966x_main.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define XTR_EOF_0 0x00000080U 1862306a36Sopenharmony_ci#define XTR_EOF_1 0x01000080U 1962306a36Sopenharmony_ci#define XTR_EOF_2 0x02000080U 2062306a36Sopenharmony_ci#define XTR_EOF_3 0x03000080U 2162306a36Sopenharmony_ci#define XTR_PRUNED 0x04000080U 2262306a36Sopenharmony_ci#define XTR_ABORT 0x05000080U 2362306a36Sopenharmony_ci#define XTR_ESCAPE 0x06000080U 2462306a36Sopenharmony_ci#define XTR_NOT_READY 0x07000080U 2562306a36Sopenharmony_ci#define XTR_VALID_BYTES(x) (4 - (((x) >> 24) & 3)) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define IO_RANGES 2 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic const struct of_device_id lan966x_match[] = { 3062306a36Sopenharmony_ci { .compatible = "microchip,lan966x-switch" }, 3162306a36Sopenharmony_ci { } 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, lan966x_match); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct lan966x_main_io_resource { 3662306a36Sopenharmony_ci enum lan966x_target id; 3762306a36Sopenharmony_ci phys_addr_t offset; 3862306a36Sopenharmony_ci int range; 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic const struct lan966x_main_io_resource lan966x_main_iomap[] = { 4262306a36Sopenharmony_ci { TARGET_CPU, 0xc0000, 0 }, /* 0xe00c0000 */ 4362306a36Sopenharmony_ci { TARGET_FDMA, 0xc0400, 0 }, /* 0xe00c0400 */ 4462306a36Sopenharmony_ci { TARGET_ORG, 0, 1 }, /* 0xe2000000 */ 4562306a36Sopenharmony_ci { TARGET_GCB, 0x4000, 1 }, /* 0xe2004000 */ 4662306a36Sopenharmony_ci { TARGET_QS, 0x8000, 1 }, /* 0xe2008000 */ 4762306a36Sopenharmony_ci { TARGET_PTP, 0xc000, 1 }, /* 0xe200c000 */ 4862306a36Sopenharmony_ci { TARGET_CHIP_TOP, 0x10000, 1 }, /* 0xe2010000 */ 4962306a36Sopenharmony_ci { TARGET_REW, 0x14000, 1 }, /* 0xe2014000 */ 5062306a36Sopenharmony_ci { TARGET_VCAP, 0x18000, 1 }, /* 0xe2018000 */ 5162306a36Sopenharmony_ci { TARGET_VCAP + 1, 0x20000, 1 }, /* 0xe2020000 */ 5262306a36Sopenharmony_ci { TARGET_VCAP + 2, 0x24000, 1 }, /* 0xe2024000 */ 5362306a36Sopenharmony_ci { TARGET_SYS, 0x28000, 1 }, /* 0xe2028000 */ 5462306a36Sopenharmony_ci { TARGET_DEV, 0x34000, 1 }, /* 0xe2034000 */ 5562306a36Sopenharmony_ci { TARGET_DEV + 1, 0x38000, 1 }, /* 0xe2038000 */ 5662306a36Sopenharmony_ci { TARGET_DEV + 2, 0x3c000, 1 }, /* 0xe203c000 */ 5762306a36Sopenharmony_ci { TARGET_DEV + 3, 0x40000, 1 }, /* 0xe2040000 */ 5862306a36Sopenharmony_ci { TARGET_DEV + 4, 0x44000, 1 }, /* 0xe2044000 */ 5962306a36Sopenharmony_ci { TARGET_DEV + 5, 0x48000, 1 }, /* 0xe2048000 */ 6062306a36Sopenharmony_ci { TARGET_DEV + 6, 0x4c000, 1 }, /* 0xe204c000 */ 6162306a36Sopenharmony_ci { TARGET_DEV + 7, 0x50000, 1 }, /* 0xe2050000 */ 6262306a36Sopenharmony_ci { TARGET_QSYS, 0x100000, 1 }, /* 0xe2100000 */ 6362306a36Sopenharmony_ci { TARGET_AFI, 0x120000, 1 }, /* 0xe2120000 */ 6462306a36Sopenharmony_ci { TARGET_ANA, 0x140000, 1 }, /* 0xe2140000 */ 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic int lan966x_create_targets(struct platform_device *pdev, 6862306a36Sopenharmony_ci struct lan966x *lan966x) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct resource *iores[IO_RANGES]; 7162306a36Sopenharmony_ci void __iomem *begin[IO_RANGES]; 7262306a36Sopenharmony_ci int idx; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* Initially map the entire range and after that update each target to 7562306a36Sopenharmony_ci * point inside the region at the correct offset. It is possible that 7662306a36Sopenharmony_ci * other devices access the same region so don't add any checks about 7762306a36Sopenharmony_ci * this. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_ci for (idx = 0; idx < IO_RANGES; idx++) { 8062306a36Sopenharmony_ci iores[idx] = platform_get_resource(pdev, IORESOURCE_MEM, 8162306a36Sopenharmony_ci idx); 8262306a36Sopenharmony_ci if (!iores[idx]) { 8362306a36Sopenharmony_ci dev_err(&pdev->dev, "Invalid resource\n"); 8462306a36Sopenharmony_ci return -EINVAL; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci begin[idx] = devm_ioremap(&pdev->dev, 8862306a36Sopenharmony_ci iores[idx]->start, 8962306a36Sopenharmony_ci resource_size(iores[idx])); 9062306a36Sopenharmony_ci if (!begin[idx]) { 9162306a36Sopenharmony_ci dev_err(&pdev->dev, "Unable to get registers: %s\n", 9262306a36Sopenharmony_ci iores[idx]->name); 9362306a36Sopenharmony_ci return -ENOMEM; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(lan966x_main_iomap); idx++) { 9862306a36Sopenharmony_ci const struct lan966x_main_io_resource *iomap = 9962306a36Sopenharmony_ci &lan966x_main_iomap[idx]; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci lan966x->regs[iomap->id] = begin[iomap->range] + iomap->offset; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return 0; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic bool lan966x_port_unique_address(struct net_device *dev) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct lan966x_port *port = netdev_priv(dev); 11062306a36Sopenharmony_ci struct lan966x *lan966x = port->lan966x; 11162306a36Sopenharmony_ci int p; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci for (p = 0; p < lan966x->num_phys_ports; ++p) { 11462306a36Sopenharmony_ci port = lan966x->ports[p]; 11562306a36Sopenharmony_ci if (!port || port->dev == dev) 11662306a36Sopenharmony_ci continue; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (ether_addr_equal(dev->dev_addr, port->dev->dev_addr)) 11962306a36Sopenharmony_ci return false; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return true; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic int lan966x_port_set_mac_address(struct net_device *dev, void *p) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct lan966x_port *port = netdev_priv(dev); 12862306a36Sopenharmony_ci struct lan966x *lan966x = port->lan966x; 12962306a36Sopenharmony_ci const struct sockaddr *addr = p; 13062306a36Sopenharmony_ci int ret; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (ether_addr_equal(addr->sa_data, dev->dev_addr)) 13362306a36Sopenharmony_ci return 0; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* Learn the new net device MAC address in the mac table. */ 13662306a36Sopenharmony_ci ret = lan966x_mac_cpu_learn(lan966x, addr->sa_data, HOST_PVID); 13762306a36Sopenharmony_ci if (ret) 13862306a36Sopenharmony_ci return ret; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* If there is another port with the same address as the dev, then don't 14162306a36Sopenharmony_ci * delete it from the MAC table 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_ci if (!lan966x_port_unique_address(dev)) 14462306a36Sopenharmony_ci goto out; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* Then forget the previous one. */ 14762306a36Sopenharmony_ci ret = lan966x_mac_cpu_forget(lan966x, dev->dev_addr, HOST_PVID); 14862306a36Sopenharmony_ci if (ret) 14962306a36Sopenharmony_ci return ret; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ciout: 15262306a36Sopenharmony_ci eth_hw_addr_set(dev, addr->sa_data); 15362306a36Sopenharmony_ci return ret; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic int lan966x_port_get_phys_port_name(struct net_device *dev, 15762306a36Sopenharmony_ci char *buf, size_t len) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct lan966x_port *port = netdev_priv(dev); 16062306a36Sopenharmony_ci int ret; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci ret = snprintf(buf, len, "p%d", port->chip_port); 16362306a36Sopenharmony_ci if (ret >= len) 16462306a36Sopenharmony_ci return -EINVAL; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return 0; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic int lan966x_port_open(struct net_device *dev) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct lan966x_port *port = netdev_priv(dev); 17262306a36Sopenharmony_ci struct lan966x *lan966x = port->lan966x; 17362306a36Sopenharmony_ci int err; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* Enable receiving frames on the port, and activate auto-learning of 17662306a36Sopenharmony_ci * MAC addresses. 17762306a36Sopenharmony_ci */ 17862306a36Sopenharmony_ci lan_rmw(ANA_PORT_CFG_LEARNAUTO_SET(1) | 17962306a36Sopenharmony_ci ANA_PORT_CFG_RECV_ENA_SET(1) | 18062306a36Sopenharmony_ci ANA_PORT_CFG_PORTID_VAL_SET(port->chip_port), 18162306a36Sopenharmony_ci ANA_PORT_CFG_LEARNAUTO | 18262306a36Sopenharmony_ci ANA_PORT_CFG_RECV_ENA | 18362306a36Sopenharmony_ci ANA_PORT_CFG_PORTID_VAL, 18462306a36Sopenharmony_ci lan966x, ANA_PORT_CFG(port->chip_port)); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci err = phylink_fwnode_phy_connect(port->phylink, port->fwnode, 0); 18762306a36Sopenharmony_ci if (err) { 18862306a36Sopenharmony_ci netdev_err(dev, "Could not attach to PHY\n"); 18962306a36Sopenharmony_ci return err; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci phylink_start(port->phylink); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return 0; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic int lan966x_port_stop(struct net_device *dev) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct lan966x_port *port = netdev_priv(dev); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci lan966x_port_config_down(port); 20262306a36Sopenharmony_ci phylink_stop(port->phylink); 20362306a36Sopenharmony_ci phylink_disconnect_phy(port->phylink); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return 0; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic int lan966x_port_inj_status(struct lan966x *lan966x) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci return lan_rd(lan966x, QS_INJ_STATUS); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic int lan966x_port_inj_ready(struct lan966x *lan966x, u8 grp) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci u32 val; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (lan_rd(lan966x, QS_INJ_STATUS) & QS_INJ_STATUS_FIFO_RDY_SET(BIT(grp))) 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci return readx_poll_timeout_atomic(lan966x_port_inj_status, lan966x, val, 22162306a36Sopenharmony_ci QS_INJ_STATUS_FIFO_RDY_GET(val) & BIT(grp), 22262306a36Sopenharmony_ci READL_SLEEP_US, READL_TIMEOUT_US); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic int lan966x_port_ifh_xmit(struct sk_buff *skb, 22662306a36Sopenharmony_ci __be32 *ifh, 22762306a36Sopenharmony_ci struct net_device *dev) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci struct lan966x_port *port = netdev_priv(dev); 23062306a36Sopenharmony_ci struct lan966x *lan966x = port->lan966x; 23162306a36Sopenharmony_ci u32 i, count, last; 23262306a36Sopenharmony_ci u8 grp = 0; 23362306a36Sopenharmony_ci u32 val; 23462306a36Sopenharmony_ci int err; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci val = lan_rd(lan966x, QS_INJ_STATUS); 23762306a36Sopenharmony_ci if (!(QS_INJ_STATUS_FIFO_RDY_GET(val) & BIT(grp)) || 23862306a36Sopenharmony_ci (QS_INJ_STATUS_WMARK_REACHED_GET(val) & BIT(grp))) 23962306a36Sopenharmony_ci goto err; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* Write start of frame */ 24262306a36Sopenharmony_ci lan_wr(QS_INJ_CTRL_GAP_SIZE_SET(1) | 24362306a36Sopenharmony_ci QS_INJ_CTRL_SOF_SET(1), 24462306a36Sopenharmony_ci lan966x, QS_INJ_CTRL(grp)); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* Write IFH header */ 24762306a36Sopenharmony_ci for (i = 0; i < IFH_LEN; ++i) { 24862306a36Sopenharmony_ci /* Wait until the fifo is ready */ 24962306a36Sopenharmony_ci err = lan966x_port_inj_ready(lan966x, grp); 25062306a36Sopenharmony_ci if (err) 25162306a36Sopenharmony_ci goto err; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci lan_wr((__force u32)ifh[i], lan966x, QS_INJ_WR(grp)); 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* Write frame */ 25762306a36Sopenharmony_ci count = DIV_ROUND_UP(skb->len, 4); 25862306a36Sopenharmony_ci last = skb->len % 4; 25962306a36Sopenharmony_ci for (i = 0; i < count; ++i) { 26062306a36Sopenharmony_ci /* Wait until the fifo is ready */ 26162306a36Sopenharmony_ci err = lan966x_port_inj_ready(lan966x, grp); 26262306a36Sopenharmony_ci if (err) 26362306a36Sopenharmony_ci goto err; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci lan_wr(((u32 *)skb->data)[i], lan966x, QS_INJ_WR(grp)); 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* Add padding */ 26962306a36Sopenharmony_ci while (i < (LAN966X_BUFFER_MIN_SZ / 4)) { 27062306a36Sopenharmony_ci /* Wait until the fifo is ready */ 27162306a36Sopenharmony_ci err = lan966x_port_inj_ready(lan966x, grp); 27262306a36Sopenharmony_ci if (err) 27362306a36Sopenharmony_ci goto err; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci lan_wr(0, lan966x, QS_INJ_WR(grp)); 27662306a36Sopenharmony_ci ++i; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* Inidcate EOF and valid bytes in the last word */ 28062306a36Sopenharmony_ci lan_wr(QS_INJ_CTRL_GAP_SIZE_SET(1) | 28162306a36Sopenharmony_ci QS_INJ_CTRL_VLD_BYTES_SET(skb->len < LAN966X_BUFFER_MIN_SZ ? 28262306a36Sopenharmony_ci 0 : last) | 28362306a36Sopenharmony_ci QS_INJ_CTRL_EOF_SET(1), 28462306a36Sopenharmony_ci lan966x, QS_INJ_CTRL(grp)); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* Add dummy CRC */ 28762306a36Sopenharmony_ci lan_wr(0, lan966x, QS_INJ_WR(grp)); 28862306a36Sopenharmony_ci skb_tx_timestamp(skb); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci dev->stats.tx_packets++; 29162306a36Sopenharmony_ci dev->stats.tx_bytes += skb->len; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && 29462306a36Sopenharmony_ci LAN966X_SKB_CB(skb)->rew_op == IFH_REW_OP_TWO_STEP_PTP) 29562306a36Sopenharmony_ci return NETDEV_TX_OK; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci dev_consume_skb_any(skb); 29862306a36Sopenharmony_ci return NETDEV_TX_OK; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cierr: 30162306a36Sopenharmony_ci if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && 30262306a36Sopenharmony_ci LAN966X_SKB_CB(skb)->rew_op == IFH_REW_OP_TWO_STEP_PTP) 30362306a36Sopenharmony_ci lan966x_ptp_txtstamp_release(port, skb); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci return NETDEV_TX_BUSY; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic void lan966x_ifh_set(u8 *ifh, size_t val, size_t pos, size_t length) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci int i = 0; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci do { 31362306a36Sopenharmony_ci u8 p = IFH_LEN_BYTES - (pos + i) / 8 - 1; 31462306a36Sopenharmony_ci u8 v = val >> i & 0xff; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* There is no need to check for limits of the array, as these 31762306a36Sopenharmony_ci * will never be written 31862306a36Sopenharmony_ci */ 31962306a36Sopenharmony_ci ifh[p] |= v << ((pos + i) % 8); 32062306a36Sopenharmony_ci ifh[p - 1] |= v >> (8 - (pos + i) % 8); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci i += 8; 32362306a36Sopenharmony_ci } while (i < length); 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_civoid lan966x_ifh_set_bypass(void *ifh, u64 bypass) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci lan966x_ifh_set(ifh, bypass, IFH_POS_BYPASS, IFH_WID_BYPASS); 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_civoid lan966x_ifh_set_port(void *ifh, u64 port) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci lan966x_ifh_set(ifh, port, IFH_POS_DSTS, IFH_WID_DSTS); 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic void lan966x_ifh_set_qos_class(void *ifh, u64 qos) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci lan966x_ifh_set(ifh, qos, IFH_POS_QOS_CLASS, IFH_WID_QOS_CLASS); 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic void lan966x_ifh_set_ipv(void *ifh, u64 ipv) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci lan966x_ifh_set(ifh, ipv, IFH_POS_IPV, IFH_WID_IPV); 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic void lan966x_ifh_set_vid(void *ifh, u64 vid) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci lan966x_ifh_set(ifh, vid, IFH_POS_TCI, IFH_WID_TCI); 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic void lan966x_ifh_set_rew_op(void *ifh, u64 rew_op) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci lan966x_ifh_set(ifh, rew_op, IFH_POS_REW_CMD, IFH_WID_REW_CMD); 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic void lan966x_ifh_set_timestamp(void *ifh, u64 timestamp) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci lan966x_ifh_set(ifh, timestamp, IFH_POS_TIMESTAMP, IFH_WID_TIMESTAMP); 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic netdev_tx_t lan966x_port_xmit(struct sk_buff *skb, 36262306a36Sopenharmony_ci struct net_device *dev) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci struct lan966x_port *port = netdev_priv(dev); 36562306a36Sopenharmony_ci struct lan966x *lan966x = port->lan966x; 36662306a36Sopenharmony_ci __be32 ifh[IFH_LEN]; 36762306a36Sopenharmony_ci int err; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci memset(ifh, 0x0, sizeof(__be32) * IFH_LEN); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci lan966x_ifh_set_bypass(ifh, 1); 37262306a36Sopenharmony_ci lan966x_ifh_set_port(ifh, BIT_ULL(port->chip_port)); 37362306a36Sopenharmony_ci lan966x_ifh_set_qos_class(ifh, skb->priority >= 7 ? 0x7 : skb->priority); 37462306a36Sopenharmony_ci lan966x_ifh_set_ipv(ifh, skb->priority >= 7 ? 0x7 : skb->priority); 37562306a36Sopenharmony_ci lan966x_ifh_set_vid(ifh, skb_vlan_tag_get(skb)); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (port->lan966x->ptp && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) { 37862306a36Sopenharmony_ci err = lan966x_ptp_txtstamp_request(port, skb); 37962306a36Sopenharmony_ci if (err) 38062306a36Sopenharmony_ci return err; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci lan966x_ifh_set_rew_op(ifh, LAN966X_SKB_CB(skb)->rew_op); 38362306a36Sopenharmony_ci lan966x_ifh_set_timestamp(ifh, LAN966X_SKB_CB(skb)->ts_id); 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci spin_lock(&lan966x->tx_lock); 38762306a36Sopenharmony_ci if (port->lan966x->fdma) 38862306a36Sopenharmony_ci err = lan966x_fdma_xmit(skb, ifh, dev); 38962306a36Sopenharmony_ci else 39062306a36Sopenharmony_ci err = lan966x_port_ifh_xmit(skb, ifh, dev); 39162306a36Sopenharmony_ci spin_unlock(&lan966x->tx_lock); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci return err; 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic int lan966x_port_change_mtu(struct net_device *dev, int new_mtu) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci struct lan966x_port *port = netdev_priv(dev); 39962306a36Sopenharmony_ci struct lan966x *lan966x = port->lan966x; 40062306a36Sopenharmony_ci int old_mtu = dev->mtu; 40162306a36Sopenharmony_ci int err; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci lan_wr(DEV_MAC_MAXLEN_CFG_MAX_LEN_SET(LAN966X_HW_MTU(new_mtu)), 40462306a36Sopenharmony_ci lan966x, DEV_MAC_MAXLEN_CFG(port->chip_port)); 40562306a36Sopenharmony_ci dev->mtu = new_mtu; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (!lan966x->fdma) 40862306a36Sopenharmony_ci return 0; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci err = lan966x_fdma_change_mtu(lan966x); 41162306a36Sopenharmony_ci if (err) { 41262306a36Sopenharmony_ci lan_wr(DEV_MAC_MAXLEN_CFG_MAX_LEN_SET(LAN966X_HW_MTU(old_mtu)), 41362306a36Sopenharmony_ci lan966x, DEV_MAC_MAXLEN_CFG(port->chip_port)); 41462306a36Sopenharmony_ci dev->mtu = old_mtu; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci return err; 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic int lan966x_mc_unsync(struct net_device *dev, const unsigned char *addr) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci struct lan966x_port *port = netdev_priv(dev); 42362306a36Sopenharmony_ci struct lan966x *lan966x = port->lan966x; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci return lan966x_mac_forget(lan966x, addr, HOST_PVID, ENTRYTYPE_LOCKED); 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic int lan966x_mc_sync(struct net_device *dev, const unsigned char *addr) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct lan966x_port *port = netdev_priv(dev); 43162306a36Sopenharmony_ci struct lan966x *lan966x = port->lan966x; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci return lan966x_mac_cpu_learn(lan966x, addr, HOST_PVID); 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic void lan966x_port_set_rx_mode(struct net_device *dev) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci __dev_mc_sync(dev, lan966x_mc_sync, lan966x_mc_unsync); 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cistatic int lan966x_port_get_parent_id(struct net_device *dev, 44262306a36Sopenharmony_ci struct netdev_phys_item_id *ppid) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci struct lan966x_port *port = netdev_priv(dev); 44562306a36Sopenharmony_ci struct lan966x *lan966x = port->lan966x; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci ppid->id_len = sizeof(lan966x->base_mac); 44862306a36Sopenharmony_ci memcpy(&ppid->id, &lan966x->base_mac, ppid->id_len); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci return 0; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic int lan966x_port_hwtstamp_get(struct net_device *dev, 45462306a36Sopenharmony_ci struct kernel_hwtstamp_config *cfg) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct lan966x_port *port = netdev_priv(dev); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (!port->lan966x->ptp) 45962306a36Sopenharmony_ci return -EOPNOTSUPP; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci lan966x_ptp_hwtstamp_get(port, cfg); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic int lan966x_port_hwtstamp_set(struct net_device *dev, 46762306a36Sopenharmony_ci struct kernel_hwtstamp_config *cfg, 46862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci struct lan966x_port *port = netdev_priv(dev); 47162306a36Sopenharmony_ci int err; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (cfg->source != HWTSTAMP_SOURCE_NETDEV && 47462306a36Sopenharmony_ci cfg->source != HWTSTAMP_SOURCE_PHYLIB) 47562306a36Sopenharmony_ci return -EOPNOTSUPP; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci err = lan966x_ptp_setup_traps(port, cfg); 47862306a36Sopenharmony_ci if (err) 47962306a36Sopenharmony_ci return err; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (cfg->source == HWTSTAMP_SOURCE_NETDEV) { 48262306a36Sopenharmony_ci if (!port->lan966x->ptp) 48362306a36Sopenharmony_ci return -EOPNOTSUPP; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci err = lan966x_ptp_hwtstamp_set(port, cfg, extack); 48662306a36Sopenharmony_ci if (err) { 48762306a36Sopenharmony_ci lan966x_ptp_del_traps(port); 48862306a36Sopenharmony_ci return err; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci return 0; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic const struct net_device_ops lan966x_port_netdev_ops = { 49662306a36Sopenharmony_ci .ndo_open = lan966x_port_open, 49762306a36Sopenharmony_ci .ndo_stop = lan966x_port_stop, 49862306a36Sopenharmony_ci .ndo_start_xmit = lan966x_port_xmit, 49962306a36Sopenharmony_ci .ndo_change_mtu = lan966x_port_change_mtu, 50062306a36Sopenharmony_ci .ndo_set_rx_mode = lan966x_port_set_rx_mode, 50162306a36Sopenharmony_ci .ndo_get_phys_port_name = lan966x_port_get_phys_port_name, 50262306a36Sopenharmony_ci .ndo_get_stats64 = lan966x_stats_get, 50362306a36Sopenharmony_ci .ndo_set_mac_address = lan966x_port_set_mac_address, 50462306a36Sopenharmony_ci .ndo_get_port_parent_id = lan966x_port_get_parent_id, 50562306a36Sopenharmony_ci .ndo_eth_ioctl = phy_do_ioctl, 50662306a36Sopenharmony_ci .ndo_setup_tc = lan966x_tc_setup, 50762306a36Sopenharmony_ci .ndo_bpf = lan966x_xdp, 50862306a36Sopenharmony_ci .ndo_xdp_xmit = lan966x_xdp_xmit, 50962306a36Sopenharmony_ci .ndo_hwtstamp_get = lan966x_port_hwtstamp_get, 51062306a36Sopenharmony_ci .ndo_hwtstamp_set = lan966x_port_hwtstamp_set, 51162306a36Sopenharmony_ci}; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cibool lan966x_netdevice_check(const struct net_device *dev) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci return dev->netdev_ops == &lan966x_port_netdev_ops; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cibool lan966x_hw_offload(struct lan966x *lan966x, u32 port, struct sk_buff *skb) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci u32 val; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* The IGMP and MLD frames are not forward by the HW if 52362306a36Sopenharmony_ci * multicast snooping is enabled, therefor don't mark as 52462306a36Sopenharmony_ci * offload to allow the SW to forward the frames accordingly. 52562306a36Sopenharmony_ci */ 52662306a36Sopenharmony_ci val = lan_rd(lan966x, ANA_CPU_FWD_CFG(port)); 52762306a36Sopenharmony_ci if (!(val & (ANA_CPU_FWD_CFG_IGMP_REDIR_ENA | 52862306a36Sopenharmony_ci ANA_CPU_FWD_CFG_MLD_REDIR_ENA))) 52962306a36Sopenharmony_ci return true; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (eth_type_vlan(skb->protocol)) { 53262306a36Sopenharmony_ci skb = skb_vlan_untag(skb); 53362306a36Sopenharmony_ci if (unlikely(!skb)) 53462306a36Sopenharmony_ci return false; 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP) && 53862306a36Sopenharmony_ci ip_hdr(skb)->protocol == IPPROTO_IGMP) 53962306a36Sopenharmony_ci return false; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_IPV6) && 54262306a36Sopenharmony_ci skb->protocol == htons(ETH_P_IPV6) && 54362306a36Sopenharmony_ci ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) && 54462306a36Sopenharmony_ci !ipv6_mc_check_mld(skb)) 54562306a36Sopenharmony_ci return false; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci return true; 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic int lan966x_port_xtr_status(struct lan966x *lan966x, u8 grp) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci return lan_rd(lan966x, QS_XTR_RD(grp)); 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic int lan966x_port_xtr_ready(struct lan966x *lan966x, u8 grp) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci u32 val; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci return read_poll_timeout(lan966x_port_xtr_status, val, 56062306a36Sopenharmony_ci val != XTR_NOT_READY, 56162306a36Sopenharmony_ci READL_SLEEP_US, READL_TIMEOUT_US, false, 56262306a36Sopenharmony_ci lan966x, grp); 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_cistatic int lan966x_rx_frame_word(struct lan966x *lan966x, u8 grp, u32 *rval) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci u32 bytes_valid; 56862306a36Sopenharmony_ci u32 val; 56962306a36Sopenharmony_ci int err; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci val = lan_rd(lan966x, QS_XTR_RD(grp)); 57262306a36Sopenharmony_ci if (val == XTR_NOT_READY) { 57362306a36Sopenharmony_ci err = lan966x_port_xtr_ready(lan966x, grp); 57462306a36Sopenharmony_ci if (err) 57562306a36Sopenharmony_ci return -EIO; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci switch (val) { 57962306a36Sopenharmony_ci case XTR_ABORT: 58062306a36Sopenharmony_ci return -EIO; 58162306a36Sopenharmony_ci case XTR_EOF_0: 58262306a36Sopenharmony_ci case XTR_EOF_1: 58362306a36Sopenharmony_ci case XTR_EOF_2: 58462306a36Sopenharmony_ci case XTR_EOF_3: 58562306a36Sopenharmony_ci case XTR_PRUNED: 58662306a36Sopenharmony_ci bytes_valid = XTR_VALID_BYTES(val); 58762306a36Sopenharmony_ci val = lan_rd(lan966x, QS_XTR_RD(grp)); 58862306a36Sopenharmony_ci if (val == XTR_ESCAPE) 58962306a36Sopenharmony_ci *rval = lan_rd(lan966x, QS_XTR_RD(grp)); 59062306a36Sopenharmony_ci else 59162306a36Sopenharmony_ci *rval = val; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci return bytes_valid; 59462306a36Sopenharmony_ci case XTR_ESCAPE: 59562306a36Sopenharmony_ci *rval = lan_rd(lan966x, QS_XTR_RD(grp)); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci return 4; 59862306a36Sopenharmony_ci default: 59962306a36Sopenharmony_ci *rval = val; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci return 4; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic u64 lan966x_ifh_get(u8 *ifh, size_t pos, size_t length) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci u64 val = 0; 60862306a36Sopenharmony_ci u8 v; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci for (int i = 0; i < length ; i++) { 61162306a36Sopenharmony_ci int j = pos + i; 61262306a36Sopenharmony_ci int k = j % 8; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (i == 0 || k == 0) 61562306a36Sopenharmony_ci v = ifh[IFH_LEN_BYTES - (j / 8) - 1]; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if (v & (1 << k)) 61862306a36Sopenharmony_ci val |= (1ULL << i); 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci return val; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_civoid lan966x_ifh_get_src_port(void *ifh, u64 *src_port) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci *src_port = lan966x_ifh_get(ifh, IFH_POS_SRCPORT, IFH_WID_SRCPORT); 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic void lan966x_ifh_get_len(void *ifh, u64 *len) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci *len = lan966x_ifh_get(ifh, IFH_POS_LEN, IFH_WID_LEN); 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_civoid lan966x_ifh_get_timestamp(void *ifh, u64 *timestamp) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci *timestamp = lan966x_ifh_get(ifh, IFH_POS_TIMESTAMP, IFH_WID_TIMESTAMP); 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_cistatic irqreturn_t lan966x_xtr_irq_handler(int irq, void *args) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci struct lan966x *lan966x = args; 64262306a36Sopenharmony_ci int i, grp = 0, err = 0; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (!(lan_rd(lan966x, QS_XTR_DATA_PRESENT) & BIT(grp))) 64562306a36Sopenharmony_ci return IRQ_NONE; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci do { 64862306a36Sopenharmony_ci u64 src_port, len, timestamp; 64962306a36Sopenharmony_ci struct net_device *dev; 65062306a36Sopenharmony_ci struct sk_buff *skb; 65162306a36Sopenharmony_ci int sz = 0, buf_len; 65262306a36Sopenharmony_ci u32 ifh[IFH_LEN]; 65362306a36Sopenharmony_ci u32 *buf; 65462306a36Sopenharmony_ci u32 val; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci for (i = 0; i < IFH_LEN; i++) { 65762306a36Sopenharmony_ci err = lan966x_rx_frame_word(lan966x, grp, &ifh[i]); 65862306a36Sopenharmony_ci if (err != 4) 65962306a36Sopenharmony_ci goto recover; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci err = 0; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci lan966x_ifh_get_src_port(ifh, &src_port); 66562306a36Sopenharmony_ci lan966x_ifh_get_len(ifh, &len); 66662306a36Sopenharmony_ci lan966x_ifh_get_timestamp(ifh, ×tamp); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci WARN_ON(src_port >= lan966x->num_phys_ports); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci dev = lan966x->ports[src_port]->dev; 67162306a36Sopenharmony_ci skb = netdev_alloc_skb(dev, len); 67262306a36Sopenharmony_ci if (unlikely(!skb)) { 67362306a36Sopenharmony_ci netdev_err(dev, "Unable to allocate sk_buff\n"); 67462306a36Sopenharmony_ci err = -ENOMEM; 67562306a36Sopenharmony_ci break; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci buf_len = len - ETH_FCS_LEN; 67862306a36Sopenharmony_ci buf = (u32 *)skb_put(skb, buf_len); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci len = 0; 68162306a36Sopenharmony_ci do { 68262306a36Sopenharmony_ci sz = lan966x_rx_frame_word(lan966x, grp, &val); 68362306a36Sopenharmony_ci if (sz < 0) { 68462306a36Sopenharmony_ci kfree_skb(skb); 68562306a36Sopenharmony_ci goto recover; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci *buf++ = val; 68962306a36Sopenharmony_ci len += sz; 69062306a36Sopenharmony_ci } while (len < buf_len); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci /* Read the FCS */ 69362306a36Sopenharmony_ci sz = lan966x_rx_frame_word(lan966x, grp, &val); 69462306a36Sopenharmony_ci if (sz < 0) { 69562306a36Sopenharmony_ci kfree_skb(skb); 69662306a36Sopenharmony_ci goto recover; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* Update the statistics if part of the FCS was read before */ 70062306a36Sopenharmony_ci len -= ETH_FCS_LEN - sz; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci if (unlikely(dev->features & NETIF_F_RXFCS)) { 70362306a36Sopenharmony_ci buf = (u32 *)skb_put(skb, ETH_FCS_LEN); 70462306a36Sopenharmony_ci *buf = val; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci lan966x_ptp_rxtstamp(lan966x, skb, src_port, timestamp); 70862306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci if (lan966x->bridge_mask & BIT(src_port)) { 71162306a36Sopenharmony_ci skb->offload_fwd_mark = 1; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci skb_reset_network_header(skb); 71462306a36Sopenharmony_ci if (!lan966x_hw_offload(lan966x, src_port, skb)) 71562306a36Sopenharmony_ci skb->offload_fwd_mark = 0; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (!skb_defer_rx_timestamp(skb)) 71962306a36Sopenharmony_ci netif_rx(skb); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci dev->stats.rx_bytes += len; 72262306a36Sopenharmony_ci dev->stats.rx_packets++; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cirecover: 72562306a36Sopenharmony_ci if (sz < 0 || err) 72662306a36Sopenharmony_ci lan_rd(lan966x, QS_XTR_RD(grp)); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci } while (lan_rd(lan966x, QS_XTR_DATA_PRESENT) & BIT(grp)); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci return IRQ_HANDLED; 73162306a36Sopenharmony_ci} 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_cistatic irqreturn_t lan966x_ana_irq_handler(int irq, void *args) 73462306a36Sopenharmony_ci{ 73562306a36Sopenharmony_ci struct lan966x *lan966x = args; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci return lan966x_mac_irq_handler(lan966x); 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cistatic void lan966x_cleanup_ports(struct lan966x *lan966x) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci struct lan966x_port *port; 74362306a36Sopenharmony_ci int p; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci for (p = 0; p < lan966x->num_phys_ports; p++) { 74662306a36Sopenharmony_ci port = lan966x->ports[p]; 74762306a36Sopenharmony_ci if (!port) 74862306a36Sopenharmony_ci continue; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci if (port->dev) 75162306a36Sopenharmony_ci unregister_netdev(port->dev); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci lan966x_xdp_port_deinit(port); 75462306a36Sopenharmony_ci if (lan966x->fdma && lan966x->fdma_ndev == port->dev) 75562306a36Sopenharmony_ci lan966x_fdma_netdev_deinit(lan966x, port->dev); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci if (port->phylink) { 75862306a36Sopenharmony_ci rtnl_lock(); 75962306a36Sopenharmony_ci lan966x_port_stop(port->dev); 76062306a36Sopenharmony_ci rtnl_unlock(); 76162306a36Sopenharmony_ci phylink_destroy(port->phylink); 76262306a36Sopenharmony_ci port->phylink = NULL; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci if (port->fwnode) 76662306a36Sopenharmony_ci fwnode_handle_put(port->fwnode); 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci disable_irq(lan966x->xtr_irq); 77062306a36Sopenharmony_ci lan966x->xtr_irq = -ENXIO; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci if (lan966x->ana_irq > 0) { 77362306a36Sopenharmony_ci disable_irq(lan966x->ana_irq); 77462306a36Sopenharmony_ci lan966x->ana_irq = -ENXIO; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (lan966x->fdma) 77862306a36Sopenharmony_ci devm_free_irq(lan966x->dev, lan966x->fdma_irq, lan966x); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (lan966x->ptp_irq > 0) 78162306a36Sopenharmony_ci devm_free_irq(lan966x->dev, lan966x->ptp_irq, lan966x); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci if (lan966x->ptp_ext_irq > 0) 78462306a36Sopenharmony_ci devm_free_irq(lan966x->dev, lan966x->ptp_ext_irq, lan966x); 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cistatic int lan966x_probe_port(struct lan966x *lan966x, u32 p, 78862306a36Sopenharmony_ci phy_interface_t phy_mode, 78962306a36Sopenharmony_ci struct fwnode_handle *portnp) 79062306a36Sopenharmony_ci{ 79162306a36Sopenharmony_ci struct lan966x_port *port; 79262306a36Sopenharmony_ci struct phylink *phylink; 79362306a36Sopenharmony_ci struct net_device *dev; 79462306a36Sopenharmony_ci int err; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci if (p >= lan966x->num_phys_ports) 79762306a36Sopenharmony_ci return -EINVAL; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci dev = devm_alloc_etherdev_mqs(lan966x->dev, 80062306a36Sopenharmony_ci sizeof(struct lan966x_port), 80162306a36Sopenharmony_ci NUM_PRIO_QUEUES, 1); 80262306a36Sopenharmony_ci if (!dev) 80362306a36Sopenharmony_ci return -ENOMEM; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci SET_NETDEV_DEV(dev, lan966x->dev); 80662306a36Sopenharmony_ci port = netdev_priv(dev); 80762306a36Sopenharmony_ci port->dev = dev; 80862306a36Sopenharmony_ci port->lan966x = lan966x; 80962306a36Sopenharmony_ci port->chip_port = p; 81062306a36Sopenharmony_ci lan966x->ports[p] = port; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci dev->max_mtu = ETH_MAX_MTU; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci dev->netdev_ops = &lan966x_port_netdev_ops; 81562306a36Sopenharmony_ci dev->ethtool_ops = &lan966x_ethtool_ops; 81662306a36Sopenharmony_ci dev->features |= NETIF_F_HW_VLAN_CTAG_TX | 81762306a36Sopenharmony_ci NETIF_F_HW_VLAN_STAG_TX | 81862306a36Sopenharmony_ci NETIF_F_HW_TC; 81962306a36Sopenharmony_ci dev->hw_features |= NETIF_F_HW_TC; 82062306a36Sopenharmony_ci dev->priv_flags |= IFF_SEE_ALL_HWTSTAMP_REQUESTS; 82162306a36Sopenharmony_ci dev->needed_headroom = IFH_LEN_BYTES; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci eth_hw_addr_gen(dev, lan966x->base_mac, p + 1); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci lan966x_mac_learn(lan966x, PGID_CPU, dev->dev_addr, HOST_PVID, 82662306a36Sopenharmony_ci ENTRYTYPE_LOCKED); 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci port->phylink_config.dev = &port->dev->dev; 82962306a36Sopenharmony_ci port->phylink_config.type = PHYLINK_NETDEV; 83062306a36Sopenharmony_ci port->phylink_pcs.poll = true; 83162306a36Sopenharmony_ci port->phylink_pcs.ops = &lan966x_phylink_pcs_ops; 83262306a36Sopenharmony_ci port->phylink_pcs.neg_mode = true; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci port->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | 83562306a36Sopenharmony_ci MAC_10 | MAC_100 | MAC_1000FD | MAC_2500FD; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci phy_interface_set_rgmii(port->phylink_config.supported_interfaces); 83862306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_MII, 83962306a36Sopenharmony_ci port->phylink_config.supported_interfaces); 84062306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_GMII, 84162306a36Sopenharmony_ci port->phylink_config.supported_interfaces); 84262306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_SGMII, 84362306a36Sopenharmony_ci port->phylink_config.supported_interfaces); 84462306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_QSGMII, 84562306a36Sopenharmony_ci port->phylink_config.supported_interfaces); 84662306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_QUSGMII, 84762306a36Sopenharmony_ci port->phylink_config.supported_interfaces); 84862306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_1000BASEX, 84962306a36Sopenharmony_ci port->phylink_config.supported_interfaces); 85062306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_2500BASEX, 85162306a36Sopenharmony_ci port->phylink_config.supported_interfaces); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci phylink = phylink_create(&port->phylink_config, 85462306a36Sopenharmony_ci portnp, 85562306a36Sopenharmony_ci phy_mode, 85662306a36Sopenharmony_ci &lan966x_phylink_mac_ops); 85762306a36Sopenharmony_ci if (IS_ERR(phylink)) { 85862306a36Sopenharmony_ci port->dev = NULL; 85962306a36Sopenharmony_ci return PTR_ERR(phylink); 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci port->phylink = phylink; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if (lan966x->fdma) 86562306a36Sopenharmony_ci dev->xdp_features = NETDEV_XDP_ACT_BASIC | 86662306a36Sopenharmony_ci NETDEV_XDP_ACT_REDIRECT | 86762306a36Sopenharmony_ci NETDEV_XDP_ACT_NDO_XMIT; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci err = register_netdev(dev); 87062306a36Sopenharmony_ci if (err) { 87162306a36Sopenharmony_ci dev_err(lan966x->dev, "register_netdev failed\n"); 87262306a36Sopenharmony_ci return err; 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci lan966x_vlan_port_set_vlan_aware(port, 0); 87662306a36Sopenharmony_ci lan966x_vlan_port_set_vid(port, HOST_PVID, false, false); 87762306a36Sopenharmony_ci lan966x_vlan_port_apply(port); 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci return 0; 88062306a36Sopenharmony_ci} 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_cistatic void lan966x_init(struct lan966x *lan966x) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci u32 p, i; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci /* MAC table initialization */ 88762306a36Sopenharmony_ci lan966x_mac_init(lan966x); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci lan966x_vlan_init(lan966x); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci /* Flush queues */ 89262306a36Sopenharmony_ci lan_wr(lan_rd(lan966x, QS_XTR_FLUSH) | 89362306a36Sopenharmony_ci GENMASK(1, 0), 89462306a36Sopenharmony_ci lan966x, QS_XTR_FLUSH); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci /* Allow to drain */ 89762306a36Sopenharmony_ci mdelay(1); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci /* All Queues normal */ 90062306a36Sopenharmony_ci lan_wr(lan_rd(lan966x, QS_XTR_FLUSH) & 90162306a36Sopenharmony_ci ~(GENMASK(1, 0)), 90262306a36Sopenharmony_ci lan966x, QS_XTR_FLUSH); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci /* Set MAC age time to default value, the entry is aged after 90562306a36Sopenharmony_ci * 2 * AGE_PERIOD 90662306a36Sopenharmony_ci */ 90762306a36Sopenharmony_ci lan_wr(ANA_AUTOAGE_AGE_PERIOD_SET(BR_DEFAULT_AGEING_TIME / 2 / HZ), 90862306a36Sopenharmony_ci lan966x, ANA_AUTOAGE); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci /* Disable learning for frames discarded by VLAN ingress filtering */ 91162306a36Sopenharmony_ci lan_rmw(ANA_ADVLEARN_VLAN_CHK_SET(1), 91262306a36Sopenharmony_ci ANA_ADVLEARN_VLAN_CHK, 91362306a36Sopenharmony_ci lan966x, ANA_ADVLEARN); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci /* Setup frame ageing - "2 sec" - The unit is 6.5 us on lan966x */ 91662306a36Sopenharmony_ci lan_wr(SYS_FRM_AGING_AGE_TX_ENA_SET(1) | 91762306a36Sopenharmony_ci (20000000 / 65), 91862306a36Sopenharmony_ci lan966x, SYS_FRM_AGING); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci /* Map the 8 CPU extraction queues to CPU port */ 92162306a36Sopenharmony_ci lan_wr(0, lan966x, QSYS_CPU_GROUP_MAP); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci /* Do byte-swap and expect status after last data word 92462306a36Sopenharmony_ci * Extraction: Mode: manual extraction) | Byte_swap 92562306a36Sopenharmony_ci */ 92662306a36Sopenharmony_ci lan_wr(QS_XTR_GRP_CFG_MODE_SET(lan966x->fdma ? 2 : 1) | 92762306a36Sopenharmony_ci QS_XTR_GRP_CFG_BYTE_SWAP_SET(1), 92862306a36Sopenharmony_ci lan966x, QS_XTR_GRP_CFG(0)); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci /* Injection: Mode: manual injection | Byte_swap */ 93162306a36Sopenharmony_ci lan_wr(QS_INJ_GRP_CFG_MODE_SET(lan966x->fdma ? 2 : 1) | 93262306a36Sopenharmony_ci QS_INJ_GRP_CFG_BYTE_SWAP_SET(1), 93362306a36Sopenharmony_ci lan966x, QS_INJ_GRP_CFG(0)); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci lan_rmw(QS_INJ_CTRL_GAP_SIZE_SET(0), 93662306a36Sopenharmony_ci QS_INJ_CTRL_GAP_SIZE, 93762306a36Sopenharmony_ci lan966x, QS_INJ_CTRL(0)); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci /* Enable IFH insertion/parsing on CPU ports */ 94062306a36Sopenharmony_ci lan_wr(SYS_PORT_MODE_INCL_INJ_HDR_SET(1) | 94162306a36Sopenharmony_ci SYS_PORT_MODE_INCL_XTR_HDR_SET(1), 94262306a36Sopenharmony_ci lan966x, SYS_PORT_MODE(CPU_PORT)); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci /* Setup flooding PGIDs */ 94562306a36Sopenharmony_ci lan_wr(ANA_FLOODING_IPMC_FLD_MC4_DATA_SET(PGID_MCIPV4) | 94662306a36Sopenharmony_ci ANA_FLOODING_IPMC_FLD_MC4_CTRL_SET(PGID_MC) | 94762306a36Sopenharmony_ci ANA_FLOODING_IPMC_FLD_MC6_DATA_SET(PGID_MCIPV6) | 94862306a36Sopenharmony_ci ANA_FLOODING_IPMC_FLD_MC6_CTRL_SET(PGID_MC), 94962306a36Sopenharmony_ci lan966x, ANA_FLOODING_IPMC); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci /* There are 8 priorities */ 95262306a36Sopenharmony_ci for (i = 0; i < 8; ++i) 95362306a36Sopenharmony_ci lan_rmw(ANA_FLOODING_FLD_MULTICAST_SET(PGID_MC) | 95462306a36Sopenharmony_ci ANA_FLOODING_FLD_UNICAST_SET(PGID_UC) | 95562306a36Sopenharmony_ci ANA_FLOODING_FLD_BROADCAST_SET(PGID_BC), 95662306a36Sopenharmony_ci ANA_FLOODING_FLD_MULTICAST | 95762306a36Sopenharmony_ci ANA_FLOODING_FLD_UNICAST | 95862306a36Sopenharmony_ci ANA_FLOODING_FLD_BROADCAST, 95962306a36Sopenharmony_ci lan966x, ANA_FLOODING(i)); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci for (i = 0; i < PGID_ENTRIES; ++i) 96262306a36Sopenharmony_ci /* Set all the entries to obey VLAN_VLAN */ 96362306a36Sopenharmony_ci lan_rmw(ANA_PGID_CFG_OBEY_VLAN_SET(1), 96462306a36Sopenharmony_ci ANA_PGID_CFG_OBEY_VLAN, 96562306a36Sopenharmony_ci lan966x, ANA_PGID_CFG(i)); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci for (p = 0; p < lan966x->num_phys_ports; p++) { 96862306a36Sopenharmony_ci /* Disable bridging by default */ 96962306a36Sopenharmony_ci lan_rmw(ANA_PGID_PGID_SET(0x0), 97062306a36Sopenharmony_ci ANA_PGID_PGID, 97162306a36Sopenharmony_ci lan966x, ANA_PGID(p + PGID_SRC)); 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci /* Do not forward BPDU frames to the front ports and copy them 97462306a36Sopenharmony_ci * to CPU 97562306a36Sopenharmony_ci */ 97662306a36Sopenharmony_ci lan_wr(0xffff, lan966x, ANA_CPU_FWD_BPDU_CFG(p)); 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci /* Set source buffer size for each priority and each port to 1500 bytes */ 98062306a36Sopenharmony_ci for (i = 0; i <= QSYS_Q_RSRV; ++i) { 98162306a36Sopenharmony_ci lan_wr(1500 / 64, lan966x, QSYS_RES_CFG(i)); 98262306a36Sopenharmony_ci lan_wr(1500 / 64, lan966x, QSYS_RES_CFG(512 + i)); 98362306a36Sopenharmony_ci } 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci /* Enable switching to/from cpu port */ 98662306a36Sopenharmony_ci lan_wr(QSYS_SW_PORT_MODE_PORT_ENA_SET(1) | 98762306a36Sopenharmony_ci QSYS_SW_PORT_MODE_SCH_NEXT_CFG_SET(1) | 98862306a36Sopenharmony_ci QSYS_SW_PORT_MODE_INGRESS_DROP_MODE_SET(1), 98962306a36Sopenharmony_ci lan966x, QSYS_SW_PORT_MODE(CPU_PORT)); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci /* Configure and enable the CPU port */ 99262306a36Sopenharmony_ci lan_rmw(ANA_PGID_PGID_SET(0), 99362306a36Sopenharmony_ci ANA_PGID_PGID, 99462306a36Sopenharmony_ci lan966x, ANA_PGID(CPU_PORT)); 99562306a36Sopenharmony_ci lan_rmw(ANA_PGID_PGID_SET(BIT(CPU_PORT)), 99662306a36Sopenharmony_ci ANA_PGID_PGID, 99762306a36Sopenharmony_ci lan966x, ANA_PGID(PGID_CPU)); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci /* Multicast to all other ports */ 100062306a36Sopenharmony_ci lan_rmw(GENMASK(lan966x->num_phys_ports - 1, 0), 100162306a36Sopenharmony_ci ANA_PGID_PGID, 100262306a36Sopenharmony_ci lan966x, ANA_PGID(PGID_MC)); 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci /* This will be controlled by mrouter ports */ 100562306a36Sopenharmony_ci lan_rmw(GENMASK(lan966x->num_phys_ports - 1, 0), 100662306a36Sopenharmony_ci ANA_PGID_PGID, 100762306a36Sopenharmony_ci lan966x, ANA_PGID(PGID_MCIPV4)); 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci lan_rmw(GENMASK(lan966x->num_phys_ports - 1, 0), 101062306a36Sopenharmony_ci ANA_PGID_PGID, 101162306a36Sopenharmony_ci lan966x, ANA_PGID(PGID_MCIPV6)); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci /* Unicast to all other ports */ 101462306a36Sopenharmony_ci lan_rmw(GENMASK(lan966x->num_phys_ports - 1, 0), 101562306a36Sopenharmony_ci ANA_PGID_PGID, 101662306a36Sopenharmony_ci lan966x, ANA_PGID(PGID_UC)); 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci /* Broadcast to the CPU port and to other ports */ 101962306a36Sopenharmony_ci lan_rmw(ANA_PGID_PGID_SET(BIT(CPU_PORT) | GENMASK(lan966x->num_phys_ports - 1, 0)), 102062306a36Sopenharmony_ci ANA_PGID_PGID, 102162306a36Sopenharmony_ci lan966x, ANA_PGID(PGID_BC)); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci lan_wr(REW_PORT_CFG_NO_REWRITE_SET(1), 102462306a36Sopenharmony_ci lan966x, REW_PORT_CFG(CPU_PORT)); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci lan_rmw(ANA_ANAINTR_INTR_ENA_SET(1), 102762306a36Sopenharmony_ci ANA_ANAINTR_INTR_ENA, 102862306a36Sopenharmony_ci lan966x, ANA_ANAINTR); 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci spin_lock_init(&lan966x->tx_lock); 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci lan966x_taprio_init(lan966x); 103362306a36Sopenharmony_ci} 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_cistatic int lan966x_ram_init(struct lan966x *lan966x) 103662306a36Sopenharmony_ci{ 103762306a36Sopenharmony_ci return lan_rd(lan966x, SYS_RAM_INIT); 103862306a36Sopenharmony_ci} 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_cistatic int lan966x_reset_switch(struct lan966x *lan966x) 104162306a36Sopenharmony_ci{ 104262306a36Sopenharmony_ci struct reset_control *switch_reset; 104362306a36Sopenharmony_ci int val = 0; 104462306a36Sopenharmony_ci int ret; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci switch_reset = devm_reset_control_get_optional_shared(lan966x->dev, 104762306a36Sopenharmony_ci "switch"); 104862306a36Sopenharmony_ci if (IS_ERR(switch_reset)) 104962306a36Sopenharmony_ci return dev_err_probe(lan966x->dev, PTR_ERR(switch_reset), 105062306a36Sopenharmony_ci "Could not obtain switch reset"); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci reset_control_reset(switch_reset); 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci /* Don't reinitialize the switch core, if it is already initialized. In 105562306a36Sopenharmony_ci * case it is initialized twice, some pointers inside the queue system 105662306a36Sopenharmony_ci * in HW will get corrupted and then after a while the queue system gets 105762306a36Sopenharmony_ci * full and no traffic is passing through the switch. The issue is seen 105862306a36Sopenharmony_ci * when loading and unloading the driver and sending traffic through the 105962306a36Sopenharmony_ci * switch. 106062306a36Sopenharmony_ci */ 106162306a36Sopenharmony_ci if (lan_rd(lan966x, SYS_RESET_CFG) & SYS_RESET_CFG_CORE_ENA) 106262306a36Sopenharmony_ci return 0; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci lan_wr(SYS_RESET_CFG_CORE_ENA_SET(0), lan966x, SYS_RESET_CFG); 106562306a36Sopenharmony_ci lan_wr(SYS_RAM_INIT_RAM_INIT_SET(1), lan966x, SYS_RAM_INIT); 106662306a36Sopenharmony_ci ret = readx_poll_timeout(lan966x_ram_init, lan966x, 106762306a36Sopenharmony_ci val, (val & BIT(1)) == 0, READL_SLEEP_US, 106862306a36Sopenharmony_ci READL_TIMEOUT_US); 106962306a36Sopenharmony_ci if (ret) 107062306a36Sopenharmony_ci return ret; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci lan_wr(SYS_RESET_CFG_CORE_ENA_SET(1), lan966x, SYS_RESET_CFG); 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci return 0; 107562306a36Sopenharmony_ci} 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_cistatic int lan966x_probe(struct platform_device *pdev) 107862306a36Sopenharmony_ci{ 107962306a36Sopenharmony_ci struct fwnode_handle *ports, *portnp; 108062306a36Sopenharmony_ci struct lan966x *lan966x; 108162306a36Sopenharmony_ci u8 mac_addr[ETH_ALEN]; 108262306a36Sopenharmony_ci int err; 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci lan966x = devm_kzalloc(&pdev->dev, sizeof(*lan966x), GFP_KERNEL); 108562306a36Sopenharmony_ci if (!lan966x) 108662306a36Sopenharmony_ci return -ENOMEM; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci platform_set_drvdata(pdev, lan966x); 108962306a36Sopenharmony_ci lan966x->dev = &pdev->dev; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci lan966x->debugfs_root = debugfs_create_dir("lan966x", NULL); 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci if (!device_get_mac_address(&pdev->dev, mac_addr)) { 109462306a36Sopenharmony_ci ether_addr_copy(lan966x->base_mac, mac_addr); 109562306a36Sopenharmony_ci } else { 109662306a36Sopenharmony_ci pr_info("MAC addr was not set, use random MAC\n"); 109762306a36Sopenharmony_ci eth_random_addr(lan966x->base_mac); 109862306a36Sopenharmony_ci lan966x->base_mac[5] &= 0xf0; 109962306a36Sopenharmony_ci } 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci err = lan966x_create_targets(pdev, lan966x); 110262306a36Sopenharmony_ci if (err) 110362306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, err, 110462306a36Sopenharmony_ci "Failed to create targets"); 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci err = lan966x_reset_switch(lan966x); 110762306a36Sopenharmony_ci if (err) 110862306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, err, "Reset failed"); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci lan966x->num_phys_ports = NUM_PHYS_PORTS; 111162306a36Sopenharmony_ci lan966x->ports = devm_kcalloc(&pdev->dev, lan966x->num_phys_ports, 111262306a36Sopenharmony_ci sizeof(struct lan966x_port *), 111362306a36Sopenharmony_ci GFP_KERNEL); 111462306a36Sopenharmony_ci if (!lan966x->ports) 111562306a36Sopenharmony_ci return -ENOMEM; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci /* There QS system has 32KB of memory */ 111862306a36Sopenharmony_ci lan966x->shared_queue_sz = LAN966X_BUFFER_MEMORY; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci /* set irq */ 112162306a36Sopenharmony_ci lan966x->xtr_irq = platform_get_irq_byname(pdev, "xtr"); 112262306a36Sopenharmony_ci if (lan966x->xtr_irq < 0) 112362306a36Sopenharmony_ci return lan966x->xtr_irq; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci err = devm_request_threaded_irq(&pdev->dev, lan966x->xtr_irq, NULL, 112662306a36Sopenharmony_ci lan966x_xtr_irq_handler, IRQF_ONESHOT, 112762306a36Sopenharmony_ci "frame extraction", lan966x); 112862306a36Sopenharmony_ci if (err) { 112962306a36Sopenharmony_ci pr_err("Unable to use xtr irq"); 113062306a36Sopenharmony_ci return -ENODEV; 113162306a36Sopenharmony_ci } 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci lan966x->ana_irq = platform_get_irq_byname(pdev, "ana"); 113462306a36Sopenharmony_ci if (lan966x->ana_irq > 0) { 113562306a36Sopenharmony_ci err = devm_request_threaded_irq(&pdev->dev, lan966x->ana_irq, NULL, 113662306a36Sopenharmony_ci lan966x_ana_irq_handler, IRQF_ONESHOT, 113762306a36Sopenharmony_ci "ana irq", lan966x); 113862306a36Sopenharmony_ci if (err) 113962306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, err, "Unable to use ana irq"); 114062306a36Sopenharmony_ci } 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci lan966x->ptp_irq = platform_get_irq_byname(pdev, "ptp"); 114362306a36Sopenharmony_ci if (lan966x->ptp_irq > 0) { 114462306a36Sopenharmony_ci err = devm_request_threaded_irq(&pdev->dev, lan966x->ptp_irq, NULL, 114562306a36Sopenharmony_ci lan966x_ptp_irq_handler, IRQF_ONESHOT, 114662306a36Sopenharmony_ci "ptp irq", lan966x); 114762306a36Sopenharmony_ci if (err) 114862306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, err, "Unable to use ptp irq"); 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci lan966x->ptp = 1; 115162306a36Sopenharmony_ci } 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci lan966x->fdma_irq = platform_get_irq_byname(pdev, "fdma"); 115462306a36Sopenharmony_ci if (lan966x->fdma_irq > 0) { 115562306a36Sopenharmony_ci err = devm_request_irq(&pdev->dev, lan966x->fdma_irq, 115662306a36Sopenharmony_ci lan966x_fdma_irq_handler, 0, 115762306a36Sopenharmony_ci "fdma irq", lan966x); 115862306a36Sopenharmony_ci if (err) 115962306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, err, "Unable to use fdma irq"); 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci lan966x->fdma = true; 116262306a36Sopenharmony_ci } 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci if (lan966x->ptp) { 116562306a36Sopenharmony_ci lan966x->ptp_ext_irq = platform_get_irq_byname(pdev, "ptp-ext"); 116662306a36Sopenharmony_ci if (lan966x->ptp_ext_irq > 0) { 116762306a36Sopenharmony_ci err = devm_request_threaded_irq(&pdev->dev, 116862306a36Sopenharmony_ci lan966x->ptp_ext_irq, NULL, 116962306a36Sopenharmony_ci lan966x_ptp_ext_irq_handler, 117062306a36Sopenharmony_ci IRQF_ONESHOT, 117162306a36Sopenharmony_ci "ptp-ext irq", lan966x); 117262306a36Sopenharmony_ci if (err) 117362306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, err, 117462306a36Sopenharmony_ci "Unable to use ptp-ext irq"); 117562306a36Sopenharmony_ci } 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci ports = device_get_named_child_node(&pdev->dev, "ethernet-ports"); 117962306a36Sopenharmony_ci if (!ports) 118062306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, -ENODEV, 118162306a36Sopenharmony_ci "no ethernet-ports child found\n"); 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci /* init switch */ 118462306a36Sopenharmony_ci lan966x_init(lan966x); 118562306a36Sopenharmony_ci lan966x_stats_init(lan966x); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci /* go over the child nodes */ 118862306a36Sopenharmony_ci fwnode_for_each_available_child_node(ports, portnp) { 118962306a36Sopenharmony_ci phy_interface_t phy_mode; 119062306a36Sopenharmony_ci struct phy *serdes; 119162306a36Sopenharmony_ci u32 p; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci if (fwnode_property_read_u32(portnp, "reg", &p)) 119462306a36Sopenharmony_ci continue; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci phy_mode = fwnode_get_phy_mode(portnp); 119762306a36Sopenharmony_ci err = lan966x_probe_port(lan966x, p, phy_mode, portnp); 119862306a36Sopenharmony_ci if (err) 119962306a36Sopenharmony_ci goto cleanup_ports; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci /* Read needed configuration */ 120262306a36Sopenharmony_ci lan966x->ports[p]->config.portmode = phy_mode; 120362306a36Sopenharmony_ci lan966x->ports[p]->fwnode = fwnode_handle_get(portnp); 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci serdes = devm_of_phy_optional_get(lan966x->dev, 120662306a36Sopenharmony_ci to_of_node(portnp), NULL); 120762306a36Sopenharmony_ci if (IS_ERR(serdes)) { 120862306a36Sopenharmony_ci err = PTR_ERR(serdes); 120962306a36Sopenharmony_ci goto cleanup_ports; 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci lan966x->ports[p]->serdes = serdes; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci lan966x_port_init(lan966x->ports[p]); 121462306a36Sopenharmony_ci err = lan966x_xdp_port_init(lan966x->ports[p]); 121562306a36Sopenharmony_ci if (err) 121662306a36Sopenharmony_ci goto cleanup_ports; 121762306a36Sopenharmony_ci } 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci fwnode_handle_put(ports); 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci lan966x_mdb_init(lan966x); 122262306a36Sopenharmony_ci err = lan966x_fdb_init(lan966x); 122362306a36Sopenharmony_ci if (err) 122462306a36Sopenharmony_ci goto cleanup_ports; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci err = lan966x_ptp_init(lan966x); 122762306a36Sopenharmony_ci if (err) 122862306a36Sopenharmony_ci goto cleanup_fdb; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci err = lan966x_fdma_init(lan966x); 123162306a36Sopenharmony_ci if (err) 123262306a36Sopenharmony_ci goto cleanup_ptp; 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci err = lan966x_vcap_init(lan966x); 123562306a36Sopenharmony_ci if (err) 123662306a36Sopenharmony_ci goto cleanup_fdma; 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci lan966x_dcb_init(lan966x); 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci return 0; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_cicleanup_fdma: 124362306a36Sopenharmony_ci lan966x_fdma_deinit(lan966x); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_cicleanup_ptp: 124662306a36Sopenharmony_ci lan966x_ptp_deinit(lan966x); 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_cicleanup_fdb: 124962306a36Sopenharmony_ci lan966x_fdb_deinit(lan966x); 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_cicleanup_ports: 125262306a36Sopenharmony_ci fwnode_handle_put(ports); 125362306a36Sopenharmony_ci fwnode_handle_put(portnp); 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci lan966x_cleanup_ports(lan966x); 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci cancel_delayed_work_sync(&lan966x->stats_work); 125862306a36Sopenharmony_ci destroy_workqueue(lan966x->stats_queue); 125962306a36Sopenharmony_ci mutex_destroy(&lan966x->stats_lock); 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci return err; 126262306a36Sopenharmony_ci} 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_cistatic int lan966x_remove(struct platform_device *pdev) 126562306a36Sopenharmony_ci{ 126662306a36Sopenharmony_ci struct lan966x *lan966x = platform_get_drvdata(pdev); 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci lan966x_taprio_deinit(lan966x); 126962306a36Sopenharmony_ci lan966x_vcap_deinit(lan966x); 127062306a36Sopenharmony_ci lan966x_fdma_deinit(lan966x); 127162306a36Sopenharmony_ci lan966x_cleanup_ports(lan966x); 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci cancel_delayed_work_sync(&lan966x->stats_work); 127462306a36Sopenharmony_ci destroy_workqueue(lan966x->stats_queue); 127562306a36Sopenharmony_ci mutex_destroy(&lan966x->stats_lock); 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci lan966x_mac_purge_entries(lan966x); 127862306a36Sopenharmony_ci lan966x_mdb_deinit(lan966x); 127962306a36Sopenharmony_ci lan966x_fdb_deinit(lan966x); 128062306a36Sopenharmony_ci lan966x_ptp_deinit(lan966x); 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci debugfs_remove_recursive(lan966x->debugfs_root); 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci return 0; 128562306a36Sopenharmony_ci} 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_cistatic struct platform_driver lan966x_driver = { 128862306a36Sopenharmony_ci .probe = lan966x_probe, 128962306a36Sopenharmony_ci .remove = lan966x_remove, 129062306a36Sopenharmony_ci .driver = { 129162306a36Sopenharmony_ci .name = "lan966x-switch", 129262306a36Sopenharmony_ci .of_match_table = lan966x_match, 129362306a36Sopenharmony_ci }, 129462306a36Sopenharmony_ci}; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_cistatic int __init lan966x_switch_driver_init(void) 129762306a36Sopenharmony_ci{ 129862306a36Sopenharmony_ci int ret; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci lan966x_register_notifier_blocks(); 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci ret = platform_driver_register(&lan966x_driver); 130362306a36Sopenharmony_ci if (ret) 130462306a36Sopenharmony_ci goto err; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci return 0; 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_cierr: 130962306a36Sopenharmony_ci lan966x_unregister_notifier_blocks(); 131062306a36Sopenharmony_ci return ret; 131162306a36Sopenharmony_ci} 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_cistatic void __exit lan966x_switch_driver_exit(void) 131462306a36Sopenharmony_ci{ 131562306a36Sopenharmony_ci platform_driver_unregister(&lan966x_driver); 131662306a36Sopenharmony_ci lan966x_unregister_notifier_blocks(); 131762306a36Sopenharmony_ci} 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_cimodule_init(lan966x_switch_driver_init); 132062306a36Sopenharmony_cimodule_exit(lan966x_switch_driver_exit); 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ciMODULE_DESCRIPTION("Microchip LAN966X switch driver"); 132362306a36Sopenharmony_ciMODULE_AUTHOR("Horatiu Vultur <horatiu.vultur@microchip.com>"); 132462306a36Sopenharmony_ciMODULE_LICENSE("Dual MIT/GPL"); 1325