From patchwork Fri Jan 24 12:23:45 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Basharath Hussain Khaja X-Patchwork-Id: 859723 Received: from server.wki.vra.mybluehostin.me (server.wki.vra.mybluehostin.me [162.240.238.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 870342B9BC; Fri, 24 Jan 2025 12:25:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=162.240.238.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1737721543; cv=none; b=BMWojjNtNNVbXidh+GvHTO5x2TvcVsdB+xUvAnwQpWBlkrXJ74KVirJgDnUr4yEdUVO51rTrwiMYkSjZsPRl5vspygAuYZNtrRNUQ350GrTRKQCP5KbS3MYWY7Nchu7ny4MisdZDAEvbIInB+POE2IC+F/mLIz24zyhpH4SoYFg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1737721543; c=relaxed/simple; bh=8vgBOkG+kxmGe3X1eDwb8QRi263eIr4tgp+cMu8qoVE=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=fHi7C5TjP7Nd3JR0mzB3PMhnaNj2S1SrqpJuLVULw39dt3ghBaJth0ztTdChpyqhW2y0c9W+PAtznHRoG/6rvAtpAfP9BcvlSqfSPJFLBkB9Nw/wMzvwXhpNW5RqjlUdtMoLfewoSeXUbnQ87OGa+JSWiFlFPXINCcaSu0OW5Hs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=couthit.com; spf=pass smtp.mailfrom=couthit.com; dkim=pass (2048-bit key) header.d=couthit.com header.i=@couthit.com header.b=krX9IsFB; arc=none smtp.client-ip=162.240.238.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=couthit.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=couthit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=couthit.com header.i=@couthit.com header.b="krX9IsFB" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=couthit.com ; s=default; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=k4Lh7/vdHYScylzxkvKUUMe016Ltr0UiHdRGfue72Zw=; b=krX9IsFB7qFAVjEE6rfYA8QS9N Jgn5UBFQiRPOXylXgyV+Kl+4jtiEyogBGC55UaVnLvpL0E2qYuevJOVroSbFxFpa7XjaFoaQ2LO14 svA49/deuwaiuI+BCpGtxy3fCgyb9EtTJ/A301dSSRKb5rQLNjV6XFPZkudaq3OwT6unjz3ldrG49 tNgzg+atX7vvvTiEkcyEiSV3vCBkIZAUyuVVDrkMVOApbDGaIEqXdgH5ZFFqI/Yi1m1O+CHvc2+2m ftxAZBGV8tA2WlhoXg6WgB7JvDVv1idK1xAUop3ecN64piyIaERVrNSc/kAWF9J3QpBmN/IbwmboQ 7dLESZUQ==; Received: from [122.175.9.182] (port=47173 helo=cypher.couthit.local) by server.wki.vra.mybluehostin.me with esmtpa (Exim 4.96.2) (envelope-from ) id 1tbIkh-0004DJ-01; Fri, 24 Jan 2025 17:55:35 +0530 From: Basharath Hussain Khaja To: danishanwar@ti.com, rogerq@kernel.org, andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, nm@ti.com, ssantosh@kernel.org, tony@atomide.com, richardcochran@gmail.com, parvathi@couthit.com, basharath@couthit.com, schnelle@linux.ibm.com, rdunlap@infradead.org, diogo.ivo@siemens.com, m-karicheri2@ti.com, horms@kernel.org, jacob.e.keller@intel.com, m-malladi@ti.com, javier.carrasco.cruz@gmail.com, afd@ti.com, s-anna@ti.com Cc: linux-arm-kernel@lists.infradead.org, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-omap@vger.kernel.org, pratheesh@ti.com, prajith@ti.com, vigneshr@ti.com, praneeth@ti.com, srk@ti.com, rogerq@ti.com, krishna@couthit.com, pmohan@couthit.com, mohan@couthit.com Subject: [RFC v2 PATCH 02/10] net: ti: prueth: Adds ICSSM Ethernet driver Date: Fri, 24 Jan 2025 17:53:45 +0530 Message-Id: <20250124122353.1457174-3-basharath@couthit.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250124122353.1457174-1-basharath@couthit.com> References: <20250124122353.1457174-1-basharath@couthit.com> Precedence: bulk X-Mailing-List: linux-omap@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-AntiAbuse: This header was added to track abuse, please include it with any abuse report X-AntiAbuse: Primary Hostname - server.wki.vra.mybluehostin.me X-AntiAbuse: Original Domain - vger.kernel.org X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] X-AntiAbuse: Sender Address Domain - couthit.com X-Get-Message-Sender-Via: server.wki.vra.mybluehostin.me: authenticated_id: basharath@couthit.com X-Authenticated-Sender: server.wki.vra.mybluehostin.me: basharath@couthit.com X-Source: X-Source-Args: X-Source-Dir: From: Roger Quadros Updates Kernel configuration to enable PRUETH driver and its dependencies along with makefile changes to add the new PRUETH driver. Changes includes init and deinit of ICSSM PRU Ethernet driver including net dev registration and firmware loading for DUAL-MAC mode running on PRU-ICSS2 instance. Changes also includes link handling, PRU booting, default firmware loading and PRU stopping using existing remoteproc driver APIs. Signed-off-by: Roger Quadros Signed-off-by: Andrew F. Davis Signed-off-by: Parvathi Pudi Signed-off-by: Basharath Hussain Khaja --- drivers/net/ethernet/ti/Kconfig | 12 + drivers/net/ethernet/ti/Makefile | 3 + drivers/net/ethernet/ti/icssm/icssm_prueth.c | 524 +++++++++++++++++++ drivers/net/ethernet/ti/icssm/icssm_prueth.h | 100 ++++ 4 files changed, 639 insertions(+) create mode 100644 drivers/net/ethernet/ti/icssm/icssm_prueth.c create mode 100644 drivers/net/ethernet/ti/icssm/icssm_prueth.h diff --git a/drivers/net/ethernet/ti/Kconfig b/drivers/net/ethernet/ti/Kconfig index 0d5a862cd78a..96ad084f1dce 100644 --- a/drivers/net/ethernet/ti/Kconfig +++ b/drivers/net/ethernet/ti/Kconfig @@ -227,4 +227,16 @@ config TI_ICSS_IEP To compile this driver as a module, choose M here. The module will be called icss_iep. +config TI_PRUETH + tristate "TI PRU Ethernet EMAC driver" + depends on PRU_REMOTEPROC + depends on NET_SWITCHDEV + select TI_ICSS_IEP + imply PTP_1588_CLOCK + help + Some TI SoCs has Programmable Realtime Units (PRUs) cores which can + support Single or Dual Ethernet ports with help of firmware code running + on PRU cores. This driver supports remoteproc based communication to + PRU firmware to expose ethernet interface to Linux. + endif # NET_VENDOR_TI diff --git a/drivers/net/ethernet/ti/Makefile b/drivers/net/ethernet/ti/Makefile index cbcf44806924..93c0a4d0e33a 100644 --- a/drivers/net/ethernet/ti/Makefile +++ b/drivers/net/ethernet/ti/Makefile @@ -3,6 +3,9 @@ # Makefile for the TI network device drivers. # +obj-$(CONFIG_TI_PRUETH) += icssm-prueth.o +icssm-prueth-y := icssm/icssm_prueth.o + obj-$(CONFIG_TI_CPSW) += cpsw-common.o obj-$(CONFIG_TI_DAVINCI_EMAC) += cpsw-common.o obj-$(CONFIG_TI_CPSW_SWITCHDEV) += cpsw-common.o diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth.c b/drivers/net/ethernet/ti/icssm/icssm_prueth.c new file mode 100644 index 000000000000..aec81962c6c2 --- /dev/null +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth.c @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* Texas Instruments ICSSM Ethernet Driver + * + * Copyright (C) 2018-2022 Texas Instruments Incorporated - https://www.ti.com/ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "icssm_prueth.h" + +/* called back by PHY layer if there is change in link state of hw port*/ +static void icssm_emac_adjust_link(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct phy_device *phydev = emac->phydev; + bool new_state = false; + unsigned long flags; + + spin_lock_irqsave(&emac->lock, flags); + + if (phydev->link) { + /* check the mode of operation */ + if (phydev->duplex != emac->duplex) { + new_state = true; + emac->duplex = phydev->duplex; + } + if (phydev->speed != emac->speed) { + new_state = true; + emac->speed = phydev->speed; + } + if (!emac->link) { + new_state = true; + emac->link = 1; + } + } else if (emac->link) { + new_state = true; + emac->link = 0; + } + + if (new_state) + phy_print_status(phydev); + + if (emac->link) { + /* reactivate the transmit queue if it is stopped */ + if (netif_running(ndev) && netif_queue_stopped(ndev)) + netif_wake_queue(ndev); + } else { + if (!netif_queue_stopped(ndev)) + netif_stop_queue(ndev); + } + + spin_unlock_irqrestore(&emac->lock, flags); +} + +static int icssm_emac_set_boot_pru(struct prueth_emac *emac, + struct net_device *ndev) +{ + const struct prueth_firmware *pru_firmwares; + struct prueth *prueth = emac->prueth; + const char *fw_name; + int ret; + + pru_firmwares = &prueth->fw_data->fw_pru[emac->port_id - 1]; + fw_name = pru_firmwares->fw_name[prueth->eth_type]; + if (!fw_name) { + netdev_err(ndev, "eth_type %d not supported\n", + prueth->eth_type); + return -ENODEV; + } + + ret = rproc_set_firmware(emac->pru, fw_name); + if (ret) { + netdev_err(ndev, "failed to set PRU0 firmware %s: %d\n", + fw_name, ret); + return ret; + } + + ret = rproc_boot(emac->pru); + if (ret) { + netdev_err(ndev, "failed to boot PRU0: %d\n", ret); + return ret; + } + + return ret; +} + +/** + * icssm_emac_ndo_open - EMAC device open + * @ndev: network adapter device + * + * Called when system wants to start the interface. + * + * Return: 0 for a successful open, or appropriate error code + */ +static int icssm_emac_ndo_open(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + int ret; + + ret = icssm_emac_set_boot_pru(emac, ndev); + if (ret) + netdev_err(ndev, "failed to boot PRU: %d\n", ret); + + /* start PHY */ + phy_start(emac->phydev); + + return 0; +} + +/** + * icssm_emac_ndo_stop - EMAC device stop + * @ndev: network adapter device + * + * Called when system wants to stop or down the interface. + * + * Return: Always 0 (Success) + */ +static int icssm_emac_ndo_stop(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + + /* stop PHY */ + phy_stop(emac->phydev); + + rproc_shutdown(emac->pru); + + return 0; +} + +static const struct net_device_ops emac_netdev_ops = { + .ndo_open = icssm_emac_ndo_open, + .ndo_stop = icssm_emac_ndo_stop, +}; + +/* get emac_port corresponding to eth_node name */ +static int icssm_prueth_node_port(struct device_node *eth_node) +{ + u32 port_id; + int ret; + + ret = of_property_read_u32(eth_node, "reg", &port_id); + if (ret) + return ret; + + if (port_id == 0) + return PRUETH_PORT_MII0; + else if (port_id == 1) + return PRUETH_PORT_MII1; + else + return PRUETH_PORT_INVALID; +} + +/* get MAC instance corresponding to eth_node name */ +static int icssm_prueth_node_mac(struct device_node *eth_node) +{ + u32 port_id; + int ret; + + ret = of_property_read_u32(eth_node, "reg", &port_id); + if (ret) + return ret; + + if (port_id == 0) + return PRUETH_MAC0; + else if (port_id == 1) + return PRUETH_MAC1; + else + return PRUETH_MAC_INVALID; +} + +static int icssm_prueth_netdev_init(struct prueth *prueth, + struct device_node *eth_node) +{ + struct prueth_emac *emac; + struct net_device *ndev; + enum prueth_port port; + enum prueth_mac mac; + int ret; + + port = icssm_prueth_node_port(eth_node); + if (port == PRUETH_PORT_INVALID) + return -EINVAL; + + mac = icssm_prueth_node_mac(eth_node); + if (mac == PRUETH_MAC_INVALID) + return -EINVAL; + + ndev = devm_alloc_etherdev(prueth->dev, sizeof(*emac)); + if (!ndev) + return -ENOMEM; + + SET_NETDEV_DEV(ndev, prueth->dev); + emac = netdev_priv(ndev); + prueth->emac[mac] = emac; + emac->prueth = prueth; + emac->ndev = ndev; + emac->port_id = port; + + /* by default eth_type is EMAC */ + switch (port) { + case PRUETH_PORT_MII0: + emac->pru = prueth->pru0; + break; + case PRUETH_PORT_MII1: + emac->pru = prueth->pru1; + break; + default: + return -EINVAL; + } + /* get mac address from DT and set private and netdev addr */ + ret = of_get_ethdev_address(eth_node, ndev); + if (!is_valid_ether_addr(ndev->dev_addr)) { + eth_hw_addr_random(ndev); + dev_warn(prueth->dev, "port %d: using random MAC addr: %pM\n", + port, ndev->dev_addr); + } + ether_addr_copy(emac->mac_addr, ndev->dev_addr); + + /* connect PHY */ + emac->phydev = of_phy_get_and_connect(ndev, eth_node, + icssm_emac_adjust_link); + if (!emac->phydev) { + dev_dbg(prueth->dev, "PHY connection failed\n"); + ret = -EPROBE_DEFER; + goto free; + } + + /* remove unsupported modes */ + phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_10baseT_Full_BIT); + + phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_10baseT_Half_BIT); + phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_100baseT_Half_BIT); + + phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_Pause_BIT); + phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_Asym_Pause_BIT); + + ndev->netdev_ops = &emac_netdev_ops; + + return 0; +free: + prueth->emac[mac] = NULL; + + return ret; +} + +static void icssm_prueth_netdev_exit(struct prueth *prueth, + struct device_node *eth_node) +{ + struct prueth_emac *emac; + enum prueth_mac mac; + + mac = icssm_prueth_node_mac(eth_node); + if (mac == PRUETH_MAC_INVALID) + return; + + emac = prueth->emac[mac]; + if (!emac) + return; + + phy_disconnect(emac->phydev); + + prueth->emac[mac] = NULL; +} + +static int icssm_prueth_probe(struct platform_device *pdev) +{ + struct device_node *eth0_node = NULL, *eth1_node = NULL; + struct device_node *eth_node, *eth_ports_node; + enum pruss_pru_id pruss_id0, pruss_id1; + struct device *dev = &pdev->dev; + struct device_node *np; + struct prueth *prueth; + int i, ret; + + np = dev->of_node; + if (!np) + return -ENODEV; /* we don't support non DT */ + + prueth = devm_kzalloc(dev, sizeof(*prueth), GFP_KERNEL); + if (!prueth) + return -ENOMEM; + + platform_set_drvdata(pdev, prueth); + prueth->dev = dev; + prueth->fw_data = device_get_match_data(dev); + + eth_ports_node = of_get_child_by_name(np, "ethernet-ports"); + if (!eth_ports_node) + return -ENOENT; + + for_each_child_of_node(eth_ports_node, eth_node) { + u32 reg; + + if (strcmp(eth_node->name, "ethernet-port")) + continue; + ret = of_property_read_u32(eth_node, "reg", ®); + if (ret < 0) { + dev_err(dev, "%pOF error reading port_id %d\n", + eth_node, ret); + } + + of_node_get(eth_node); + + if (reg == 0) { + eth0_node = eth_node; + if (!of_device_is_available(eth0_node)) { + of_node_put(eth0_node); + eth0_node = NULL; + } + } else if (reg == 1) { + eth1_node = eth_node; + if (!of_device_is_available(eth1_node)) { + of_node_put(eth1_node); + eth1_node = NULL; + } + } else { + dev_err(dev, "port reg should be 0 or 1\n"); + } + } + + of_node_put(eth_ports_node); + + /* At least one node must be present and available else we fail */ + if (!eth0_node && !eth1_node) { + dev_err(dev, "neither port0 nor port1 node available\n"); + return -ENODEV; + } + + if (eth0_node == eth1_node) { + dev_err(dev, "port0 and port1 can't have same reg\n"); + of_node_put(eth0_node); + return -ENODEV; + } + + prueth->eth_node[PRUETH_MAC0] = eth0_node; + prueth->eth_node[PRUETH_MAC1] = eth1_node; + + if (eth0_node) { + prueth->pru0 = pru_rproc_get(np, 0, &pruss_id0); + if (IS_ERR(prueth->pru0)) { + ret = PTR_ERR(prueth->pru0); + if (ret != -EPROBE_DEFER) + dev_err(dev, "unable to get PRU0: %d\n", ret); + goto put_pru; + } + } + + if (eth1_node) { + prueth->pru1 = pru_rproc_get(np, 1, &pruss_id1); + if (IS_ERR(prueth->pru1)) { + ret = PTR_ERR(prueth->pru1); + if (ret != -EPROBE_DEFER) + dev_err(dev, "unable to get PRU1: %d\n", ret); + goto put_pru; + } + } + + /* setup netdev interfaces */ + if (eth0_node) { + ret = icssm_prueth_netdev_init(prueth, eth0_node); + if (ret) { + if (ret != -EPROBE_DEFER) { + dev_err(dev, "netdev init %s failed: %d\n", + eth0_node->name, ret); + } + goto put_pru; + } + } + + if (eth1_node) { + ret = icssm_prueth_netdev_init(prueth, eth1_node); + if (ret) { + if (ret != -EPROBE_DEFER) { + dev_err(dev, "netdev init %s failed: %d\n", + eth1_node->name, ret); + } + goto netdev_exit; + } + } + + /* register the network devices */ + if (eth0_node) { + ret = register_netdev(prueth->emac[PRUETH_MAC0]->ndev); + if (ret) { + dev_err(dev, "can't register netdev for port MII0"); + goto netdev_exit; + } + + prueth->registered_netdevs[PRUETH_MAC0] = + prueth->emac[PRUETH_MAC0]->ndev; + } + + if (eth1_node) { + ret = register_netdev(prueth->emac[PRUETH_MAC1]->ndev); + if (ret) { + dev_err(dev, "can't register netdev for port MII1"); + goto netdev_unregister; + } + + prueth->registered_netdevs[PRUETH_MAC1] = + prueth->emac[PRUETH_MAC1]->ndev; + } + + if (eth1_node) + of_node_put(eth1_node); + if (eth0_node) + of_node_put(eth0_node); + return 0; + +netdev_unregister: + for (i = 0; i < PRUETH_NUM_MACS; i++) { + if (!prueth->registered_netdevs[i]) + continue; + unregister_netdev(prueth->registered_netdevs[i]); + } + +netdev_exit: + for (i = 0; i < PRUETH_NUM_MACS; i++) { + eth_node = prueth->eth_node[i]; + if (!eth_node) + continue; + + icssm_prueth_netdev_exit(prueth, eth_node); + } + +put_pru: + if (eth1_node) { + if (prueth->pru1) + pru_rproc_put(prueth->pru1); + of_node_put(eth1_node); + } + + if (eth0_node) { + if (prueth->pru0) + pru_rproc_put(prueth->pru0); + of_node_put(eth0_node); + } + + return ret; +} + +static void icssm_prueth_remove(struct platform_device *pdev) +{ + struct prueth *prueth = platform_get_drvdata(pdev); + struct device_node *eth_node; + int i; + + for (i = 0; i < PRUETH_NUM_MACS; i++) { + if (!prueth->registered_netdevs[i]) + continue; + unregister_netdev(prueth->registered_netdevs[i]); + } + + for (i = 0; i < PRUETH_NUM_MACS; i++) { + eth_node = prueth->eth_node[i]; + if (!eth_node) + continue; + + icssm_prueth_netdev_exit(prueth, eth_node); + of_node_put(eth_node); + } + + pruss_put(prueth->pruss); + + if (prueth->eth_node[PRUETH_MAC0]) + pru_rproc_put(prueth->pru1); + if (prueth->eth_node[PRUETH_MAC1]) + pru_rproc_put(prueth->pru0); +} + +/* AM57xx SoC-specific firmware data */ +static struct prueth_private_data am57xx_prueth_pdata = { + .fw_pru[PRUSS_PRU0] = { + .fw_name[PRUSS_ETHTYPE_EMAC] = + "ti-pruss/am57xx-pru0-prueth-fw.elf", + }, + .fw_pru[PRUSS_PRU1] = { + .fw_name[PRUSS_ETHTYPE_EMAC] = + "ti-pruss/am57xx-pru1-prueth-fw.elf", + }, +}; + +static const struct of_device_id prueth_dt_match[] = { + { .compatible = "ti,am57-prueth", .data = &am57xx_prueth_pdata, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, prueth_dt_match); + +static struct platform_driver prueth_driver = { + .probe = icssm_prueth_probe, + .remove = icssm_prueth_remove, + .driver = { + .name = "prueth", + .of_match_table = prueth_dt_match, + }, +}; +module_platform_driver(prueth_driver); + +MODULE_AUTHOR("Roger Quadros "); +MODULE_AUTHOR("Andrew F. Davis "); +MODULE_DESCRIPTION("PRUSS ICSSM Ethernet Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth.h b/drivers/net/ethernet/ti/icssm/icssm_prueth.h new file mode 100644 index 000000000000..7f857edc6eb2 --- /dev/null +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Texas Instruments ICSSM Ethernet driver + * + * Copyright (C) 2018-2022 Texas Instruments Incorporated - https://www.ti.com/ + * + */ + +#ifndef __NET_TI_PRUETH_H +#define __NET_TI_PRUETH_H + +#include +#include +#include +#include + +/* PRU Ethernet Type - Ethernet functionality (protocol + * implemented) provided by the PRU firmware being loaded. + */ +enum pruss_ethtype { + PRUSS_ETHTYPE_EMAC = 0, + PRUSS_ETHTYPE_HSR, + PRUSS_ETHTYPE_PRP, + PRUSS_ETHTYPE_SWITCH, + PRUSS_ETHTYPE_MAX, +}; + +/* In switch mode there are 3 real ports i.e. 3 mac addrs. + * however Linux sees only the host side port. The other 2 ports + * are the switch ports. + * In emac mode there are 2 real ports i.e. 2 mac addrs. + * Linux sees both the ports. + */ +enum prueth_port { + PRUETH_PORT_HOST = 0, /* host side port */ + PRUETH_PORT_MII0, /* physical port MII 0 */ + PRUETH_PORT_MII1, /* physical port MII 1 */ + PRUETH_PORT_INVALID, /* Invalid prueth port */ +}; + +enum prueth_mac { + PRUETH_MAC0 = 0, + PRUETH_MAC1, + PRUETH_NUM_MACS, + PRUETH_MAC_INVALID, +}; + +/** + * struct prueth_firmware - PRU Ethernet FW data + * @fw_name: firmware names of firmware to run on PRU + */ +struct prueth_firmware { + const char *fw_name[PRUSS_ETHTYPE_MAX]; +}; + +/** + * struct prueth_private_data - PRU Ethernet private data + * @fw_pru: firmware names to be used for PRUSS ethernet usecases + * @support_lre: boolean to indicate if lre is enabled + * @support_switch: boolean to indicate if switch is enabled + */ +struct prueth_private_data { + const struct prueth_firmware fw_pru[PRUSS_NUM_PRUS]; + bool support_lre; + bool support_switch; +}; + +/* data for each emac port */ +struct prueth_emac { + struct prueth *prueth; + struct net_device *ndev; + + struct rproc *pru; + struct phy_device *phydev; + + int link; + int speed; + int duplex; + + enum prueth_port port_id; + const char *phy_id; + u8 mac_addr[6]; + phy_interface_t phy_if; + spinlock_t lock; /* serialize access */ +}; + +struct prueth { + struct device *dev; + struct pruss *pruss; + struct rproc *pru0, *pru1; + + const struct prueth_private_data *fw_data; + struct prueth_fw_offsets *fw_offsets; + + struct device_node *eth_node[PRUETH_NUM_MACS]; + struct prueth_emac *emac[PRUETH_NUM_MACS]; + struct net_device *registered_netdevs[PRUETH_NUM_MACS]; + + unsigned int eth_type; +}; +#endif /* __NET_TI_PRUETH_H */ From patchwork Fri Jan 24 12:37:01 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Basharath Hussain Khaja X-Patchwork-Id: 859722 Received: from server.wki.vra.mybluehostin.me (server.wki.vra.mybluehostin.me [162.240.238.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 518D621A422; Fri, 24 Jan 2025 12:37:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=162.240.238.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1737722271; cv=none; b=LQi/qzxP738hkROu0k8NyCVHJ3h60GtP5ojlsDCIDOKGe/m25fyjETyTxCijYzabIkVdsj5s0sspjUPRNSQTneBmzMFUmyIyl1YG73JH1pdcsWYIqbz/tlFw5eVvn/9OlBoc0ADN2IO0W6ELnLpf+bc3NfhzuPES87q8JUTWn4w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1737722271; c=relaxed/simple; bh=IEdqGpWBlO7BcdWmpHM6s/ypUwbyRvrlgR5CxA2anIY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=sokXIcV+9jQtsor6Id0t91WL7GYZQG339ta1r9KhPfi6orYRF8e/0bRvXirAHe6XsDB66b0ptGM9HTKy5WQQml+5a1yi9F6M6KDhwvJng7JnATVotGobptWhvh+k9DOlJHPAJITLK0XK4WqFQrga6ThN90KAVNPrp5PWlxzk0XU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=couthit.com; spf=pass smtp.mailfrom=couthit.com; dkim=pass (2048-bit key) header.d=couthit.com header.i=@couthit.com header.b=GW0ENSsG; arc=none smtp.client-ip=162.240.238.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=couthit.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=couthit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=couthit.com header.i=@couthit.com header.b="GW0ENSsG" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=couthit.com ; s=default; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=BKd69uRZUc8HDskr4h8ath83MaEEYQm7FQWqxS7CABk=; b=GW0ENSsG5ov/uD20URKbCKydaE 4lpRtZ/K1Y1k9x7bpimdR3UxdqPLcNHc6fN9iFvsDS1ohjtzfQYd2pM8jhlqrJgNXXPuwdfFM1sHp WThOSsO3gKs2VV7u6DE2kJe+ZAeKGcFPnkdGvXMPsPIGHRr5Juf+F6mmOHcuC1W9S4UUKc1KpstYP gGtvdkVcoHg0miWRBxh4x3rmoLwRutjbRkq0eur8FtgU5nKRtVQSQULI0Eam2eBQHnOqK2WvOlJa5 ue/dpSuURNnebuRbgOr+opticXB6to2iLy+mKeD8KdvsqsninU5LUGGEJSZf87HCd0uxoRZenhkMB 6d7JbP8w==; Received: from [122.175.9.182] (port=52302 helo=cypher.couthit.local) by server.wki.vra.mybluehostin.me with esmtpa (Exim 4.96.2) (envelope-from ) id 1tbIwR-0004SM-2T; Fri, 24 Jan 2025 18:07:44 +0530 From: Basharath Hussain Khaja To: danishanwar@ti.com, rogerq@kernel.org, andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, nm@ti.com, ssantosh@kernel.org, tony@atomide.com, richardcochran@gmail.com, parvathi@couthit.com, basharath@couthit.com, schnelle@linux.ibm.com, rdunlap@infradead.org, diogo.ivo@siemens.com, m-karicheri2@ti.com, horms@kernel.org, jacob.e.keller@intel.com, m-malladi@ti.com, javier.carrasco.cruz@gmail.com, afd@ti.com, s-anna@ti.com Cc: linux-arm-kernel@lists.infradead.org, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-omap@vger.kernel.org, pratheesh@ti.com, prajith@ti.com, vigneshr@ti.com, praneeth@ti.com, srk@ti.com, rogerq@ti.com, krishna@couthit.com, pmohan@couthit.com, mohan@couthit.com Subject: [RFC v2 PATCH 04/10] net: ti: prueth: Adds link detection, RX and TX support. Date: Fri, 24 Jan 2025 18:07:01 +0530 Message-Id: <20250124123707.1457639-5-basharath@couthit.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250124122353.1457174-1-basharath@couthit.com> References: <20250124122353.1457174-1-basharath@couthit.com> Precedence: bulk X-Mailing-List: linux-omap@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-AntiAbuse: This header was added to track abuse, please include it with any abuse report X-AntiAbuse: Primary Hostname - server.wki.vra.mybluehostin.me X-AntiAbuse: Original Domain - vger.kernel.org X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] X-AntiAbuse: Sender Address Domain - couthit.com X-Get-Message-Sender-Via: server.wki.vra.mybluehostin.me: authenticated_id: basharath@couthit.com X-Authenticated-Sender: server.wki.vra.mybluehostin.me: basharath@couthit.com X-Source: X-Source-Args: X-Source-Dir: From: Roger Quadros Changes corresponding to link configuration such as speed and duplexity. IRQ and handler initializations are performed for packet reception.Firmware receives the packet from the wire and stores it into OCMC queue. Next, it notifies the CPU via interrupt. Upon receiving the interrupt CPU will service the IRQ and packet will be processed by pushing the newly allocated SKB to upper layers. When the user application want to transmit a packet, it will invoke sys_send() which will inturn invoke the PRUETH driver, then it will write the packet into OCMC queues. PRU firmware will pick up the packet and transmit it on to the wire. Signed-off-by: Roger Quadros Signed-off-by: Andrew F. Davis Signed-off-by: Parvathi Pudi Signed-off-by: Basharath Hussain Khaja --- drivers/net/ethernet/ti/icssm/icssm_prueth.c | 599 ++++++++++++++++++- drivers/net/ethernet/ti/icssm/icssm_prueth.h | 46 ++ 2 files changed, 640 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth.c b/drivers/net/ethernet/ti/icssm/icssm_prueth.c index 82ed0e3a0d88..0ba1d16a7a15 100644 --- a/drivers/net/ethernet/ti/icssm/icssm_prueth.c +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth.c @@ -36,6 +36,13 @@ #define TX_START_DELAY 0x40 #define TX_CLK_DELAY_100M 0x6 +static inline void icssm_prueth_write_reg(struct prueth *prueth, + enum prueth_mem region, + unsigned int reg, u32 val) +{ + writel_relaxed(val, prueth->mem[region].va + reg); +} + /* Below macro is for 1528 Byte Frame support, to Allow even with * Redundancy tag */ @@ -299,18 +306,31 @@ static void icssm_prueth_init_ethernet_mode(struct prueth *prueth) icssm_prueth_hostinit(prueth); } -static int icssm_prueth_emac_config(struct prueth_emac *emac) +static void icssm_prueth_port_enable(struct prueth_emac *emac, bool enable) { struct prueth *prueth = emac->prueth; + void __iomem *port_ctrl; + void __iomem *ram; - /* PRU needs local shared RAM address for C28 */ - u32 sharedramaddr = ICSS_LOCAL_SHARED_RAM; + ram = prueth->mem[emac->dram].va; + port_ctrl = ram + PORT_CONTROL_ADDR; + writeb(!!enable, port_ctrl); +} - /* PRU needs real global OCMC address for C30*/ - u32 ocmcaddr = (u32)prueth->mem[PRUETH_MEM_OCMC].pa; +static int icssm_prueth_emac_config(struct prueth_emac *emac) +{ + struct prueth *prueth = emac->prueth; + u32 sharedramaddr, ocmcaddr; void __iomem *dram_base; void __iomem *mac_addr; void __iomem *dram; + void __iomem *sram; + + /* PRU needs local shared RAM address for C28 */ + sharedramaddr = ICSS_LOCAL_SHARED_RAM; + /* PRU needs real global OCMC address for C30*/ + ocmcaddr = (u32)prueth->mem[PRUETH_MEM_OCMC].pa; + sram = prueth->mem[PRUETH_MEM_SHARED_RAM].va; /* Clear data RAM */ icssm_prueth_clearmem(prueth, emac->dram); @@ -331,6 +351,9 @@ static int icssm_prueth_emac_config(struct prueth_emac *emac) memcpy_toio(dram, queue_descs[emac->port_id], sizeof(queue_descs[emac->port_id])); + emac->rx_queue_descs = sram + HOST_QUEUE_DESC_OFFSET; + emac->tx_queue_descs = dram; + /* Set in constant table C28 of PRU0 to ICSS Shared memory */ pru_rproc_set_ctable(emac->pru, PRU_C28, sharedramaddr); @@ -340,6 +363,40 @@ static int icssm_prueth_emac_config(struct prueth_emac *emac) return 0; } +/* update phy/port status information for firmware */ +static void icssm_emac_update_phystatus(struct prueth_emac *emac) +{ + struct prueth *prueth = emac->prueth; + u32 phy_speed, port_status = 0; + enum prueth_mem region; + u32 delay; + + region = emac->dram; + phy_speed = emac->speed; + icssm_prueth_write_reg(prueth, region, PHY_SPEED_OFFSET, phy_speed); + + delay = TX_CLK_DELAY_100M; + + delay = delay << PRUSS_MII_RT_TXCFG_TX_CLK_DELAY_SHIFT; + + if (emac->port_id) { + regmap_update_bits(prueth->mii_rt, + PRUSS_MII_RT_TXCFG1, + PRUSS_MII_RT_TXCFG_TX_CLK_DELAY_MASK, + delay); + } else { + regmap_update_bits(prueth->mii_rt, + PRUSS_MII_RT_TXCFG0, + PRUSS_MII_RT_TXCFG_TX_CLK_DELAY_MASK, + delay); + } + + if (emac->link) + port_status |= PORT_LINK_MASK; + + writeb(port_status, prueth->mem[region].va + PORT_STATUS_OFFSET); +} + /* called back by PHY layer if there is change in link state of hw port*/ static void icssm_emac_adjust_link(struct net_device *ndev) { @@ -369,6 +426,8 @@ static void icssm_emac_adjust_link(struct net_device *ndev) emac->link = 0; } + icssm_emac_update_phystatus(emac); + if (new_state) phy_print_status(phydev); @@ -384,6 +443,374 @@ static void icssm_emac_adjust_link(struct net_device *ndev) spin_unlock_irqrestore(&emac->lock, flags); } +/** + * icssm_prueth_tx_enqueue - queue a packet to firmware for transmission + * + * @emac: EMAC data structure + * @skb: packet data buffer + * @queue_id: priority queue id + * + * Return: 0 (Success) + */ +static int icssm_prueth_tx_enqueue(struct prueth_emac *emac, + struct sk_buff *skb, + enum prueth_queue_id queue_id) +{ + struct prueth_queue_desc __iomem *queue_desc; + const struct prueth_queue_info *txqueue; + u16 bd_rd_ptr, bd_wr_ptr, update_wr_ptr; + struct net_device *ndev = emac->ndev; + unsigned int buffer_desc_count; + int free_blocks, update_block; + bool buffer_wrapped = false; + int write_block, read_block; + void *src_addr, *dst_addr; + int pkt_block_size; + void __iomem *dram; + int txport, pktlen; + u32 wr_buf_desc; + void *ocmc_ram; + + dram = emac->prueth->mem[emac->dram].va; + if (eth_skb_pad(skb)) { + if (netif_msg_tx_err(emac) && net_ratelimit()) + netdev_err(ndev, "packet pad failed\n"); + return -ENOMEM; + } + + /* which port to tx: MII0 or MII1 */ + txport = emac->tx_port_queue; + src_addr = skb->data; + pktlen = skb->len; + /* Get the tx queue */ + queue_desc = emac->tx_queue_descs + queue_id; + txqueue = &queue_infos[txport][queue_id]; + + buffer_desc_count = txqueue->buffer_desc_end - + txqueue->buffer_desc_offset; + buffer_desc_count /= BD_SIZE; + buffer_desc_count++; + + bd_rd_ptr = readw(&queue_desc->rd_ptr); + bd_wr_ptr = readw(&queue_desc->wr_ptr); + + /* the PRU firmware deals mostly in pointers already + * offset into ram, we would like to deal in indexes + * within the queue we are working with for code + * simplicity, calculate this here + */ + write_block = (bd_wr_ptr - txqueue->buffer_desc_offset) / BD_SIZE; + read_block = (bd_rd_ptr - txqueue->buffer_desc_offset) / BD_SIZE; + if (write_block > read_block) { + free_blocks = buffer_desc_count - write_block; + free_blocks += read_block; + } else if (write_block < read_block) { + free_blocks = read_block - write_block; + } else { /* they are all free */ + free_blocks = buffer_desc_count; + } + + pkt_block_size = DIV_ROUND_UP(pktlen, ICSS_BLOCK_SIZE); + if (pkt_block_size > free_blocks) /* out of queue space */ + return -ENOBUFS; + + /* calculate end BD address post write */ + update_block = write_block + pkt_block_size; + + /* Check for wrap around */ + if (update_block >= buffer_desc_count) { + update_block %= buffer_desc_count; + buffer_wrapped = true; + } + + /* OCMC RAM is not cached and write order is not important */ + ocmc_ram = (__force void *)emac->prueth->mem[PRUETH_MEM_OCMC].va; + dst_addr = ocmc_ram + txqueue->buffer_offset + + (write_block * ICSS_BLOCK_SIZE); + + /* Copy the data from socket buffer(DRAM) to PRU buffers(OCMC) */ + if (buffer_wrapped) { /* wrapped around buffer */ + int bytes = (buffer_desc_count - write_block) * ICSS_BLOCK_SIZE; + int remaining; + + /* bytes is integral multiple of ICSS_BLOCK_SIZE but + * entire packet may have fit within the last BD + * if pkt_info.length is not integral multiple of + * ICSS_BLOCK_SIZE + */ + if (pktlen < bytes) + bytes = pktlen; + + /* copy non-wrapped part */ + memcpy(dst_addr, src_addr, bytes); + + /* copy wrapped part */ + src_addr += bytes; + remaining = pktlen - bytes; + dst_addr = ocmc_ram + txqueue->buffer_offset; + memcpy(dst_addr, src_addr, remaining); + } else { + memcpy(dst_addr, src_addr, pktlen); + } + + /* update first buffer descriptor */ + wr_buf_desc = (pktlen << PRUETH_BD_LENGTH_SHIFT) & + PRUETH_BD_LENGTH_MASK; + writel(wr_buf_desc, dram + bd_wr_ptr); + + /* update the write pointer in this queue descriptor, the firmware + * polls for this change so this will signal the start of transmission + */ + update_wr_ptr = txqueue->buffer_desc_offset + (update_block * BD_SIZE); + writew(update_wr_ptr, &queue_desc->wr_ptr); + + return 0; +} + +void icssm_parse_packet_info(struct prueth *prueth, u32 buffer_descriptor, + struct prueth_packet_info *pkt_info) +{ + pkt_info->start_offset = false; + + pkt_info->shadow = !!(buffer_descriptor & PRUETH_BD_SHADOW_MASK); + pkt_info->port = (buffer_descriptor & PRUETH_BD_PORT_MASK) >> + PRUETH_BD_PORT_SHIFT; + pkt_info->length = (buffer_descriptor & PRUETH_BD_LENGTH_MASK) >> + PRUETH_BD_LENGTH_SHIFT; + pkt_info->broadcast = !!(buffer_descriptor & PRUETH_BD_BROADCAST_MASK); + pkt_info->error = !!(buffer_descriptor & PRUETH_BD_ERROR_MASK); + pkt_info->sv_frame = false; + pkt_info->lookup_success = !!(buffer_descriptor & + PRUETH_BD_LOOKUP_SUCCESS_MASK); + pkt_info->flood = !!(buffer_descriptor & PRUETH_BD_SW_FLOOD_MASK); + pkt_info->timestamp = !!(buffer_descriptor & PRUETH_BD_TIMESTAMP_MASK); +} + +/* get packet from queue + * negative for error + */ +int icssm_emac_rx_packet(struct prueth_emac *emac, u16 *bd_rd_ptr, + struct prueth_packet_info *pkt_info, + const struct prueth_queue_info *rxqueue) +{ + struct net_device *ndev = emac->ndev; + unsigned int buffer_desc_count; + int read_block, update_block; + unsigned int actual_pkt_len; + bool buffer_wrapped = false; + void *src_addr, *dst_addr; + u16 start_offset = 0; + struct sk_buff *skb; + int pkt_block_size; + void *ocmc_ram; + + /* the PRU firmware deals mostly in pointers already + * offset into ram, we would like to deal in indexes + * within the queue we are working with for code + * simplicity, calculate this here + */ + buffer_desc_count = rxqueue->buffer_desc_end - + rxqueue->buffer_desc_offset; + buffer_desc_count /= BD_SIZE; + buffer_desc_count++; + read_block = (*bd_rd_ptr - rxqueue->buffer_desc_offset) / BD_SIZE; + pkt_block_size = DIV_ROUND_UP(pkt_info->length, ICSS_BLOCK_SIZE); + + /* calculate end BD address post read */ + update_block = read_block + pkt_block_size; + + /* Check for wrap around */ + if (update_block >= buffer_desc_count) { + update_block %= buffer_desc_count; + if (update_block) + buffer_wrapped = true; + } + + /* calculate new pointer in ram */ + *bd_rd_ptr = rxqueue->buffer_desc_offset + (update_block * BD_SIZE); + + /* Pkt len w/ HSR tag removed, If applicable */ + actual_pkt_len = pkt_info->length - start_offset; + + /* Allocate a socket buffer for this packet */ + skb = netdev_alloc_skb_ip_align(ndev, actual_pkt_len); + if (!skb) { + if (netif_msg_rx_err(emac) && net_ratelimit()) + netdev_err(ndev, "failed rx buffer alloc\n"); + return -ENOMEM; + } + + dst_addr = skb->data; + + /* OCMC RAM is not cached and read order is not important */ + ocmc_ram = (__force void *)emac->prueth->mem[PRUETH_MEM_OCMC].va; + + /* Get the start address of the first buffer from + * the read buffer description + */ + src_addr = ocmc_ram + rxqueue->buffer_offset + + (read_block * ICSS_BLOCK_SIZE); + src_addr += start_offset; + + /* Copy the data from PRU buffers(OCMC) to socket buffer(DRAM) */ + if (buffer_wrapped) { /* wrapped around buffer */ + int bytes = (buffer_desc_count - read_block) * ICSS_BLOCK_SIZE; + int remaining; + /* bytes is integral multiple of ICSS_BLOCK_SIZE but + * entire packet may have fit within the last BD + * if pkt_info.length is not integral multiple of + * ICSS_BLOCK_SIZE + */ + if (pkt_info->length < bytes) + bytes = pkt_info->length; + + /* If applicable, account for the HSR tag removed */ + bytes -= start_offset; + + /* copy non-wrapped part */ + memcpy(dst_addr, src_addr, bytes); + + /* copy wrapped part */ + dst_addr += bytes; + remaining = actual_pkt_len - bytes; + + src_addr = ocmc_ram + rxqueue->buffer_offset; + memcpy(dst_addr, src_addr, remaining); + src_addr += remaining; + } else { + memcpy(dst_addr, src_addr, actual_pkt_len); + src_addr += actual_pkt_len; + } + + if (!pkt_info->sv_frame) { + skb_put(skb, actual_pkt_len); + + /* send packet up the stack */ + skb->protocol = eth_type_trans(skb, ndev); + local_bh_disable(); + netif_receive_skb(skb); + local_bh_enable(); + } else { + dev_kfree_skb_any(skb); + } + + /* update stats */ + ndev->stats.rx_bytes += actual_pkt_len; + ndev->stats.rx_packets++; + + return 0; +} + +/** + * icssm_emac_rx_thread - EMAC Rx interrupt thread handler + * @irq: interrupt number + * @dev_id: pointer to net_device + * + * EMAC Rx Interrupt thread handler - function to process the rx frames in a + * irq thread function. There is only limited buffer at the ingress to + * queue the frames. As the frames are to be emptied as quickly as + * possible to avoid overflow, irq thread is necessary. Current implementation + * based on NAPI poll results in packet loss due to overflow at + * the ingress queues. Industrial use case requires loss free packet + * processing. Tests shows that with threaded irq based processing, + * no overflow happens when receiving at ~92Mbps for MTU sized frames and thus + * meet the requirement for industrial use case. + * + * Return: interrupt handled condition + */ +static irqreturn_t icssm_emac_rx_thread(int irq, void *dev_id) +{ + struct net_device *ndev = (struct net_device *)dev_id; + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth_queue_desc __iomem *queue_desc; + const struct prueth_queue_info *rxqueue; + struct prueth *prueth = emac->prueth; + struct net_device_stats *ndevstats; + struct prueth_packet_info pkt_info; + int start_queue, end_queue; + void __iomem *shared_ram; + u16 bd_rd_ptr, bd_wr_ptr; + u16 update_rd_ptr; + u8 overflow_cnt; + u32 rd_buf_desc; + int used = 0; + int i, ret; + + ndevstats = &emac->ndev->stats; + shared_ram = emac->prueth->mem[PRUETH_MEM_SHARED_RAM].va; + + start_queue = emac->rx_queue_start; + end_queue = emac->rx_queue_end; +retry: + /* search host queues for packets */ + for (i = start_queue; i <= end_queue; i++) { + queue_desc = emac->rx_queue_descs + i; + rxqueue = &queue_infos[PRUETH_PORT_HOST][i]; + + overflow_cnt = readb(&queue_desc->overflow_cnt); + if (overflow_cnt > 0) { + emac->ndev->stats.rx_over_errors += overflow_cnt; + /* reset to zero */ + writeb(0, &queue_desc->overflow_cnt); + } + + bd_rd_ptr = readw(&queue_desc->rd_ptr); + bd_wr_ptr = readw(&queue_desc->wr_ptr); + + /* while packets are available in this queue */ + while (bd_rd_ptr != bd_wr_ptr) { + /* get packet info from the read buffer descriptor */ + rd_buf_desc = readl(shared_ram + bd_rd_ptr); + icssm_parse_packet_info(prueth, rd_buf_desc, &pkt_info); + + if (pkt_info.length <= 0) { + /* a packet length of zero will cause us to + * never move the read pointer ahead, locking + * the driver, so we manually have to move it + * to the write pointer, discarding all + * remaining packets in this queue. This should + * never happen. + */ + update_rd_ptr = bd_wr_ptr; + ndevstats->rx_length_errors++; + } else if (pkt_info.length > EMAC_MAX_FRM_SUPPORT) { + /* if the packet is too large we skip it but we + * still need to move the read pointer ahead + * and assume something is wrong with the read + * pointer as the firmware should be filtering + * these packets + */ + update_rd_ptr = bd_wr_ptr; + ndevstats->rx_length_errors++; + } else { + update_rd_ptr = bd_rd_ptr; + ret = icssm_emac_rx_packet(emac, &update_rd_ptr, + &pkt_info, rxqueue); + if (ret) + return IRQ_HANDLED; + used++; + } + + /* after reading the buffer descriptor we clear it + * to prevent improperly moved read pointer errors + * from simply looking like old packets. + */ + writel(0, shared_ram + bd_rd_ptr); + + /* update read pointer in queue descriptor */ + writew(update_rd_ptr, &queue_desc->rd_ptr); + bd_rd_ptr = update_rd_ptr; + } + } + + if (used) { + used = 0; + goto retry; + } + + return IRQ_HANDLED; +} + static int icssm_emac_set_boot_pru(struct prueth_emac *emac, struct net_device *ndev) { @@ -412,6 +839,21 @@ static int icssm_emac_set_boot_pru(struct prueth_emac *emac, netdev_err(ndev, "failed to boot PRU0: %d\n", ret); return ret; } + return ret; +} + +static int icssm_emac_request_irqs(struct prueth_emac *emac) +{ + struct net_device *ndev = emac->ndev; + int ret; + + ret = request_threaded_irq(emac->rx_irq, NULL, icssm_emac_rx_thread, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + ndev->name, ndev); + if (ret) { + netdev_err(ndev, "unable to request RX IRQ\n"); + return ret; + } return ret; } @@ -442,10 +884,27 @@ static int icssm_emac_ndo_open(struct net_device *ndev) if (ret) netdev_err(ndev, "failed to boot PRU: %d\n", ret); + ret = icssm_emac_request_irqs(emac); + if (ret) + goto rproc_shutdown; + /* start PHY */ phy_start(emac->phydev); + + /* enable the port and vlan */ + icssm_prueth_port_enable(emac, true); + prueth->emac_configured |= BIT(emac->port_id); + + if (netif_msg_drv(emac)) + dev_notice(&ndev->dev, "started\n"); + return 0; + +rproc_shutdown: + rproc_shutdown(emac->pru); + + return ret; } /** @@ -459,18 +918,122 @@ static int icssm_emac_ndo_open(struct net_device *ndev) static int icssm_emac_ndo_stop(struct net_device *ndev) { struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + + prueth->emac_configured &= ~BIT(emac->port_id); + + /* disable the mac port */ + icssm_prueth_port_enable(emac, false); /* stop PHY */ phy_stop(emac->phydev); + /* stop the PRU */ rproc_shutdown(emac->pru); + /* free rx and tx interrupts */ + if (emac->tx_irq > 0) + free_irq(emac->tx_irq, ndev); + + free_irq(emac->rx_irq, ndev); + + if (netif_msg_drv(emac)) + dev_notice(&ndev->dev, "stopped\n"); + return 0; } +/* VLAN-tag PCP to priority queue map for EMAC/Switch/HSR/PRP used by driver + * Index is PCP val / 2. + * low - pcp 0..3 maps to Q4 for Host + * high - pcp 4..7 maps to Q3 for Host + * low - pcp 0..3 maps to Q2 (FWD Queue) for PRU-x + * where x = 1 for PRUETH_PORT_MII0 + * 0 for PRUETH_PORT_MII1 + * high - pcp 4..7 maps to Q1 (FWD Queue) for PRU-x + */ +static const unsigned short emac_pcp_tx_priority_queue_map[] = { + PRUETH_QUEUE4, PRUETH_QUEUE4, + PRUETH_QUEUE3, PRUETH_QUEUE3, + PRUETH_QUEUE2, PRUETH_QUEUE2, + PRUETH_QUEUE1, PRUETH_QUEUE1, +}; + +static u16 icssm_prueth_get_tx_queue_id(struct prueth *prueth, + struct sk_buff *skb) +{ + u16 vlan_tci, pcp; + int err; + + err = vlan_get_tag(skb, &vlan_tci); + if (likely(err)) + pcp = 0; + else + pcp = (vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; + + /* Below code (pcp >>= 1) is made common for all + * protocols (i.e., EMAC, RSTP, HSR and PRP)* + * pcp value 0,1 will be updated to 0 mapped to QUEUE4 + * pcp value 2,3 will be updated to 1 mapped to QUEUE4 + * pcp value 4,5 will be updated to 2 mapped to QUEUE3 + * pcp value 6,7 will be updated to 3 mapped to QUEUE3 + */ + pcp >>= 1; + + return emac_pcp_tx_priority_queue_map[pcp]; +} + +/** + * icssm_emac_ndo_start_xmit - EMAC Transmit function + * @skb: SKB pointer + * @ndev: EMAC network adapter + * + * Called by the system to transmit a packet - we queue the packet in + * EMAC hardware transmit queue + * + * Return: success(NETDEV_TX_OK) or error code (typically out of desc's) + */ +static int icssm_emac_ndo_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + int ret; + u16 qid; + + qid = icssm_prueth_get_tx_queue_id(emac->prueth, skb); + ret = icssm_prueth_tx_enqueue(emac, skb, qid); + if (ret) { + if (ret != -ENOBUFS && netif_msg_tx_err(emac) && + net_ratelimit()) + netdev_err(ndev, "packet queue failed: %d\n", ret); + goto fail_tx; + } + + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += skb->len; + dev_kfree_skb_any(skb); + + return NETDEV_TX_OK; + +fail_tx: + if (ret == -ENOBUFS) { + /* no free TX queue */ + if (emac->tx_irq > 0) + netif_stop_queue(ndev); + ret = NETDEV_TX_BUSY; + } else { + /* error */ + ndev->stats.tx_dropped++; + ret = NET_XMIT_DROP; + } + + return ret; +} + static const struct net_device_ops emac_netdev_ops = { .ndo_open = icssm_emac_ndo_open, .ndo_stop = icssm_emac_ndo_stop, + .ndo_start_xmit = icssm_emac_ndo_start_xmit, }; /* get emac_port corresponding to eth_node name */ @@ -540,16 +1103,42 @@ static int icssm_prueth_netdev_init(struct prueth *prueth, /* by default eth_type is EMAC */ switch (port) { case PRUETH_PORT_MII0: + emac->tx_port_queue = PRUETH_PORT_QUEUE_MII0; + + /* packets from MII0 are on queues 1 through 2 */ + emac->rx_queue_start = PRUETH_QUEUE1; + emac->rx_queue_end = PRUETH_QUEUE2; + emac->dram = PRUETH_MEM_DRAM0; emac->pru = prueth->pru0; break; case PRUETH_PORT_MII1: + emac->tx_port_queue = PRUETH_PORT_QUEUE_MII1; + + /* packets from MII1 are on queues 3 through 4 */ + emac->rx_queue_start = PRUETH_QUEUE3; + emac->rx_queue_end = PRUETH_QUEUE4; + emac->dram = PRUETH_MEM_DRAM1; emac->pru = prueth->pru1; break; default: return -EINVAL; } + + emac->rx_irq = of_irq_get_byname(eth_node, "rx"); + if (emac->rx_irq < 0) { + ret = emac->rx_irq; + if (ret != -EPROBE_DEFER) + dev_err(prueth->dev, "could not get rx irq\n"); + goto free; + } + emac->tx_irq = of_irq_get_byname(eth_node, "tx"); + if (emac->tx_irq < 0) { + if (emac->tx_irq != -EPROBE_DEFER) + dev_dbg(prueth->dev, "tx irq not configured\n"); + } + /* get mac address from DT and set private and netdev addr */ ret = of_get_ethdev_address(eth_node, ndev); if (!is_valid_ether_addr(ndev->dev_addr)) { diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth.h b/drivers/net/ethernet/ti/icssm/icssm_prueth.h index 6aaceb418f12..427dc8971d0f 100644 --- a/drivers/net/ethernet/ti/icssm/icssm_prueth.h +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth.h @@ -20,6 +20,12 @@ /* PRUSS local memory map */ #define ICSS_LOCAL_SHARED_RAM 0x00010000 +#define EMAC_MAX_PKTLEN (ETH_HLEN + VLAN_HLEN + ETH_DATA_LEN) +/* Below macro is for 1528 Byte Frame support, to Allow even with + * Redundancy tag + */ +#define EMAC_MAX_FRM_SUPPORT (ETH_HLEN + VLAN_HLEN + ETH_DATA_LEN + \ + ICSSM_LRE_TAG_SIZE) /* PRU Ethernet Type - Ethernet functionality (protocol * implemented) provided by the PRU firmware being loaded. @@ -76,6 +82,32 @@ struct prueth_queue_info { u16 buffer_desc_end; } __packed; +/** + * struct prueth_packet_info - Info about a packet in buffer + * @start_offset: start offset of the frame in the buffer for HSR/PRP + * @shadow: this packet is stored in the collision queue + * @port: port packet is on + * @length: length of packet + * @broadcast: this packet is a broadcast packet + * @error: this packet has an error + * @sv_frame: indicate if the frame is a SV frame for HSR/PRP + * @lookup_success: src mac found in FDB + * @flood: packet is to be flooded + * @timstamp: Specifies if timestamp is appended to the packet + */ +struct prueth_packet_info { + bool start_offset; + bool shadow; + unsigned int port; + unsigned int length; + bool broadcast; + bool error; + bool sv_frame; + bool lookup_success; + bool flood; + bool timestamp; +}; + /* In switch mode there are 3 real ports i.e. 3 mac addrs. * however Linux sees only the host side port. The other 2 ports * are the switch ports. @@ -160,14 +192,22 @@ struct prueth_emac { struct rproc *pru; struct phy_device *phydev; + struct prueth_queue_desc __iomem *rx_queue_descs; + struct prueth_queue_desc __iomem *tx_queue_descs; int link; int speed; int duplex; + int rx_irq; + int tx_irq; + enum prueth_port_queue_id tx_port_queue; + enum prueth_queue_id rx_queue_start; + enum prueth_queue_id rx_queue_end; enum prueth_port port_id; enum prueth_mem dram; const char *phy_id; + u32 msg_enable; u8 mac_addr[6]; phy_interface_t phy_if; spinlock_t lock; /* serialize access */ @@ -191,4 +231,10 @@ struct prueth { unsigned int eth_type; u8 emac_configured; }; + +void icssm_parse_packet_info(struct prueth *prueth, u32 buffer_descriptor, + struct prueth_packet_info *pkt_info); +int icssm_emac_rx_packet(struct prueth_emac *emac, u16 *bd_rd_ptr, + struct prueth_packet_info *pkt_info, + const struct prueth_queue_info *rxqueue); #endif /* __NET_TI_PRUETH_H */ From patchwork Fri Jan 24 13:40:51 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Basharath Hussain Khaja X-Patchwork-Id: 859721 Received: from server.wki.vra.mybluehostin.me (server.wki.vra.mybluehostin.me [162.240.238.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C0B8713BC18; Fri, 24 Jan 2025 13:41:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=162.240.238.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1737726118; cv=none; b=Czo+ZnMRA8Xt4PhTEhPfVy8w8XUw47771TdkaifnLiPUJ6S/QbRFl/EdHUrA1jnkWcV/2WEZ+ML8rK1Xulr4gDDGDzBpMYaeNhMXIK4GHZ4bg9/JCi2S5BjvPng73XZv8PYr3IFF6yk3w5cI5Bvadi6xzYLKhW7cE7OnvcPozdk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1737726118; c=relaxed/simple; bh=9n9BYXOh+9dqbfXqt2xoG8ZNopCE4H3ols6Ym4eDJ/U=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=pxhupqMh1P0mPNxRxZWnIM5DZYzliIVSmlsXfkLtO1nNtyZVptNGyenNpBYhZMzln9INUDFB7r5WVPaACjkc2wY+QSNjTiRd3Ung0iyG0qDYHfAdkMaQt+2TQ1Ymaw+xzAM9LyBqgR5nxN5oYypfb1pmDn7UEZxbdg0pasjBB20= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=couthit.com; spf=pass smtp.mailfrom=couthit.com; dkim=pass (2048-bit key) header.d=couthit.com header.i=@couthit.com header.b=kQsHll9S; arc=none smtp.client-ip=162.240.238.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=couthit.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=couthit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=couthit.com header.i=@couthit.com header.b="kQsHll9S" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=couthit.com ; s=default; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=pX+oprTsTE89eLpN42MA/Y2zuaeCmQ4BlYXMmr2LIY4=; b=kQsHll9STJ/tV05ZGI7GP92itM S+V7HOwuS88BlsVLzqdfT1xfty+VkeG5RavlWC7T50Kmsi5Pk2hrz6l/D+OEE6JPkemzQx5Ue2DAt Gz1n5kOTeBTeUnFXIXcp7GJS3k8xoV6vj2ZqBkwG6FNI/kAHEB+JyQQ5SRUMug+lw1niqWkJb6Nld SChQwSQAetqAUafAFikJAKiGAbAPg3+8Oibcw0xLjuR70//AhkThc6aJCtyGspoQAWGTXCIqAo4TI FYbhTGdTymK4nsQ+qUaoQ5iS0tGzwf51o+NOn7uLeMeb6WtqbFIzQIfAiBFyvUAqmRBcI2eNJKmKZ 3cwczm7A==; Received: from [122.175.9.182] (port=2665 helo=cypher.couthit.local) by server.wki.vra.mybluehostin.me with esmtpa (Exim 4.96.2) (envelope-from ) id 1tbJwU-0005Y4-1Z; Fri, 24 Jan 2025 19:11:50 +0530 From: Basharath Hussain Khaja To: danishanwar@ti.com, rogerq@kernel.org, andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, nm@ti.com, ssantosh@kernel.org, tony@atomide.com, richardcochran@gmail.com, parvathi@couthit.com, basharath@couthit.com, schnelle@linux.ibm.com, rdunlap@infradead.org, diogo.ivo@siemens.com, m-karicheri2@ti.com, horms@kernel.org, jacob.e.keller@intel.com, m-malladi@ti.com, javier.carrasco.cruz@gmail.com, afd@ti.com, s-anna@ti.com Cc: linux-arm-kernel@lists.infradead.org, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-omap@vger.kernel.org, pratheesh@ti.com, prajith@ti.com, vigneshr@ti.com, praneeth@ti.com, srk@ti.com, rogerq@ti.com, krishna@couthit.com, pmohan@couthit.com, mohan@couthit.com Subject: [RFC v2 PATCH 05/10] net: ti: prueth: Adds ethtool support for ICSSM PRUETH Driver Date: Fri, 24 Jan 2025 19:10:51 +0530 Message-Id: <20250124134056.1459060-6-basharath@couthit.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250124122353.1457174-1-basharath@couthit.com> References: <20250124122353.1457174-1-basharath@couthit.com> Precedence: bulk X-Mailing-List: linux-omap@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-AntiAbuse: This header was added to track abuse, please include it with any abuse report X-AntiAbuse: Primary Hostname - server.wki.vra.mybluehostin.me X-AntiAbuse: Original Domain - vger.kernel.org X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] X-AntiAbuse: Sender Address Domain - couthit.com X-Get-Message-Sender-Via: server.wki.vra.mybluehostin.me: authenticated_id: basharath@couthit.com X-Authenticated-Sender: server.wki.vra.mybluehostin.me: basharath@couthit.com X-Source: X-Source-Args: X-Source-Dir: From: Roger Quadros Changes for enabling ethtool support for the newly added PRU Ethernet interfaces. Extends the support for statistics collection from PRU internal memory and displays it in the user space. Along with statistics, enable/disable of features, configuring link speed etc.are now supported. The firmware running on PRU maintains statistics in internal data memory. When requested ethtool collects all the statistics for the specified interface and displays it in the user space. Makefile is updated to include ethtool support into PRUETH driver. Signed-off-by: Roger Quadros Signed-off-by: Andrew F. Davis Signed-off-by: Parvathi Pudi Signed-off-by: Basharath Hussain Khaja --- drivers/net/ethernet/ti/Makefile | 2 +- drivers/net/ethernet/ti/icssm/icssm_ethtool.c | 203 ++++++++++++++++++ drivers/net/ethernet/ti/icssm/icssm_prueth.c | 31 +++ drivers/net/ethernet/ti/icssm/icssm_prueth.h | 127 +++++++++++ 4 files changed, 362 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/ti/icssm/icssm_ethtool.c diff --git a/drivers/net/ethernet/ti/Makefile b/drivers/net/ethernet/ti/Makefile index 93c0a4d0e33a..f21dd11118ab 100644 --- a/drivers/net/ethernet/ti/Makefile +++ b/drivers/net/ethernet/ti/Makefile @@ -4,7 +4,7 @@ # obj-$(CONFIG_TI_PRUETH) += icssm-prueth.o -icssm-prueth-y := icssm/icssm_prueth.o +icssm-prueth-y := icssm/icssm_prueth.o icssm/icssm_ethtool.o obj-$(CONFIG_TI_CPSW) += cpsw-common.o obj-$(CONFIG_TI_DAVINCI_EMAC) += cpsw-common.o diff --git a/drivers/net/ethernet/ti/icssm/icssm_ethtool.c b/drivers/net/ethernet/ti/icssm/icssm_ethtool.c new file mode 100644 index 000000000000..cce3276d5565 --- /dev/null +++ b/drivers/net/ethernet/ti/icssm/icssm_ethtool.c @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Texas Instruments ICSSM Ethernet Driver + * + * Copyright (C) 2018-2022 Texas Instruments Incorporated - https://www.ti.com/ + * + */ + +#include +#include "icssm_prueth.h" + +#define PRUETH_MODULE_VERSION "0.2" +#define PRUETH_MODULE_DESCRIPTION "PRUSS Ethernet driver" + +/* set PRU firmware statistics */ +void icssm_emac_set_stats(struct prueth_emac *emac, + struct port_statistics *pstats) +{ + void __iomem *dram; + + dram = emac->prueth->mem[emac->dram].va; + memcpy_toio(dram + STATISTICS_OFFSET, pstats, STAT_SIZE); +} + +/* get statistics maintained by the PRU firmware into @pstats */ +void icssm_emac_get_stats(struct prueth_emac *emac, + struct port_statistics *pstats) +{ + void __iomem *dram; + + dram = emac->prueth->mem[emac->dram].va; + memcpy_fromio(pstats, dram + STATISTICS_OFFSET, STAT_SIZE); +} + +/** + * icssm_emac_get_drvinfo - Get EMAC driver information + * @ndev: The network adapter + * @info: ethtool info structure containing name and version + * + * Returns EMAC driver information (name and version) + */ +static void icssm_emac_get_drvinfo(struct net_device *ndev, + struct ethtool_drvinfo *info) +{ + strscpy(info->driver, PRUETH_MODULE_DESCRIPTION, sizeof(info->driver)); + strscpy(info->version, PRUETH_MODULE_VERSION, sizeof(info->version)); +} + +/** + * icssm_emac_get_link_ksettings - Get EMAC settings + * @ndev: The network adapter + * @ecmd: ethtool command + * + * Executes ethool get command + * + * Return: 0 (Success) + */ +static int icssm_emac_get_link_ksettings(struct net_device *ndev, + struct ethtool_link_ksettings *ecmd) +{ + return phy_ethtool_get_link_ksettings(ndev, ecmd); +} + +/** + * icssm_emac_set_link_ksettings - Set EMAC settings + * @ndev: The EMAC network adapter + * @ecmd: ethtool command + * + * Executes ethool set command + * + * Return: 0 (Success) + */ +static int +icssm_emac_set_link_ksettings(struct net_device *ndev, + const struct ethtool_link_ksettings *ecmd) +{ + return phy_ethtool_set_link_ksettings(ndev, ecmd); +} + +#define PRUETH_STAT_OFFSET(m) offsetof(struct port_statistics, m) + +static const struct { + char string[ETH_GSTRING_LEN]; + u32 offset; +} prueth_ethtool_stats[] = { + {"txBcast", PRUETH_STAT_OFFSET(tx_bcast)}, + {"txMcast", PRUETH_STAT_OFFSET(tx_mcast)}, + {"txUcast", PRUETH_STAT_OFFSET(tx_ucast)}, + {"txOctets", PRUETH_STAT_OFFSET(tx_octets)}, + {"rxBcast", PRUETH_STAT_OFFSET(rx_bcast)}, + {"rxMcast", PRUETH_STAT_OFFSET(rx_mcast)}, + {"rxUcast", PRUETH_STAT_OFFSET(rx_ucast)}, + {"rxOctets", PRUETH_STAT_OFFSET(rx_octets)}, + + {"tx64byte", PRUETH_STAT_OFFSET(tx64byte)}, + {"tx65_127byte", PRUETH_STAT_OFFSET(tx65_127byte)}, + {"tx128_255byte", PRUETH_STAT_OFFSET(tx128_255byte)}, + {"tx256_511byte", PRUETH_STAT_OFFSET(tx256_511byte)}, + {"tx512_1023byte", PRUETH_STAT_OFFSET(tx512_1023byte)}, + {"tx1024byte", PRUETH_STAT_OFFSET(tx1024byte)}, + {"rx64byte", PRUETH_STAT_OFFSET(rx64byte)}, + {"rx65_127byte", PRUETH_STAT_OFFSET(rx65_127byte)}, + {"rx128_255byte", PRUETH_STAT_OFFSET(rx128_255byte)}, + {"rx256_511byte", PRUETH_STAT_OFFSET(rx256_511byte)}, + {"rx512_1023byte", PRUETH_STAT_OFFSET(rx512_1023byte)}, + {"rx1024byte", PRUETH_STAT_OFFSET(rx1024byte)}, + + {"lateColl", PRUETH_STAT_OFFSET(late_coll)}, + {"singleColl", PRUETH_STAT_OFFSET(single_coll)}, + {"multiColl", PRUETH_STAT_OFFSET(multi_coll)}, + {"excessColl", PRUETH_STAT_OFFSET(excess_coll)}, + + {"rxMisAlignmentFrames", PRUETH_STAT_OFFSET(rx_misalignment_frames)}, + {"stormPrevCounterBC", PRUETH_STAT_OFFSET(stormprev_counter_bc)}, + {"stormPrevCounterMC", PRUETH_STAT_OFFSET(stormprev_counter_mc)}, + {"stormPrevCounterUC", PRUETH_STAT_OFFSET(stormprev_counter_uc)}, + {"macRxError", PRUETH_STAT_OFFSET(mac_rxerror)}, + {"SFDError", PRUETH_STAT_OFFSET(sfd_error)}, + {"defTx", PRUETH_STAT_OFFSET(def_tx)}, + {"macTxError", PRUETH_STAT_OFFSET(mac_txerror)}, + {"rxOverSizedFrames", PRUETH_STAT_OFFSET(rx_oversized_frames)}, + {"rxUnderSizedFrames", PRUETH_STAT_OFFSET(rx_undersized_frames)}, + {"rxCRCFrames", PRUETH_STAT_OFFSET(rx_crc_frames)}, + {"droppedPackets", PRUETH_STAT_OFFSET(dropped_packets)}, + + {"txHWQOverFlow", PRUETH_STAT_OFFSET(tx_hwq_overflow)}, + {"txHWQUnderFlow", PRUETH_STAT_OFFSET(tx_hwq_underflow)}, + {"vlanDropped", PRUETH_STAT_OFFSET(vlan_dropped)}, + {"multicastDropped", PRUETH_STAT_OFFSET(multicast_dropped)}, +}; + +static int icssm_emac_get_sset_count(struct net_device *ndev, int stringset) +{ + int a_size; + + switch (stringset) { + case ETH_SS_STATS: + a_size = ARRAY_SIZE(prueth_ethtool_stats); + + return a_size; + default: + return -EOPNOTSUPP; + } +} + +static void icssm_emac_get_strings(struct net_device *ndev, u32 stringset, + u8 *data) +{ + u8 *p = data; + int i; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < ARRAY_SIZE(prueth_ethtool_stats); i++) { + memcpy(p, prueth_ethtool_stats[i].string, + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + break; + default: + break; + } +} + +static void icssm_emac_get_ethtool_stats(struct net_device *ndev, + struct ethtool_stats *stats, u64 *data) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct port_statistics pstats; + void *ptr; + u32 val; + int i; + + icssm_emac_get_stats(emac, &pstats); + + for (i = 0; i < ARRAY_SIZE(prueth_ethtool_stats); i++) { + ptr = &pstats; + ptr += prueth_ethtool_stats[i].offset; + val = *(u32 *)ptr; + data[i] = val; + } +} + +static void icssm_emac_get_regs(struct net_device *ndev, + struct ethtool_regs *regs, void *p) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + + regs->version = PRUETH_REG_DUMP_GET_VER(prueth); +} + +/* Ethtool support for EMAC adapter */ +const struct ethtool_ops emac_ethtool_ops = { + .get_drvinfo = icssm_emac_get_drvinfo, + .get_link_ksettings = icssm_emac_get_link_ksettings, + .set_link_ksettings = icssm_emac_set_link_ksettings, + .get_link = ethtool_op_get_link, + .get_sset_count = icssm_emac_get_sset_count, + .get_strings = icssm_emac_get_strings, + .get_ethtool_stats = icssm_emac_get_ethtool_stats, + .get_regs = icssm_emac_get_regs, +}; +EXPORT_SYMBOL_GPL(emac_ethtool_ops); diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth.c b/drivers/net/ethernet/ti/icssm/icssm_prueth.c index 0ba1d16a7a15..35dd91c71caa 100644 --- a/drivers/net/ethernet/ti/icssm/icssm_prueth.c +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth.c @@ -880,6 +880,8 @@ static int icssm_emac_ndo_open(struct net_device *ndev) icssm_prueth_emac_config(emac); + icssm_emac_set_stats(emac, &emac->stats); + ret = icssm_emac_set_boot_pru(emac, ndev); if (ret) netdev_err(ndev, "failed to boot PRU: %d\n", ret); @@ -931,6 +933,8 @@ static int icssm_emac_ndo_stop(struct net_device *ndev) /* stop the PRU */ rproc_shutdown(emac->pru); + icssm_emac_get_stats(emac, &emac->stats); + /* free rx and tx interrupts */ if (emac->tx_irq > 0) free_irq(emac->tx_irq, ndev); @@ -1030,10 +1034,36 @@ static int icssm_emac_ndo_start_xmit(struct sk_buff *skb, return ret; } +/** + * icssm_emac_ndo_get_stats - EMAC get statistics function + * @ndev: The EMAC network adapter + * + * Called when system wants to get statistics from the device. + * + * We return the statistics in net_device_stats structure pulled from emac + * + * Return: stats + */ +static struct net_device_stats +*icssm_emac_ndo_get_stats(struct net_device *ndev) +{ + struct net_device_stats *stats = &ndev->stats; + struct prueth_emac *emac = netdev_priv(ndev); + struct port_statistics pstats; + + icssm_emac_get_stats(emac, &pstats); + stats->collisions = pstats.late_coll + pstats.single_coll + + pstats.multi_coll + pstats.excess_coll; + stats->multicast = pstats.rx_mcast; + + return stats; +} + static const struct net_device_ops emac_netdev_ops = { .ndo_open = icssm_emac_ndo_open, .ndo_stop = icssm_emac_ndo_stop, .ndo_start_xmit = icssm_emac_ndo_start_xmit, + .ndo_get_stats = icssm_emac_ndo_get_stats, }; /* get emac_port corresponding to eth_node name */ @@ -1167,6 +1197,7 @@ static int icssm_prueth_netdev_init(struct prueth *prueth, phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_Asym_Pause_BIT); ndev->netdev_ops = &emac_netdev_ops; + ndev->ethtool_ops = &emac_ethtool_ops; return 0; free: diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth.h b/drivers/net/ethernet/ti/icssm/icssm_prueth.h index 427dc8971d0f..d663a842fc38 100644 --- a/drivers/net/ethernet/ti/icssm/icssm_prueth.h +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth.h @@ -27,6 +27,12 @@ #define EMAC_MAX_FRM_SUPPORT (ETH_HLEN + VLAN_HLEN + ETH_DATA_LEN + \ ICSSM_LRE_TAG_SIZE) +#define PRUETH_REG_DUMP_VER 1 + +/* Encoding: 32-16: Reserved, 16-8: Reg dump version, 8-0: Ethertype */ +#define PRUETH_REG_DUMP_GET_VER(x) ((PRUETH_REG_DUMP_VER << 8) | \ + ((x)->eth_type)) + /* PRU Ethernet Type - Ethernet functionality (protocol * implemented) provided by the PRU firmware being loaded. */ @@ -108,6 +114,119 @@ struct prueth_packet_info { bool timestamp; }; +/** + * struct port_statistics - Statistics structure for capturing statistics + * on PRUs + * @tx_bcast: Number of broadcast packets sent + * @tx_mcast:Number of multicast packets sent + * @tx_ucast:Number of unicast packets sent + * + * @tx_octets:Number of undersized frames rcvd + * + * @rx_bcast:Number of broadcast packets rcvd + * @rx_mcast:Number of multicast packets rcvd + * @rx_ucast:Number of unicast packets rcvd + * + * @rx_octets:Number of Rx packets + * + * @tx64byte:Number of 64 byte packets sent + * @tx65_127byte:Number of 65-127 byte packets sent + * @tx128_255byte:Number of 128-255 byte packets sent + * @tx256_511byte:Number of 256-511 byte packets sent + * @tx512_1023byte:Number of 512-1023 byte packets sent + * @tx1024byte:Number of 1024 and larger size packets sent + * + * @rx64byte:Number of 64 byte packets rcvd + * @rx65_127byte:Number of 65-127 byte packets rcvd + * @rx128_255byte:Number of 128-255 byte packets rcvd + * @rx256_511byte:Number of 256-511 byte packets rcvd + * @rx512_1023byte:Number of 512-1023 byte packets rcvd + * @rx1024byte:Number of 1024 and larger size packets rcvd + * + * @late_coll:Number of late collisions(Half Duplex) + * @single_coll:Number of single collisions (Half Duplex) + * @multi_coll:Number of multiple collisions (Half Duplex) + * @excess_coll:Number of excess collisions(Half Duplex) + * + * @rx_misalignment_frames:Number of non multiple of 8 byte frames rcvd + * @stormprev_counter:Number of packets dropped because of Storm Prevention + * @mac_rxerror:Number of MAC receive errors + * @sfd_error:Number of invalid SFD + * @def_tx:Number of transmissions deferred + * @mac_txerror:Number of MAC transmit errors + * @rx_oversized_frames:Number of oversized frames rcvd + * @rx_undersized_frames:Number of undersized frames rcvd + * @rx_crc_frames:Number of CRC error frames rcvd + * @dropped_packets:Number of packets dropped due to link down on opposite port + * + * @tx_hwq_overflow:Hardware Tx Queue (on PRU) over flow count + * @tx_hwq_underflow:Hardware Tx Queue (on PRU) under flow count + * + * @u32 cs_error: Number of carrier sense errors + * @sqe_test_error: Number of MAC receive errors + * + * Above fields are aligned so that it's consistent + * with the memory layout in PRU DRAM, this is to facilitate easy + * memcpy. Don't change the order of the fields. + * + * @vlan_dropped: Number of VLAN tagged packets dropped + * @multicast_dropped: Number of multicast packets dropped + */ +struct port_statistics { + u32 tx_bcast; + u32 tx_mcast; + u32 tx_ucast; + + u32 tx_octets; + + u32 rx_bcast; + u32 rx_mcast; + u32 rx_ucast; + + u32 rx_octets; + + u32 tx64byte; + u32 tx65_127byte; + u32 tx128_255byte; + u32 tx256_511byte; + u32 tx512_1023byte; + u32 tx1024byte; + + u32 rx64byte; + u32 rx65_127byte; + u32 rx128_255byte; + u32 rx256_511byte; + u32 rx512_1023byte; + u32 rx1024byte; + + u32 late_coll; + u32 single_coll; + u32 multi_coll; + u32 excess_coll; + + u32 rx_misalignment_frames; + u32 stormprev_counter_bc; + u32 stormprev_counter_mc; + u32 stormprev_counter_uc; + u32 mac_rxerror; + u32 sfd_error; + u32 def_tx; + u32 mac_txerror; + u32 rx_oversized_frames; + u32 rx_undersized_frames; + u32 rx_crc_frames; + u32 dropped_packets; + + u32 tx_hwq_overflow; + u32 tx_hwq_underflow; + + u32 cs_error; + u32 sqe_test_error; + + u32 vlan_dropped; + u32 multicast_dropped; +} __packed; + /* In switch mode there are 3 real ports i.e. 3 mac addrs. * however Linux sees only the host side port. The other 2 ports * are the switch ports. @@ -194,6 +313,7 @@ struct prueth_emac { struct phy_device *phydev; struct prueth_queue_desc __iomem *rx_queue_descs; struct prueth_queue_desc __iomem *tx_queue_descs; + struct port_statistics stats; /* stats holder when i/f is down */ int link; int speed; @@ -232,9 +352,16 @@ struct prueth { u8 emac_configured; }; +extern const struct ethtool_ops emac_ethtool_ops; + void icssm_parse_packet_info(struct prueth *prueth, u32 buffer_descriptor, struct prueth_packet_info *pkt_info); int icssm_emac_rx_packet(struct prueth_emac *emac, u16 *bd_rd_ptr, struct prueth_packet_info *pkt_info, const struct prueth_queue_info *rxqueue); + +void icssm_emac_set_stats(struct prueth_emac *emac, + struct port_statistics *pstats); +void icssm_emac_get_stats(struct prueth_emac *emac, + struct port_statistics *pstats); #endif /* __NET_TI_PRUETH_H */ From patchwork Fri Jan 24 13:40:53 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Basharath Hussain Khaja X-Patchwork-Id: 859720 Received: from server.wki.vra.mybluehostin.me (server.wki.vra.mybluehostin.me [162.240.238.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0320938DC8; Fri, 24 Jan 2025 13:42:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=162.240.238.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1737726181; cv=none; b=pGvKiqpat5XwRKCrTBNY/McTRromRdudWK0IsQ5eDaXJapC2OamwTED7xOPaeyyBaFJ1NUnqnjELd1bToxR7Ty6I/QpQ/R12yFuN7q0AESP1mjkSmvWpeA1sEmnYvRkHy1bWRlptTkdBPPayZc5LuW78rsCRABkExC5j7qy6YH8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1737726181; c=relaxed/simple; bh=A25a+k3ZXigxQ5/CRVuoUzGbAn51f2PIKKDfb1WJUL4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=R6NeTov+5svPo91PkOlc3rqihJv9/NnOpAlIqLkzCzRmfkTSsXxOfPgsg0uHDh2yhtGU9N6lTvAZ1xWUvGcVpBiA62RyeuRYBKRN80LMhNFv7nB4+RfIADi1Nuo7QjrAc/a5VDxmKxaiSFXsNr8rvuwOoO9n1WSPFlz8ptn0waA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=couthit.com; spf=pass smtp.mailfrom=couthit.com; dkim=pass (2048-bit key) header.d=couthit.com header.i=@couthit.com header.b=X/scI3jO; arc=none smtp.client-ip=162.240.238.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=couthit.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=couthit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=couthit.com header.i=@couthit.com header.b="X/scI3jO" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=couthit.com ; s=default; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=gSgtEK5ojmqZPpAo5KUYmJ1HkRKHWt6Os+pubsNh5dc=; b=X/scI3jOnJyv2tnKv3tBl2NgqP fuBpUDUOOOykdmCdJ0ABNCUPoSGBJys/b50bomBlC7Ofmr9U7uvhEcHG77HwP9aGvCMashpgMSdch g2y6SDkIxuVZ5zhkqBsFpfZCQHGM7oKCBaCjNcgIiYuyMMtUDV4pcoAknZjifGOxn70t3EzYPv4As FcQMGAgkO5H85s4Mz/TWq6M1tKOBndK1uBwzoF9jr7zP1J916DdNCqgSMlcLDIBvV+4gg14Qzc/aX gjYFnC/b5Bl4H3FXNdAU0hDy8dz/l2hb2S/SB/n290QjyRw65hvtY4PR646tQbVKdAtcP0MK2Hz4C 9NsOOSOg==; Received: from [122.175.9.182] (port=2665 helo=cypher.couthit.local) by server.wki.vra.mybluehostin.me with esmtpa (Exim 4.96.2) (envelope-from ) id 1tbJxV-0005Y4-1M; Fri, 24 Jan 2025 19:12:53 +0530 From: Basharath Hussain Khaja To: danishanwar@ti.com, rogerq@kernel.org, andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, nm@ti.com, ssantosh@kernel.org, tony@atomide.com, richardcochran@gmail.com, parvathi@couthit.com, basharath@couthit.com, schnelle@linux.ibm.com, rdunlap@infradead.org, diogo.ivo@siemens.com, m-karicheri2@ti.com, horms@kernel.org, jacob.e.keller@intel.com, m-malladi@ti.com, javier.carrasco.cruz@gmail.com, afd@ti.com, s-anna@ti.com Cc: linux-arm-kernel@lists.infradead.org, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-omap@vger.kernel.org, pratheesh@ti.com, prajith@ti.com, vigneshr@ti.com, praneeth@ti.com, srk@ti.com, rogerq@ti.com, krishna@couthit.com, pmohan@couthit.com, mohan@couthit.com Subject: [RFC v2 PATCH 07/10] net: ti: prueth: Adds support for network filters for traffic control supported by PRU-ICSS Date: Fri, 24 Jan 2025 19:10:53 +0530 Message-Id: <20250124134056.1459060-8-basharath@couthit.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250124122353.1457174-1-basharath@couthit.com> References: <20250124122353.1457174-1-basharath@couthit.com> Precedence: bulk X-Mailing-List: linux-omap@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-AntiAbuse: This header was added to track abuse, please include it with any abuse report X-AntiAbuse: Primary Hostname - server.wki.vra.mybluehostin.me X-AntiAbuse: Original Domain - vger.kernel.org X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] X-AntiAbuse: Sender Address Domain - couthit.com X-Get-Message-Sender-Via: server.wki.vra.mybluehostin.me: authenticated_id: basharath@couthit.com X-Authenticated-Sender: server.wki.vra.mybluehostin.me: basharath@couthit.com X-Source: X-Source-Args: X-Source-Dir: From: Roger Quadros Driver updates to enable/disable network filters and traffic control features supported by the firmware running on PRU-ICSS. Control of the following features are now supported: 1. Promiscuous mode 2. Network Storm prevention 3. Multicast filtering and 4. VLAN filtering Firmware running on PRU-ICSS will go through all these filter checks prior to sending the rx packets to the host. Ethtool or dev ioctl can be used to enable/disable these features from the user space. Signed-off-by: Roger Quadros Signed-off-by: Andrew F. Davis Signed-off-by: Parvathi Pudi Signed-off-by: Basharath Hussain Khaja --- drivers/net/ethernet/ti/Makefile | 2 +- drivers/net/ethernet/ti/icssm/icssm_ethtool.c | 39 +++ drivers/net/ethernet/ti/icssm/icssm_prueth.c | 310 +++++++++++++++++- drivers/net/ethernet/ti/icssm/icssm_prueth.h | 49 ++- .../net/ethernet/ti/icssm/icssm_prueth_dos.c | 225 +++++++++++++ drivers/net/ethernet/ti/icssm/icssm_switch.h | 5 + .../ti/icssm/icssm_vlan_mcast_filter_mmap.h | 120 +++++++ 7 files changed, 747 insertions(+), 3 deletions(-) create mode 100644 drivers/net/ethernet/ti/icssm/icssm_prueth_dos.c create mode 100644 drivers/net/ethernet/ti/icssm/icssm_vlan_mcast_filter_mmap.h diff --git a/drivers/net/ethernet/ti/Makefile b/drivers/net/ethernet/ti/Makefile index f21dd11118ab..852640ce2b15 100644 --- a/drivers/net/ethernet/ti/Makefile +++ b/drivers/net/ethernet/ti/Makefile @@ -4,7 +4,7 @@ # obj-$(CONFIG_TI_PRUETH) += icssm-prueth.o -icssm-prueth-y := icssm/icssm_prueth.o icssm/icssm_ethtool.o +icssm-prueth-y := icssm/icssm_prueth.o icssm/icssm_ethtool.o icssm/icssm_prueth_dos.o obj-$(CONFIG_TI_CPSW) += cpsw-common.o obj-$(CONFIG_TI_DAVINCI_EMAC) += cpsw-common.o diff --git a/drivers/net/ethernet/ti/icssm/icssm_ethtool.c b/drivers/net/ethernet/ti/icssm/icssm_ethtool.c index ff9faaef9f9e..bf7d508aecaf 100644 --- a/drivers/net/ethernet/ti/icssm/icssm_ethtool.c +++ b/drivers/net/ethernet/ti/icssm/icssm_ethtool.c @@ -7,6 +7,7 @@ #include #include "icssm_prueth.h" +#include "icssm_vlan_mcast_filter_mmap.h" #include "../icssg/icss_iep.h" #define PRUETH_MODULE_VERSION "0.2" @@ -20,6 +21,11 @@ void icssm_emac_set_stats(struct prueth_emac *emac, dram = emac->prueth->mem[emac->dram].va; memcpy_toio(dram + STATISTICS_OFFSET, pstats, STAT_SIZE); + + writel(pstats->vlan_dropped, dram + + ICSS_EMAC_FW_VLAN_FILTER_DROP_CNT_OFFSET); + writel(pstats->multicast_dropped, dram + + ICSS_EMAC_FW_MULTICAST_FILTER_DROP_CNT_OFFSET); } /* get statistics maintained by the PRU firmware into @pstats */ @@ -30,6 +36,11 @@ void icssm_emac_get_stats(struct prueth_emac *emac, dram = emac->prueth->mem[emac->dram].va; memcpy_fromio(pstats, dram + STATISTICS_OFFSET, STAT_SIZE); + + pstats->vlan_dropped = + readl(dram + ICSS_EMAC_FW_VLAN_FILTER_DROP_CNT_OFFSET); + pstats->multicast_dropped = + readl(dram + ICSS_EMAC_FW_MULTICAST_FILTER_DROP_CNT_OFFSET); } /** @@ -181,13 +192,40 @@ static void icssm_emac_get_ethtool_stats(struct net_device *ndev, } } +static int icssm_emac_get_regs_len(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct prueth *prueth = emac->prueth; + + /* VLAN Table at the end of the memory map, after MultiCast + * filter region. So VLAN table base + + * size will give the entire size of reg dump in case of + * Dual-EMAC firmware. + */ + if (PRUETH_IS_EMAC(prueth)) { + return ICSS_EMAC_FW_VLAN_FLTR_TBL_BASE_ADDR + + ICSS_EMAC_FW_VLAN_FILTER_TABLE_SIZE_BYTES; + } + + return 0; +} + static void icssm_emac_get_regs(struct net_device *ndev, struct ethtool_regs *regs, void *p) { struct prueth_emac *emac = netdev_priv(ndev); struct prueth *prueth = emac->prueth; + void __iomem *ram; + u8 *reg = p; regs->version = PRUETH_REG_DUMP_GET_VER(prueth); + + /* Dump firmware's VLAN and MC tables */ + if (PRUETH_IS_EMAC(prueth)) { + ram = prueth->mem[emac->dram].va; + memcpy_fromio(reg, ram, icssm_emac_get_regs_len(ndev)); + return; + } } static int icssm_emac_get_ts_info(struct net_device *ndev, @@ -222,5 +260,6 @@ const struct ethtool_ops emac_ethtool_ops = { .get_strings = icssm_emac_get_strings, .get_ethtool_stats = icssm_emac_get_ethtool_stats, .get_regs = icssm_emac_get_regs, + .get_regs_len = icssm_emac_get_regs_len, }; EXPORT_SYMBOL_GPL(emac_ethtool_ops); diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth.c b/drivers/net/ethernet/ti/icssm/icssm_prueth.c index 7311366dac93..1b2571773d53 100644 --- a/drivers/net/ethernet/ti/icssm/icssm_prueth.c +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth.c @@ -29,6 +29,7 @@ #include #include "icssm_prueth.h" +#include "icssm_vlan_mcast_filter_mmap.h" #include "../icssg/icssg_mii_rt.h" #include "../icssg/icss_iep.h" @@ -37,6 +38,26 @@ #define TX_START_DELAY 0x40 #define TX_CLK_DELAY_100M 0x6 +static struct prueth_fw_offsets fw_offsets_v2_1; + +static void icssm_prueth_set_fw_offsets(struct prueth *prueth) +{ + /* Set VLAN/Multicast filter control and table offsets */ + if (PRUETH_IS_EMAC(prueth)) { + prueth->fw_offsets->vlan_ctrl_byte = + ICSS_EMAC_FW_VLAN_FILTER_CTRL_BITMAP_OFFSET; + prueth->fw_offsets->vlan_filter_tbl = + ICSS_EMAC_FW_VLAN_FLTR_TBL_BASE_ADDR; + + prueth->fw_offsets->mc_ctrl_byte = + ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_OFFSET; + prueth->fw_offsets->mc_filter_mask = + ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OFFSET; + prueth->fw_offsets->mc_filter_tbl = + ICSS_EMAC_FW_MULTICAST_FILTER_TABLE; + } +} + static inline void icssm_prueth_write_reg(struct prueth *prueth, enum prueth_mem region, unsigned int reg, u32 val) @@ -343,18 +364,25 @@ static void icssm_prueth_hostinit(struct prueth *prueth) */ static void icssm_prueth_init_ethernet_mode(struct prueth *prueth) { + icssm_prueth_set_fw_offsets(prueth); icssm_prueth_hostinit(prueth); } static void icssm_prueth_port_enable(struct prueth_emac *emac, bool enable) { struct prueth *prueth = emac->prueth; - void __iomem *port_ctrl; + void __iomem *port_ctrl, *vlan_ctrl; + u32 vlan_ctrl_offset; void __iomem *ram; + vlan_ctrl_offset = prueth->fw_offsets->vlan_ctrl_byte; + ram = prueth->mem[emac->dram].va; port_ctrl = ram + PORT_CONTROL_ADDR; writeb(!!enable, port_ctrl); + + vlan_ctrl = ram + vlan_ctrl_offset; + writeb(!!enable, vlan_ctrl); } static int icssm_prueth_emac_config(struct prueth_emac *emac) @@ -1403,6 +1431,174 @@ static struct net_device_stats return stats; } +/* enable/disable MC filter */ +static void icssm_emac_mc_filter_ctrl(struct prueth_emac *emac, bool enable) +{ + struct prueth *prueth = emac->prueth; + void __iomem *mc_filter_ctrl; + void __iomem *ram; + u32 mc_ctrl_byte; + u32 reg; + + ram = prueth->mem[emac->dram].va; + mc_ctrl_byte = prueth->fw_offsets->mc_ctrl_byte; + mc_filter_ctrl = ram + mc_ctrl_byte; + + if (enable) + reg = ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_ENABLED; + else + reg = ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_DISABLED; + + writeb(reg, mc_filter_ctrl); +} + +/* reset MC filter bins */ +static void icssm_emac_mc_filter_reset(struct prueth_emac *emac) +{ + struct prueth *prueth = emac->prueth; + void __iomem *mc_filter_tbl; + u32 mc_filter_tbl_base; + void __iomem *ram; + + ram = prueth->mem[emac->dram].va; + mc_filter_tbl_base = prueth->fw_offsets->mc_filter_tbl; + + mc_filter_tbl = ram + mc_filter_tbl_base; + memset_io(mc_filter_tbl, 0, ICSS_EMAC_FW_MULTICAST_TABLE_SIZE_BYTES); +} + +/* set MC filter hashmask */ +static void icssm_emac_mc_filter_hashmask + (struct prueth_emac *emac, + u8 mask[ICSS_EMAC_FW_MULTICAST_FILTER_MASK_SIZE_BYTES]) +{ + struct prueth *prueth = emac->prueth; + void __iomem *mc_filter_mask; + u32 mc_filter_mask_base; + void __iomem *ram; + + ram = prueth->mem[emac->dram].va; + mc_filter_mask_base = prueth->fw_offsets->mc_filter_mask; + + mc_filter_mask = ram + mc_filter_mask_base; + memcpy_toio(mc_filter_mask, mask, + ICSS_EMAC_FW_MULTICAST_FILTER_MASK_SIZE_BYTES); +} + +static void icssm_emac_mc_filter_bin_update(struct prueth_emac *emac, u8 hash, + u8 val) +{ + struct prueth *prueth = emac->prueth; + void __iomem *mc_filter_tbl; + u32 mc_filter_tbl_base; + void __iomem *ram; + + ram = prueth->mem[emac->dram].va; + mc_filter_tbl_base = prueth->fw_offsets->mc_filter_tbl; + + mc_filter_tbl = ram + mc_filter_tbl_base; + writeb(val, mc_filter_tbl + hash); +} + +void icssm_emac_mc_filter_bin_allow(struct prueth_emac *emac, u8 hash) +{ + icssm_emac_mc_filter_bin_update + (emac, hash, + ICSS_EMAC_FW_MULTICAST_FILTER_HOST_RCV_ALLOWED); +} + +void icssm_emac_mc_filter_bin_disallow(struct prueth_emac *emac, u8 hash) +{ + icssm_emac_mc_filter_bin_update + (emac, hash, + ICSS_EMAC_FW_MULTICAST_FILTER_HOST_RCV_NOT_ALLOWED); +} + +u8 icssm_emac_get_mc_hash(u8 *mac, u8 *mask) +{ + u8 hash; + int j; + + for (j = 0, hash = 0; j < ETH_ALEN; j++) + hash ^= (mac[j] & mask[j]); + + return hash; +} + +/** + * icssm_emac_ndo_set_rx_mode - EMAC set receive mode function + * @ndev: The EMAC network adapter + * + * Called when system wants to set the receive mode of the device. + * + */ +static void icssm_emac_ndo_set_rx_mode(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + bool promisc = ndev->flags & IFF_PROMISC; + struct netdev_hw_addr *ha; + struct prueth *prueth; + unsigned long flags; + void __iomem *sram; + u32 mask, reg; + u8 hash; + + prueth = emac->prueth; + sram = prueth->mem[PRUETH_MEM_SHARED_RAM].va; + reg = readl(sram + EMAC_PROMISCUOUS_MODE_OFFSET); + + /* for LRE, it is a shared table. So lock the access */ + spin_lock_irqsave(&emac->addr_lock, flags); + + /* Disable and reset multicast filter, allows allmulti */ + icssm_emac_mc_filter_ctrl(emac, false); + icssm_emac_mc_filter_reset(emac); + icssm_emac_mc_filter_hashmask(emac, emac->mc_filter_mask); + + if (PRUETH_IS_EMAC(prueth)) { + switch (emac->port_id) { + case PRUETH_PORT_MII0: + mask = EMAC_P1_PROMISCUOUS_BIT; + break; + case PRUETH_PORT_MII1: + mask = EMAC_P2_PROMISCUOUS_BIT; + break; + default: + netdev_err(ndev, "%s: invalid port\n", __func__); + goto unlock; + } + + if (promisc) { + /* Enable promiscuous mode */ + reg |= mask; + } else { + /* Disable promiscuous mode */ + reg &= ~mask; + } + + writel(reg, sram + EMAC_PROMISCUOUS_MODE_OFFSET); + + if (promisc) + goto unlock; + } + + if (ndev->flags & IFF_ALLMULTI && !PRUETH_IS_SWITCH(prueth)) + goto unlock; + + icssm_emac_mc_filter_ctrl(emac, true); /* all multicast blocked */ + + if (netdev_mc_empty(ndev)) + goto unlock; + + netdev_for_each_mc_addr(ha, ndev) { + hash = icssm_emac_get_mc_hash(ha->addr, emac->mc_filter_mask); + icssm_emac_mc_filter_bin_allow(emac, hash); + } + +unlock: + spin_unlock_irqrestore(&emac->addr_lock, flags); +} + static int icssm_emac_hwtstamp_config_set(struct net_device *ndev, struct ifreq *ifr) { @@ -1476,13 +1672,115 @@ static int icssm_emac_ndo_ioctl(struct net_device *ndev, struct ifreq *ifr, return phy_do_ioctl(ndev, ifr, cmd); } +int icssm_emac_add_del_vid(struct prueth_emac *emac, + bool add, __be16 proto, u16 vid) +{ + struct prueth *prueth = emac->prueth; + u32 vlan_filter_tbl; + unsigned long flags; + void __iomem *ram; + u8 bit_index, val; + u16 byte_index; + + vlan_filter_tbl = prueth->fw_offsets->vlan_filter_tbl; + ram = prueth->mem[emac->dram].va; + + if (proto != htons(ETH_P_8021Q)) + return -EINVAL; + + if (vid >= ICSS_EMAC_FW_VLAN_FILTER_VID_MAX) + return -EINVAL; + + /* By default, VLAN ID 0 (priority tagged packets) is routed to + * host, so nothing to be done if vid = 0 + */ + if (!vid) + return 0; + + /* for LRE, it is a shared table. So lock the access */ + spin_lock_irqsave(&emac->addr_lock, flags); + + /* VLAN filter table is 512 bytes (4096 bit) bitmap. + * Each bit controls enabling or disabling corresponding + * VID. Therefore byte index that controls a given VID is + * can calculated as vid / 8 and the bit within that byte + * that controls VID is given by vid % 8. Allow untagged + * frames to host by default. + */ + byte_index = vid / BITS_PER_BYTE; + bit_index = vid % BITS_PER_BYTE; + val = readb(ram + vlan_filter_tbl + byte_index); + if (add) + val |= BIT(bit_index); + else + val &= ~BIT(bit_index); + writeb(val, ram + vlan_filter_tbl + byte_index); + + spin_unlock_irqrestore(&emac->addr_lock, flags); + + netdev_dbg(emac->ndev, "%s VID bit at index %d and bit %d\n", + add ? "Setting" : "Clearing", byte_index, bit_index); + + return 0; +} + +static int icssm_emac_ndo_vlan_rx_add_vid(struct net_device *dev, + __be16 proto, u16 vid) +{ + struct prueth_emac *emac = netdev_priv(dev); + + return icssm_emac_add_del_vid(emac, true, proto, vid); +} + +static int icssm_emac_ndo_vlan_rx_kill_vid(struct net_device *dev, + __be16 proto, u16 vid) +{ + struct prueth_emac *emac = netdev_priv(dev); + + return icssm_emac_add_del_vid(emac, false, proto, vid); +} + +static int icssm_emac_get_port_parent_id(struct net_device *dev, + struct netdev_phys_item_id *ppid) +{ + struct prueth_emac *emac = netdev_priv(dev); + struct prueth *prueth = emac->prueth; + + ppid->id_len = sizeof(prueth->base_mac); + memcpy(&ppid->id, &prueth->base_mac, ppid->id_len); + + return 0; +} + +static int icssm_emac_ndo_get_phys_port_name(struct net_device *ndev, + char *name, size_t len) +{ + struct prueth_emac *emac = netdev_priv(ndev); + int err; + + err = snprintf(name, len, "p%d", emac->port_id); + + if (err >= len) + return -EINVAL; + + return 0; +} + static const struct net_device_ops emac_netdev_ops = { .ndo_open = icssm_emac_ndo_open, .ndo_stop = icssm_emac_ndo_stop, .ndo_start_xmit = icssm_emac_ndo_start_xmit, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, .ndo_tx_timeout = icssm_emac_ndo_tx_timeout, .ndo_get_stats = icssm_emac_ndo_get_stats, + .ndo_set_rx_mode = icssm_emac_ndo_set_rx_mode, .ndo_eth_ioctl = icssm_emac_ndo_ioctl, + .ndo_vlan_rx_add_vid = icssm_emac_ndo_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = icssm_emac_ndo_vlan_rx_kill_vid, + .ndo_setup_tc = icssm_emac_ndo_setup_tc, + .ndo_get_port_parent_id = icssm_emac_get_port_parent_id, + .ndo_get_phys_port_name = icssm_emac_ndo_get_phys_port_name, }; /* get emac_port corresponding to eth_node name */ @@ -1548,6 +1846,7 @@ static int icssm_prueth_netdev_init(struct prueth *prueth, emac->prueth = prueth; emac->ndev = ndev; emac->port_id = port; + memset(&emac->mc_filter_mask[0], 0xff, ETH_ALEN); /* default mask */ /* by default eth_type is EMAC */ switch (port) { @@ -1594,7 +1893,9 @@ static int icssm_prueth_netdev_init(struct prueth *prueth, dev_err(prueth->dev, "could not get ptp tx irq. Skipping PTP support\n"); } + spin_lock_init(&emac->lock); spin_lock_init(&emac->ptp_skb_lock); + spin_lock_init(&emac->addr_lock); /* get mac address from DT and set private and netdev addr */ ret = of_get_ethdev_address(eth_node, ndev); @@ -1623,6 +1924,10 @@ static int icssm_prueth_netdev_init(struct prueth *prueth, phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_Pause_BIT); phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_Asym_Pause_BIT); + ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_TC; + + ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER; + ndev->netdev_ops = &emac_netdev_ops; ndev->ethtool_ops = &emac_ethtool_ops; @@ -1674,6 +1979,7 @@ static int icssm_prueth_probe(struct platform_device *pdev) platform_set_drvdata(pdev, prueth); prueth->dev = dev; prueth->fw_data = device_get_match_data(dev); + prueth->fw_offsets = &fw_offsets_v2_1; eth_ports_node = of_get_child_by_name(np, "ethernet-ports"); if (!eth_ports_node) @@ -1869,6 +2175,8 @@ static int icssm_prueth_probe(struct platform_device *pdev) prueth->emac[PRUETH_MAC1]->ndev; } + eth_random_addr(prueth->base_mac); + dev_info(dev, "TI PRU ethernet driver initialized: %s EMAC mode\n", (!eth0_node || !eth1_node) ? "single" : "dual"); diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth.h b/drivers/net/ethernet/ti/icssm/icssm_prueth.h index bd7ad8318544..4eac7f9f8496 100644 --- a/drivers/net/ethernet/ti/icssm/icssm_prueth.h +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth.h @@ -28,6 +28,9 @@ #define EMAC_MAX_FRM_SUPPORT (ETH_HLEN + VLAN_HLEN + ETH_DATA_LEN + \ ICSSM_LRE_TAG_SIZE) +/* default timer for NSP and HSR/PRP */ +#define PRUETH_NSP_TIMER_MS (100) /* Refresh NSP counters every 100ms */ + #define PRUETH_REG_DUMP_VER 1 /* Encoding: 32-16: Reserved, 16-8: Reg dump version, 8-0: Ethertype */ @@ -293,6 +296,29 @@ enum prueth_mem { PRUETH_MEM_MAX, }; +/* Firmware offsets/size information */ +struct prueth_fw_offsets { + u32 index_array_offset; + u32 bin_array_offset; + u32 nt_array_offset; + u32 index_array_loc; + u32 bin_array_loc; + u32 nt_array_loc; + u32 index_array_max_entries; + u32 bin_array_max_entries; + u32 nt_array_max_entries; + u32 vlan_ctrl_byte; + u32 vlan_filter_tbl; + u32 mc_ctrl_byte; + u32 mc_filter_mask; + u32 mc_filter_tbl; + /* IEP wrap is used in the rx packet ordering logic and + * is different for ICSSM v1.0 vs 2.1 + */ + u32 iep_wrap; + u16 hash_mask; +}; + /** * struct prueth_private_data - PRU Ethernet private data * @fw_pru: firmware names to be used for PRUSS ethernet usecases @@ -305,6 +331,11 @@ struct prueth_private_data { bool support_switch; }; +struct nsp_counter { + unsigned long cookie; + u16 credit; +}; + /* data for each emac port */ struct prueth_emac { struct prueth *prueth; @@ -330,8 +361,16 @@ struct prueth_emac { const char *phy_id; u32 msg_enable; u8 mac_addr[6]; + unsigned char mc_filter_mask[ETH_ALEN]; /* for multicast filtering */ phy_interface_t phy_if; + spinlock_t lock; /* serialize access */ + spinlock_t addr_lock; /* serialize access to VLAN/MC filter table */ + + struct nsp_counter nsp_bc; + struct nsp_counter nsp_mc; + struct nsp_counter nsp_uc; + bool nsp_enabled; struct sk_buff *ptp_skb[PRUETH_PTP_TS_EVENTS]; spinlock_t ptp_skb_lock; /* serialize access */ @@ -358,19 +397,27 @@ struct prueth { unsigned int eth_type; u8 emac_configured; + u8 base_mac[ETH_ALEN]; }; extern const struct ethtool_ops emac_ethtool_ops; +int icssm_emac_ndo_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data); void icssm_parse_packet_info(struct prueth *prueth, u32 buffer_descriptor, struct prueth_packet_info *pkt_info); int icssm_emac_rx_packet(struct prueth_emac *emac, u16 *bd_rd_ptr, struct prueth_packet_info *pkt_info, const struct prueth_queue_info *rxqueue); - +int icssm_emac_add_del_vid(struct prueth_emac *emac, + bool add, __be16 proto, u16 vid); irqreturn_t icssm_prueth_ptp_tx_irq_handle(int irq, void *dev); irqreturn_t icssm_prueth_ptp_tx_irq_work(int irq, void *dev); +void icssm_emac_mc_filter_bin_allow(struct prueth_emac *emac, u8 hash); +void icssm_emac_mc_filter_bin_disallow(struct prueth_emac *emac, u8 hash); +u8 icssm_emac_get_mc_hash(u8 *mac, u8 *mask); + void icssm_emac_set_stats(struct prueth_emac *emac, struct port_statistics *pstats); void icssm_emac_get_stats(struct prueth_emac *emac, diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth_dos.c b/drivers/net/ethernet/ti/icssm/icssm_prueth_dos.c new file mode 100644 index 000000000000..8382bd8cab7c --- /dev/null +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth_dos.c @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com + */ + +#include +#include +#include +#include +#include + +#include "../icssg/icssg_mii_rt.h" +#include "icssm_vlan_mcast_filter_mmap.h" +#include "icssm_prueth.h" + +static void icssm_emac_nsp_enable(void __iomem *counter, u16 credit) +{ + writel((credit << PRUETH_NSP_CREDIT_SHIFT) | PRUETH_NSP_ENABLE, + counter); +} + +/** + * icssm_prueth_enable_nsp - enable nsp + * + * @emac: EMAC data structure + * + */ +static void icssm_prueth_enable_nsp(struct prueth_emac *emac) +{ + struct prueth *prueth = emac->prueth; + void __iomem *dram; + + dram = prueth->mem[emac->dram].va; + + if (emac->nsp_bc.cookie) + icssm_emac_nsp_enable(dram + STORM_PREVENTION_OFFSET_BC, + emac->nsp_bc.credit); + if (emac->nsp_mc.cookie) + icssm_emac_nsp_enable(dram + STORM_PREVENTION_OFFSET_MC, + emac->nsp_mc.credit); + if (emac->nsp_uc.cookie) + icssm_emac_nsp_enable(dram + STORM_PREVENTION_OFFSET_UC, + emac->nsp_uc.credit); +} + +static int icssm_emac_flower_parse_policer(struct prueth_emac *emac, + struct netlink_ext_ack *extack, + struct flow_cls_offload *cls, + u64 rate_bytes_per_sec) +{ + struct flow_rule *rule = flow_cls_offload_flow_rule(cls); + struct flow_dissector *dissector = rule->match.dissector; + u8 null_mac[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + u8 mc_mac[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00}; + struct flow_match_eth_addrs match; + struct nsp_counter *nsp = NULL; + char *str; + u32 pps; + + if (dissector->used_keys & + ~(BIT(FLOW_DISSECTOR_KEY_BASIC) | + BIT(FLOW_DISSECTOR_KEY_CONTROL) | + BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS))) { + NL_SET_ERR_MSG_MOD(extack, + "Unsupported keys used"); + return -EOPNOTSUPP; + } + + if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { + NL_SET_ERR_MSG_MOD(extack, "Not matching on eth address"); + return -EOPNOTSUPP; + } + + flow_rule_match_eth_addrs(rule, &match); + + if (!ether_addr_equal_masked(match.key->src, null_mac, + match.mask->src)) { + NL_SET_ERR_MSG_MOD(extack, + "Matching on source MAC not supported"); + return -EOPNOTSUPP; + } + + if (ether_addr_equal(match.key->dst, bc_mac)) { + if (!emac->nsp_bc.cookie || + emac->nsp_bc.cookie == cls->cookie) + nsp = &emac->nsp_bc; + else + NL_SET_ERR_MSG_MOD(extack, "BC Filter already set"); + str = "Broad"; + } else if (ether_addr_equal_masked(match.key->dst, mc_mac, mc_mac)) { + if (!emac->nsp_mc.cookie || + emac->nsp_mc.cookie == cls->cookie) + nsp = &emac->nsp_mc; + else + NL_SET_ERR_MSG_MOD(extack, "MC Filter already set"); + str = "Multi"; + } else { + if (!emac->nsp_uc.cookie || + emac->nsp_uc.cookie == cls->cookie) + nsp = &emac->nsp_uc; + else + NL_SET_ERR_MSG_MOD(extack, "UC Filter already set"); + str = "Uni"; + } + + if (!nsp) + return -EOPNOTSUPP; + + /* Calculate number of packets per second for given bps + * assuming min ethernet packet size + */ + pps = div_u64(rate_bytes_per_sec, ETH_ZLEN); + /* Convert that to packets per 100ms */ + pps /= MSEC_PER_SEC / PRUETH_NSP_TIMER_MS; + + nsp->cookie = cls->cookie; + nsp->credit = pps; + emac->nsp_enabled = emac->nsp_bc.cookie | emac->nsp_mc.cookie | + emac->nsp_uc.cookie; + + icssm_prueth_enable_nsp(emac); + + netdev_dbg(emac->ndev, + "%scast filter set to %d packets per %dms\n", str, + nsp->credit, PRUETH_NSP_TIMER_MS); + + return 0; +} + +static int icssm_emac_configure_clsflower(struct prueth_emac *emac, + struct flow_cls_offload *cls) +{ + struct flow_rule *rule = flow_cls_offload_flow_rule(cls); + struct netlink_ext_ack *extack = cls->common.extack; + const struct flow_action_entry *act; + int i; + + flow_action_for_each(i, act, &rule->action) { + switch (act->id) { + case FLOW_ACTION_POLICE: + return icssm_emac_flower_parse_policer + (emac, extack, cls, + act->police.rate_bytes_ps); + default: + NL_SET_ERR_MSG_MOD(extack, + "Action not supported"); + return -EOPNOTSUPP; + } + } + return -EOPNOTSUPP; +} + +static int icssm_emac_delete_clsflower(struct prueth_emac *emac, + struct flow_cls_offload *cls) +{ + struct prueth *prueth = emac->prueth; + void __iomem *dram; + + dram = prueth->mem[emac->dram].va; + + if (cls->cookie == emac->nsp_bc.cookie) { + emac->nsp_bc.cookie = 0; + emac->nsp_bc.credit = 0; + writel(0, dram + STORM_PREVENTION_OFFSET_BC); + } else if (cls->cookie == emac->nsp_mc.cookie) { + emac->nsp_mc.cookie = 0; + emac->nsp_mc.credit = 0; + writel(0, dram + STORM_PREVENTION_OFFSET_MC); + } else if (cls->cookie == emac->nsp_uc.cookie) { + emac->nsp_uc.cookie = 0; + emac->nsp_uc.credit = 0; + writel(0, dram + STORM_PREVENTION_OFFSET_UC); + } + + emac->nsp_enabled = emac->nsp_bc.cookie | emac->nsp_mc.cookie | + emac->nsp_uc.cookie; + + return 0; +} + +static int icssm_emac_setup_tc_cls_flower(struct prueth_emac *emac, + struct flow_cls_offload *cls_flower) +{ + switch (cls_flower->command) { + case FLOW_CLS_REPLACE: + return icssm_emac_configure_clsflower(emac, cls_flower); + case FLOW_CLS_DESTROY: + return icssm_emac_delete_clsflower(emac, cls_flower); + default: + return -EOPNOTSUPP; + } +} + +static int icssm_emac_setup_tc_block_cb(enum tc_setup_type type, + void *type_data, void *cb_priv) +{ + struct prueth_emac *emac = cb_priv; + + if (!tc_cls_can_offload_and_chain0(emac->ndev, type_data)) + return -EOPNOTSUPP; + + switch (type) { + case TC_SETUP_CLSFLOWER: + return icssm_emac_setup_tc_cls_flower(emac, type_data); + default: + return -EOPNOTSUPP; + } +} + +static LIST_HEAD(emac_block_cb_list); + +int icssm_emac_ndo_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data) +{ + struct prueth_emac *emac = netdev_priv(dev); + + if (type == TC_SETUP_BLOCK) { + return flow_block_cb_setup_simple(type_data, + &emac_block_cb_list, + icssm_emac_setup_tc_block_cb, + emac, emac, true); + } + + return -EOPNOTSUPP; +} diff --git a/drivers/net/ethernet/ti/icssm/icssm_switch.h b/drivers/net/ethernet/ti/icssm/icssm_switch.h index b13e0706ccec..0053191380b7 100644 --- a/drivers/net/ethernet/ti/icssm/icssm_switch.h +++ b/drivers/net/ethernet/ti/icssm/icssm_switch.h @@ -146,6 +146,11 @@ /* 4 bytes ? */ #define STP_INVALID_STATE_OFFSET (STATISTICS_OFFSET + STAT_SIZE + 33) +/* Shared RAM Offsets for Switch */ +/* NSP (Network Storm Prevention) timer re-uses NT timer */ +#define PRUETH_NSP_CREDIT_SHIFT 8 +#define PRUETH_NSP_ENABLE BIT(0) + /* DRAM Offsets for EMAC * Present on Both DRAM0 and DRAM1 */ diff --git a/drivers/net/ethernet/ti/icssm/icssm_vlan_mcast_filter_mmap.h b/drivers/net/ethernet/ti/icssm/icssm_vlan_mcast_filter_mmap.h new file mode 100644 index 000000000000..32b5c228c5fb --- /dev/null +++ b/drivers/net/ethernet/ti/icssm/icssm_vlan_mcast_filter_mmap.h @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* Copyright (C) 2015-2021 Texas Instruments Incorporated - https://www.ti.com + * + * This file contains VLAN/Multicast filtering feature memory map + * + */ + +#ifndef ICSS_VLAN_MULTICAST_FILTER_MM_H +#define ICSS_VLAN_MULTICAST_FILTER_MM_H + +/* VLAN/Multicast filter defines & offsets, + * present on both PRU0 and PRU1 DRAM + */ + +/* Feature enable/disable values for multicast filtering */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_DISABLED 0x00 +#define ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_ENABLED 0x01 + +/* Feature enable/disable values for VLAN filtering */ +#define ICSS_EMAC_FW_VLAN_FILTER_CTRL_DISABLED 0x00 +#define ICSS_EMAC_FW_VLAN_FILTER_CTRL_ENABLED 0x01 + +/* Add/remove multicast mac id for filtering bin */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_HOST_RCV_ALLOWED 0x01 +#define ICSS_EMAC_FW_MULTICAST_FILTER_HOST_RCV_NOT_ALLOWED 0x00 + +/* Default HASH value for the multicast filtering Mask */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_INIT_VAL 0xFF + +/* Size requirements for Multicast filtering feature */ +#define ICSS_EMAC_FW_MULTICAST_TABLE_SIZE_BYTES 256 +#define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_SIZE_BYTES 6 +#define ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_SIZE_BYTES 1 +#define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OVERRIDE_STATUS_SIZE_BYTES 1 +#define ICSS_EMAC_FW_MULTICAST_FILTER_DROP_CNT_SIZE_BYTES 4 + +/* Size requirements for VLAN filtering feature : 4096 bits = 512 bytes */ +#define ICSS_EMAC_FW_VLAN_FILTER_TABLE_SIZE_BYTES 512 +#define ICSS_EMAC_FW_VLAN_FILTER_CTRL_SIZE_BYTES 1 +#define ICSS_EMAC_FW_VLAN_FILTER_DROP_CNT_SIZE_BYTES 4 + +/* Mask override set status */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OVERRIDE_SET 1 +/* Mask override not set status */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OVERRIDE_NOT_SET 0 +/* 6 bytes HASH Mask for the MAC */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OFFSET 0xF4 +/* 0 -> multicast filtering disabled | 1 -> multicast filtering enabled */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_OFFSET \ + (ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OFFSET + \ + ICSS_EMAC_FW_MULTICAST_FILTER_MASK_SIZE_BYTES) +/* Status indicating if the HASH override is done or not: 0: no, 1: yes */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_OVERRIDE_STATUS \ + (ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_OFFSET + \ + ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_SIZE_BYTES) +/* Multicast drop statistics */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_DROP_CNT_OFFSET \ + (ICSS_EMAC_FW_MULTICAST_FILTER_OVERRIDE_STATUS +\ + ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OVERRIDE_STATUS_SIZE_BYTES) +/* Multicast table */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_TABLE \ + (ICSS_EMAC_FW_MULTICAST_FILTER_DROP_CNT_OFFSET +\ + ICSS_EMAC_FW_MULTICAST_FILTER_DROP_CNT_SIZE_BYTES) + +/* Multicast filter defines & offsets for LRE + */ +#define ICSS_LRE_FW_MULTICAST_TABLE_SEARCH_OP_CONTROL_BIT 0xE0 +/* one byte field : + * 0 -> multicast filtering disabled + * 1 -> multicast filtering enabled + */ +#define ICSS_LRE_FW_MULTICAST_FILTER_MASK 0xE4 +#define ICSS_LRE_FW_MULTICAST_FILTER_TABLE 0x100 + +/* VLAN table Offsets */ +#define ICSS_EMAC_FW_VLAN_FLTR_TBL_BASE_ADDR 0x200 +#define ICSS_EMAC_FW_VLAN_FILTER_CTRL_BITMAP_OFFSET 0xEF +#define ICSS_EMAC_FW_VLAN_FILTER_DROP_CNT_OFFSET \ + (ICSS_EMAC_FW_VLAN_FILTER_CTRL_BITMAP_OFFSET + \ + ICSS_EMAC_FW_VLAN_FILTER_CTRL_SIZE_BYTES) + +/* VLAN filter Control Bit maps */ +/* one bit field, bit 0: | 0 : VLAN filter disabled (default), + * 1: VLAN filter enabled + */ +#define ICSS_EMAC_FW_VLAN_FILTER_CTRL_ENABLE_BIT 0 +/* one bit field, bit 1: | 0 : untagged host rcv allowed (default), + * 1: untagged host rcv not allowed + */ +#define ICSS_EMAC_FW_VLAN_FILTER_UNTAG_HOST_RCV_ALLOW_CTRL_BIT 1 +/* one bit field, bit 1: | 0 : priotag host rcv allowed (default), + * 1: priotag host rcv not allowed + */ +#define ICSS_EMAC_FW_VLAN_FILTER_PRIOTAG_HOST_RCV_ALLOW_CTRL_BIT 2 +/* one bit field, bit 1: | 0 : skip sv vlan flow + * :1 : take sv vlan flow (not applicable for dual emac ) + */ +#define ICSS_EMAC_FW_VLAN_FILTER_SV_VLAN_FLOW_HOST_RCV_ALLOW_CTRL_BIT 3 + +/* VLAN IDs */ +#define ICSS_EMAC_FW_VLAN_FILTER_PRIOTAG_VID 0 +#define ICSS_EMAC_FW_VLAN_FILTER_VID_MIN 0x0000 +#define ICSS_EMAC_FW_VLAN_FILTER_VID_MAX 0x0FFF + +/* VLAN Filtering Commands */ +#define ICSS_EMAC_FW_VLAN_FILTER_ADD_VLAN_VID_CMD 0x00 +#define ICSS_EMAC_FW_VLAN_FILTER_REMOVE_VLAN_VID_CMD 0x01 + +/* Switch defines for VLAN/MC filtering */ +/* SRAM + * VLAN filter defines & offsets + */ +#define ICSS_LRE_FW_VLAN_FLTR_CTRL_BYTE 0x1FE +/* one bit field | 0 : VLAN filter disabled + * | 1 : VLAN filter enabled + */ +#define ICSS_LRE_FW_VLAN_FLTR_TBL_BASE_ADDR 0x200 + +#endif /* ICSS_MULTICAST_FILTER_MM_H */ From patchwork Fri Jan 24 14:45:54 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Basharath Hussain Khaja X-Patchwork-Id: 859719 Received: from server.wki.vra.mybluehostin.me (server.wki.vra.mybluehostin.me [162.240.238.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9FFB513C81B; Fri, 24 Jan 2025 14:47:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=162.240.238.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1737730030; cv=none; b=WmQUbLzd73z2wslJihT7Mfwap9N990MO9jVmzpDmog4sTG8nu7ZpBhz3EAaHnQW0EnekFwqMo62VOM1rPB70TRkFcsOJC4JBQCnIxVo8xLeo5PXoDh1KgqTvYgmLsnye2vqE7NKraCJv3lOXPQnlsNkSL7qWxtAd0/BgAO07V18= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1737730030; c=relaxed/simple; bh=pezlbPzEe8te78ZrZYz6wTryMqXUE4x3VGHuL4NRUYQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=nEcvmwSwjKclVcW1VcgP6o7LlFczDdlmzDNsxpxGMcmLO7aEfWZPi5mTeqTGdopaoT+BMZ41zErHzmWfQJMKJS28bvRbulQwvPRGCj1m0AWAMTxuR1sBaVML+aXlFIYPMeRPH5McicjX3Dk9uuugASyXvNajwe2glumcx1wYL2s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=couthit.com; spf=pass smtp.mailfrom=couthit.com; dkim=pass (2048-bit key) header.d=couthit.com header.i=@couthit.com header.b=Ee2RSY1H; arc=none smtp.client-ip=162.240.238.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=couthit.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=couthit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=couthit.com header.i=@couthit.com header.b="Ee2RSY1H" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=couthit.com ; s=default; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=oAF/UwyFDty0jZm744MKJhq2579iZwIfwjisq10Mv3o=; b=Ee2RSY1HyD/9c2bTH3b7/K8xi4 jlytSvP5OO0dwOk/t8qWGwctEPsSLBOtdvFe1e0O+70ysBErDRAIxZh36QubF9+OtbZli9VOJG2Eo u6N2N/AX2CJi2SO6y1E3TCPMnzGF58VwL7MB1dWYm2L5hHPlTex5Wnk75R7YKjArdKoyr2ab/vb7b ut/4gp/hAxhM6U48SHWYwFHKh/DlbhhqcDHbwvmwseFVUmr+AfJzs3t2l0WOd/BeHn5GPZLrISG1+ +WUjTXp9Gi4ROG8FTf+RBq0IhAnfKOOOPV4+KOExRSwbekQM2dLztK855jxM1IlmLHN9Y0rrCJ/54 OcZICY/A==; Received: from [122.175.9.182] (port=38494 helo=cypher.couthit.local) by server.wki.vra.mybluehostin.me with esmtpa (Exim 4.96.2) (envelope-from ) id 1tbKxa-0006Wf-2g; Fri, 24 Jan 2025 20:17:03 +0530 From: Basharath Hussain Khaja To: danishanwar@ti.com, rogerq@kernel.org, andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, nm@ti.com, ssantosh@kernel.org, tony@atomide.com, richardcochran@gmail.com, parvathi@couthit.com, basharath@couthit.com, schnelle@linux.ibm.com, rdunlap@infradead.org, diogo.ivo@siemens.com, m-karicheri2@ti.com, horms@kernel.org, jacob.e.keller@intel.com, m-malladi@ti.com, javier.carrasco.cruz@gmail.com, afd@ti.com, s-anna@ti.com Cc: linux-arm-kernel@lists.infradead.org, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-omap@vger.kernel.org, pratheesh@ti.com, prajith@ti.com, vigneshr@ti.com, praneeth@ti.com, srk@ti.com, rogerq@ti.com, krishna@couthit.com, pmohan@couthit.com, mohan@couthit.com Subject: [RFC v2 PATCH 09/10] net: ti: prueth: Adds power management support for PRU-ICSS Date: Fri, 24 Jan 2025 20:15:54 +0530 Message-Id: <20250124144555.1462044-10-basharath@couthit.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250124122353.1457174-1-basharath@couthit.com> References: <20250124122353.1457174-1-basharath@couthit.com> Precedence: bulk X-Mailing-List: linux-omap@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-AntiAbuse: This header was added to track abuse, please include it with any abuse report X-AntiAbuse: Primary Hostname - server.wki.vra.mybluehostin.me X-AntiAbuse: Original Domain - vger.kernel.org X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] X-AntiAbuse: Sender Address Domain - couthit.com X-Get-Message-Sender-Via: server.wki.vra.mybluehostin.me: authenticated_id: basharath@couthit.com X-Authenticated-Sender: server.wki.vra.mybluehostin.me: basharath@couthit.com X-Source: X-Source-Args: X-Source-Dir: From: Roger Quadros Changes for supporting the sleep/resume feature for PRU-ICSS. PRU-ICSS will be kept in IDLE mode for optimal power consumption by Linux power management subsystem and will be resumed when it is required. Signed-off-by: Roger Quadros Signed-off-by: Andrew F. Davis Signed-off-by: Parvathi Pudi Signed-off-by: Basharath Hussain Khaja --- drivers/net/ethernet/ti/icssm/icssm_prueth.c | 62 ++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth.c b/drivers/net/ethernet/ti/icssm/icssm_prueth.c index fdc97ed9c1fb..a3c8c746ebf8 100644 --- a/drivers/net/ethernet/ti/icssm/icssm_prueth.c +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth.c @@ -2300,6 +2300,67 @@ static void icssm_prueth_remove(struct platform_device *pdev) pru_rproc_put(prueth->pru0); } +#ifdef CONFIG_PM_SLEEP +static int icssm_prueth_suspend(struct device *dev) +{ + struct prueth *prueth = dev_get_drvdata(dev); + struct net_device *ndev; + int i, ret; + + for (i = 0; i < PRUETH_NUM_MACS; i++) { + ndev = prueth->registered_netdevs[i]; + + if (!ndev) + continue; + + if (netif_running(ndev)) { + netif_device_detach(ndev); + ret = icssm_emac_ndo_stop(ndev); + if (ret < 0) { + netdev_err(ndev, "failed to stop: %d", ret); + return ret; + } + } + } + + pruss_cfg_ocp_master_ports(prueth->pruss, 0); + + return 0; +} + +static int icssm_prueth_resume(struct device *dev) +{ + struct prueth *prueth = dev_get_drvdata(dev); + struct net_device *ndev; + int i, ret; + + pruss_cfg_ocp_master_ports(prueth->pruss, 1); + + for (i = 0; i < PRUETH_NUM_MACS; i++) { + ndev = prueth->registered_netdevs[i]; + + if (!ndev) + continue; + + if (netif_running(ndev)) { + ret = icssm_emac_ndo_open(ndev); + if (ret < 0) { + netdev_err(ndev, "failed to start: %d", ret); + return ret; + } + netif_device_attach(ndev); + } + } + + return 0; +} + +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops prueth_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(icssm_prueth_suspend, icssm_prueth_resume) +}; + /* AM57xx SoC-specific firmware data */ static struct prueth_private_data am57xx_prueth_pdata = { .fw_pru[PRUSS_PRU0] = { @@ -2324,6 +2385,7 @@ static struct platform_driver prueth_driver = { .driver = { .name = "prueth", .of_match_table = prueth_dt_match, + .pm = &prueth_dev_pm_ops, }, }; module_platform_driver(prueth_driver);