openwrt-cghmn-mt300n/target/linux/qualcommbe/patches-6.6/103-39-net-ethernet-qualcomm-Add-netdevice-support-for-QCOM.patch
Christian Marangi 93173aee96
qualcommbe: ipq95xx: Add initial support for new target
Add initial support for new target with the initial patch for ethernet
support using pending upstream patches for PCS UNIPHY, PPE and EDMA.

Only initramfs currently working as support for new SPI/NAND
implementation, USB, CPUFreq and other devices is still unfinished and
needs to be evaluated.

Link: https://github.com/openwrt/openwrt/pull/17725
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
2025-01-25 21:24:06 +01:00

415 lines
11 KiB
Diff

From cbcaf81cd148b77ee0570a482b536f269a9f6657 Mon Sep 17 00:00:00 2001
From: Suruchi Agarwal <quic_suruchia@quicinc.com>
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 <quic_pavir@quicinc.com>
Signed-off-by: Pavithra R <quic_pavir@quicinc.com>
Signed-off-by: Suruchi Agarwal <quic_suruchia@quicinc.com>
---
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 <linux/etherdevice.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/of_net.h>
+#include <linux/phylink.h>
+#include <linux/printk.h>
+
+#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 <linux/regmap.h>
#include <linux/rtnetlink.h>
+#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