From cbcaf81cd148b77ee0570a482b536f269a9f6657 Mon Sep 17 00:00:00 2001 From: Suruchi Agarwal Date: Thu, 21 Mar 2024 16:14:46 -0700 Subject: [PATCH 39/50] net: ethernet: qualcomm: Add netdevice support for QCOM IPQ9574 chipset. Add EDMA ports and netdevice operations for QCOM IPQ9574 chipset. Change-Id: I08b2eff52b4ef0d6d428c1c416f5580ef010973f Co-developed-by: Pavithra R Signed-off-by: Pavithra R Signed-off-by: Suruchi Agarwal --- drivers/net/ethernet/qualcomm/ppe/Makefile | 2 +- drivers/net/ethernet/qualcomm/ppe/edma.h | 3 + drivers/net/ethernet/qualcomm/ppe/edma_port.c | 270 ++++++++++++++++++ drivers/net/ethernet/qualcomm/ppe/edma_port.h | 31 ++ drivers/net/ethernet/qualcomm/ppe/ppe_port.c | 19 ++ 5 files changed, 324 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_port.c create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_port.h diff --git a/drivers/net/ethernet/qualcomm/ppe/Makefile b/drivers/net/ethernet/qualcomm/ppe/Makefile index 7fea135ceb36..e26677644aa9 100644 --- a/drivers/net/ethernet/qualcomm/ppe/Makefile +++ b/drivers/net/ethernet/qualcomm/ppe/Makefile @@ -7,4 +7,4 @@ obj-$(CONFIG_QCOM_PPE) += qcom-ppe.o qcom-ppe-objs := ppe.o ppe_config.o ppe_api.o ppe_debugfs.o ppe_port.o #EDMA -qcom-ppe-objs += edma.o \ No newline at end of file +qcom-ppe-objs += edma.o edma_port.o \ No newline at end of file diff --git a/drivers/net/ethernet/qualcomm/ppe/edma.h b/drivers/net/ethernet/qualcomm/ppe/edma.h index 6bad51c976dd..5261002f883d 100644 --- a/drivers/net/ethernet/qualcomm/ppe/edma.h +++ b/drivers/net/ethernet/qualcomm/ppe/edma.h @@ -26,6 +26,9 @@ /* Number of PPE queue priorities supported per ARM core. */ #define EDMA_PRI_MAX_PER_CORE 8 +/* Interface ID start. */ +#define EDMA_START_IFNUM 1 + /** * struct edma_ring_info - EDMA ring data structure. * @max_rings: Maximum number of rings diff --git a/drivers/net/ethernet/qualcomm/ppe/edma_port.c b/drivers/net/ethernet/qualcomm/ppe/edma_port.c new file mode 100644 index 000000000000..6292b83d746d --- /dev/null +++ b/drivers/net/ethernet/qualcomm/ppe/edma_port.c @@ -0,0 +1,270 @@ +// SPDX-License-Identifier: GPL-2.0-only + /* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +/* EDMA port initialization, configuration and netdevice ops handling */ + +#include +#include +#include +#include +#include +#include + +#include "edma.h" +#include "edma_port.h" +#include "ppe_regs.h" + +/* Number of netdev queues. */ +#define EDMA_NETDEV_QUEUE_NUM 4 + +static u16 __maybe_unused edma_port_select_queue(__maybe_unused struct net_device *netdev, + __maybe_unused struct sk_buff *skb, + __maybe_unused struct net_device *sb_dev) +{ + int cpu = get_cpu(); + + put_cpu(); + + return cpu; +} + +static int edma_port_open(struct net_device *netdev) +{ + struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev); + struct ppe_port *ppe_port; + + if (!port_priv) + return -EINVAL; + + /* Inform the Linux Networking stack about the hardware capability of + * checksum offloading and other features. Each port is + * responsible to maintain the feature set it supports. + */ + netdev->features |= EDMA_NETDEV_FEATURES; + netdev->hw_features |= EDMA_NETDEV_FEATURES; + netdev->vlan_features |= EDMA_NETDEV_FEATURES; + netdev->wanted_features |= EDMA_NETDEV_FEATURES; + + ppe_port = port_priv->ppe_port; + + if (ppe_port->phylink) + phylink_start(ppe_port->phylink); + + netif_start_queue(netdev); + + return 0; +} + +static int edma_port_close(struct net_device *netdev) +{ + struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev); + struct ppe_port *ppe_port; + + if (!port_priv) + return -EINVAL; + + netif_stop_queue(netdev); + + ppe_port = port_priv->ppe_port; + + /* Phylink close. */ + if (ppe_port->phylink) + phylink_stop(ppe_port->phylink); + + return 0; +} + +static int edma_port_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ + struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev); + struct ppe_port *ppe_port; + int ret = -EINVAL; + + if (!port_priv) + return -EINVAL; + + ppe_port = port_priv->ppe_port; + if (ppe_port->phylink) + return phylink_mii_ioctl(ppe_port->phylink, ifr, cmd); + + return ret; +} + +static int edma_port_change_mtu(struct net_device *netdev, int mtu) +{ + struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev); + + if (!port_priv) + return -EINVAL; + + netdev->mtu = mtu; + + return ppe_port_set_maxframe(port_priv->ppe_port, mtu); +} + +static netdev_features_t edma_port_feature_check(__maybe_unused struct sk_buff *skb, + __maybe_unused struct net_device *netdev, + netdev_features_t features) +{ + return features; +} + +static void edma_port_get_stats64(struct net_device *netdev, + struct rtnl_link_stats64 *stats) +{ + struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev); + + if (!port_priv) + return; + + ppe_port_get_stats64(port_priv->ppe_port, stats); +} + +static int edma_port_set_mac_address(struct net_device *netdev, void *macaddr) +{ + struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev); + struct sockaddr *addr = (struct sockaddr *)macaddr; + int ret; + + if (!port_priv) + return -EINVAL; + + netdev_dbg(netdev, "AddrFamily: %d, %0x:%0x:%0x:%0x:%0x:%0x\n", + addr->sa_family, addr->sa_data[0], addr->sa_data[1], + addr->sa_data[2], addr->sa_data[3], addr->sa_data[4], + addr->sa_data[5]); + + ret = eth_prepare_mac_addr_change(netdev, addr); + if (ret) + return ret; + + if (ppe_port_set_mac_address(port_priv->ppe_port, (u8 *)addr)) { + netdev_err(netdev, "set mac address failed for dev: %s\n", netdev->name); + return -EINVAL; + } + + eth_commit_mac_addr_change(netdev, addr); + + return 0; +} + +static const struct net_device_ops edma_port_netdev_ops = { + .ndo_open = edma_port_open, + .ndo_stop = edma_port_close, + .ndo_get_stats64 = edma_port_get_stats64, + .ndo_set_mac_address = edma_port_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = edma_port_change_mtu, + .ndo_eth_ioctl = edma_port_ioctl, + .ndo_features_check = edma_port_feature_check, + .ndo_select_queue = edma_port_select_queue, +}; + +/** + * edma_port_destroy - EDMA port destroy. + * @port: PPE port + * + * Unregister and free the netdevice. + */ +void edma_port_destroy(struct ppe_port *port) +{ + int port_id = port->port_id; + struct net_device *netdev = edma_ctx->netdev_arr[port_id - 1]; + + unregister_netdev(netdev); + free_netdev(netdev); + ppe_port_phylink_destroy(port); + edma_ctx->netdev_arr[port_id - 1] = NULL; +} + +/** + * edma_port_setup - EDMA port Setup. + * @port: PPE port + * + * Initialize and register the netdevice. + * + * Return 0 on success, negative error code on failure. + */ +int edma_port_setup(struct ppe_port *port) +{ + struct ppe_device *ppe_dev = edma_ctx->ppe_dev; + struct device_node *np = port->np; + struct edma_port_priv *port_priv; + int port_id = port->port_id; + struct net_device *netdev; + u8 mac_addr[ETH_ALEN]; + int ret = 0; + u8 *maddr; + + netdev = alloc_etherdev_mqs(sizeof(struct edma_port_priv), + EDMA_NETDEV_QUEUE_NUM, EDMA_NETDEV_QUEUE_NUM); + if (!netdev) { + pr_err("alloc_etherdev() failed\n"); + return -ENOMEM; + } + + SET_NETDEV_DEV(netdev, ppe_dev->dev); + netdev->dev.of_node = np; + + /* max_mtu is set to 1500 in ether_setup(). */ + netdev->max_mtu = ETH_MAX_MTU; + + port_priv = netdev_priv(netdev); + memset((void *)port_priv, 0, sizeof(struct edma_port_priv)); + + port_priv->ppe_port = port; + port_priv->netdev = netdev; + netdev->watchdog_timeo = 5 * HZ; + netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE; + netdev->netdev_ops = &edma_port_netdev_ops; + netdev->gso_max_segs = GSO_MAX_SEGS; + + maddr = mac_addr; + if (of_get_mac_address(np, maddr)) + maddr = NULL; + + if (maddr && is_valid_ether_addr(maddr)) { + eth_hw_addr_set(netdev, maddr); + } else { + eth_hw_addr_random(netdev); + netdev_info(netdev, "GMAC%d Using random MAC address - %pM\n", + port_id, netdev->dev_addr); + } + + netdev_dbg(netdev, "Configuring the port %s(qcom-id:%d)\n", + netdev->name, port_id); + + /* We expect 'port_id' to correspond to ports numbers on SoC. + * These begin from '1' and hence we subtract + * one when using it as an array index. + */ + edma_ctx->netdev_arr[port_id - 1] = netdev; + + /* Setup phylink. */ + ret = ppe_port_phylink_setup(port, netdev); + if (ret) { + netdev_dbg(netdev, "EDMA port phylink setup for netdevice %s\n", + netdev->name); + goto port_phylink_setup_fail; + } + + /* Register the network interface. */ + ret = register_netdev(netdev); + if (ret) { + netdev_dbg(netdev, "Error registering netdevice %s\n", + netdev->name); + goto register_netdev_fail; + } + + netdev_dbg(netdev, "Setup EDMA port GMAC%d done\n", port_id); + return ret; + +register_netdev_fail: + ppe_port_phylink_destroy(port); +port_phylink_setup_fail: + free_netdev(netdev); + edma_ctx->netdev_arr[port_id - 1] = NULL; + + return ret; +} diff --git a/drivers/net/ethernet/qualcomm/ppe/edma_port.h b/drivers/net/ethernet/qualcomm/ppe/edma_port.h new file mode 100644 index 000000000000..0f2deb39556e --- /dev/null +++ b/drivers/net/ethernet/qualcomm/ppe/edma_port.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef __EDMA_PORTS__ +#define __EDMA_PORTS__ + +#include "ppe_port.h" + +#define EDMA_NETDEV_FEATURES (NETIF_F_FRAGLIST \ + | NETIF_F_SG \ + | NETIF_F_RXCSUM \ + | NETIF_F_HW_CSUM \ + | NETIF_F_TSO \ + | NETIF_F_TSO6) + +/** + * struct edma_port_priv - EDMA port priv structure. + * @ppe_port: Pointer to PPE port + * @netdev: Corresponding netdevice + * @flags: Feature flags + */ +struct edma_port_priv { + struct ppe_port *ppe_port; + struct net_device *netdev; + unsigned long flags; +}; + +void edma_port_destroy(struct ppe_port *port); +int edma_port_setup(struct ppe_port *port); +#endif diff --git a/drivers/net/ethernet/qualcomm/ppe/ppe_port.c b/drivers/net/ethernet/qualcomm/ppe/ppe_port.c index 52820e2eedf8..05c52ba07aef 100644 --- a/drivers/net/ethernet/qualcomm/ppe/ppe_port.c +++ b/drivers/net/ethernet/qualcomm/ppe/ppe_port.c @@ -13,6 +13,7 @@ #include #include +#include "edma_port.h" #include "ppe.h" #include "ppe_port.h" #include "ppe_regs.h" @@ -1277,12 +1278,26 @@ int ppe_port_mac_init(struct ppe_device *ppe_dev) goto err_port_node; } + ret = edma_port_setup(&ppe_ports->port[i]); + if (ret) { + dev_err(ppe_dev->dev, "QCOM EDMA port setup failed\n"); + i--; + goto err_port_setup; + } + i++; } of_node_put(ports_node); return 0; +err_port_setup: + /* Destroy edma ports created till now */ + while (i >= 0) { + edma_port_destroy(&ppe_ports->port[i]); + i--; + } + err_port_clk: for (j = 0; j < i; j++) ppe_port_clock_deinit(&ppe_ports->port[j]); @@ -1307,6 +1322,10 @@ void ppe_port_mac_deinit(struct ppe_device *ppe_dev) for (i = 0; i < ppe_dev->ports->num; i++) { ppe_port = &ppe_dev->ports->port[i]; + + /* Destroy all phylinks and edma ports */ + edma_port_destroy(ppe_port); + ppe_port_clock_deinit(ppe_port); } } -- 2.45.2