This is the PRUSS Ethernet driver for TI AM654 Sr2.0 and laterSoCs with
the ICSSG PRU Sub-system running EMAC firmware. ICSSG Subsystem supports
two slices per instance. This driver caters to both slices / ports of
the icssg subsystem.

Since it is not possible for Ethernet driver to register more than one
port for a given instance, this patch introduces top level PRUETH as
UCLASS_MISC and binds UCLASS_ETH to individual ports in order to support
bringing up more than one Ethernet interface in U-Boot.

Since top level driver is UCLASS_MISC, board files would need to
instantiate the driver explicitly.

Signed-off-by: MD Danish Anwar <danishan...@ti.com>
---
 arch/arm/mach-k3/common.c     |  11 +
 drivers/net/ti/Kconfig        |  13 +
 drivers/net/ti/Makefile       |   1 +
 drivers/net/ti/icssg_prueth.c | 685 ++++++++++++++++++++++++++++++++++
 drivers/net/ti/icssg_prueth.h |   3 +
 5 files changed, 713 insertions(+)
 create mode 100644 drivers/net/ti/icssg_prueth.c

diff --git a/arch/arm/mach-k3/common.c b/arch/arm/mach-k3/common.c
index b0fb87b97a..d151277d6d 100644
--- a/arch/arm/mach-k3/common.c
+++ b/arch/arm/mach-k3/common.c
@@ -270,6 +270,17 @@ int misc_init_r(void)
                        printf("Failed to probe am65_cpsw_nuss driver\n");
        }
 
+       if (IS_ENABLED(CONFIG_TI_ICSSG_PRUETH)) {
+               struct udevice *dev;
+               int ret;
+
+               ret = uclass_get_device_by_driver(UCLASS_MISC,
+                                                 DM_DRIVER_GET(prueth),
+                                                 &dev);
+               if (ret)
+                       printf("Failed to probe prueth driver\n");
+       }
+
        /* Default FIT boot on HS-SE devices */
        if (get_device_type() == K3_DEVICE_TYPE_HS_SE)
                env_set("boot_fit", "1");
diff --git a/drivers/net/ti/Kconfig b/drivers/net/ti/Kconfig
index 72eccc99e5..ddfa95a0b7 100644
--- a/drivers/net/ti/Kconfig
+++ b/drivers/net/ti/Kconfig
@@ -57,3 +57,16 @@ config MDIO_TI_CPSW
        help
          This driver supports the TI CPSW MDIO interface found in various
          TI SoCs.
+
+config TI_ICSSG_PRUETH
+       bool "TI Gigabit PRU Ethernet driver"
+       depends on ARCH_K3
+       imply DM_MDIO
+       imply MISC_INIT_R
+       imply MISC
+       imply MDIO_TI_CPSW
+       select PHYLIB
+       select FS_LOADER
+       help
+         Support Gigabit Ethernet ports over the ICSSG PRU Subsystem
+         This subsystem is available starting with the AM65 platform.
diff --git a/drivers/net/ti/Makefile b/drivers/net/ti/Makefile
index 30c4c4b6d5..b2b3aa3b18 100644
--- a/drivers/net/ti/Makefile
+++ b/drivers/net/ti/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_DRIVER_TI_EMAC) += davinci_emac.o
 obj-$(CONFIG_DRIVER_TI_KEYSTONE_NET) += keystone_net.o cpsw_mdio.o
 obj-$(CONFIG_TI_AM65_CPSW_NUSS) += am65-cpsw-nuss.o
 obj-$(CONFIG_MDIO_TI_CPSW) += cpsw_mdio.o
+obj-$(CONFIG_TI_ICSSG_PRUETH) += icssg_prueth.o icssg_classifier.o 
icssg_config.o icssg_queues.o
diff --git a/drivers/net/ti/icssg_prueth.c b/drivers/net/ti/icssg_prueth.c
new file mode 100644
index 0000000000..d22a56c217
--- /dev/null
+++ b/drivers/net/ti/icssg_prueth.c
@@ -0,0 +1,685 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Texas Instruments K3 AM65 PRU Ethernet Driver
+ *
+ * Copyright (C) 2018-2024, Texas Instruments, Incorporated
+ *
+ */
+
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <clk.h>
+#include <dm/lists.h>
+#include <dm/device.h>
+#include <dma-uclass.h>
+#include <dm/of_access.h>
+#include <dm/pinctrl.h>
+#include <fs_loader.h>
+#include <miiphy.h>
+#include <net.h>
+#include <phy.h>
+#include <power-domain.h>
+#include <linux/soc/ti/ti-udma.h>
+#include <regmap.h>
+#include <remoteproc.h>
+#include <syscon.h>
+#include <soc.h>
+#include <linux/pruss_driver.h>
+#include <dm/device_compat.h>
+
+#include "icssg_prueth.h"
+#include "icss_mii_rt.h"
+
+#define ICSS_SLICE0     0
+#define ICSS_SLICE1     1
+
+#ifdef PKTSIZE_ALIGN
+#define UDMA_RX_BUF_SIZE PKTSIZE_ALIGN
+#else
+#define UDMA_RX_BUF_SIZE ALIGN(1522, ARCH_DMA_MINALIGN)
+#endif
+
+#ifdef PKTBUFSRX
+#define UDMA_RX_DESC_NUM PKTBUFSRX
+#else
+#define UDMA_RX_DESC_NUM 4
+#endif
+
+/* Config region lies in shared RAM */
+#define ICSS_CONFIG_OFFSET_SLICE0      0
+#define ICSS_CONFIG_OFFSET_SLICE1      0x8000
+
+/* Firmware flags */
+#define ICSS_SET_RUN_FLAG_VLAN_ENABLE          BIT(0)  /* switch only */
+#define ICSS_SET_RUN_FLAG_FLOOD_UNICAST                BIT(1)  /* switch only 
*/
+#define ICSS_SET_RUN_FLAG_PROMISC              BIT(2)  /* MAC only */
+#define ICSS_SET_RUN_FLAG_MULTICAST_PROMISC    BIT(3)  /* MAC only */
+
+/* CTRLMMR_ICSSG_RGMII_CTRL register bits */
+#define ICSSG_CTRL_RGMII_ID_MODE               BIT(24)
+
+/* Management packet type */
+#define PRUETH_PKT_TYPE_CMD            0x10
+
+/* Number of PRU Cores per Slice */
+#define ICSSG_NUM_PRU_CORES            3
+
+static int icssg_gmii_select(struct prueth_priv *priv)
+{
+       struct phy_device *phydev = priv->phydev;
+
+       if (phydev->interface != PHY_INTERFACE_MODE_MII &&
+           phydev->interface < PHY_INTERFACE_MODE_RGMII &&
+           phydev->interface > PHY_INTERFACE_MODE_RGMII_TXID) {
+               dev_err(priv->dev, "PHY mode unsupported %s\n",
+                       phy_string_for_interface(phydev->interface));
+               return -EINVAL;
+       }
+
+       /* AM65 SR2.0 has TX Internal delay always enabled by hardware
+        * and it is not possible to disable TX Internal delay. The below
+        * switch case block describes how we handle different phy modes
+        * based on hardware restriction.
+        */
+       switch (phydev->interface) {
+       case PHY_INTERFACE_MODE_RGMII_ID:
+               phydev->interface = PHY_INTERFACE_MODE_RGMII_RXID;
+               break;
+       case PHY_INTERFACE_MODE_RGMII_TXID:
+               phydev->interface = PHY_INTERFACE_MODE_RGMII;
+               break;
+       case PHY_INTERFACE_MODE_RGMII:
+       case PHY_INTERFACE_MODE_RGMII_RXID:
+               dev_err(priv->dev, "RGMII mode without TX delay is not 
supported");
+               return -EINVAL;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int icssg_phy_init(struct udevice *dev)
+{
+       struct prueth_priv *priv = dev_get_priv(dev);
+       struct phy_device *phydev;
+       u32 supported = PHY_GBIT_FEATURES;
+       int ret;
+
+       phydev = dm_eth_phy_connect(dev);
+       if (!phydev) {
+               dev_err(dev, "phy_connect() failed\n");
+               return -ENODEV;
+       }
+
+       /* disable unsupported features */
+       supported &= ~(PHY_10BT_FEATURES |
+                       SUPPORTED_100baseT_Half |
+                       SUPPORTED_1000baseT_Half |
+                       SUPPORTED_Pause |
+                       SUPPORTED_Asym_Pause);
+
+       phydev->supported &= supported;
+       phydev->advertising = phydev->supported;
+       priv->phydev = phydev;
+
+       ret = icssg_gmii_select(priv);
+       if (ret)
+               goto out;
+
+       ret = phy_config(phydev);
+       if (ret < 0)
+               dev_err(dev, "phy_config() failed: %d", ret);
+out:
+       return ret;
+}
+
+static void icssg_config_set_speed(struct prueth_priv *priv, int speed)
+{
+       struct prueth *prueth = priv->prueth;
+       u8 fw_speed;
+
+       switch (speed) {
+       case SPEED_1000:
+               fw_speed = FW_LINK_SPEED_1G;
+               break;
+       case SPEED_100:
+               fw_speed = FW_LINK_SPEED_100M;
+               break;
+       case SPEED_10:
+               fw_speed = FW_LINK_SPEED_10M;
+               break;
+       default:
+               /* Other links speeds not supported */
+               dev_err(priv->dev, "Unsupported link speed\n");
+               return;
+       }
+
+       writeb(fw_speed, prueth->dram[priv->port_id].pa + 
PORT_LINK_SPEED_OFFSET);
+}
+
+static int icssg_update_link(struct prueth_priv *priv)
+{
+       struct phy_device *phy = priv->phydev;
+       struct prueth *prueth = priv->prueth;
+       bool gig_en = false, full_duplex = false;
+
+       if (phy->link) { /* link up */
+               if (phy->speed == SPEED_1000)
+                       gig_en = true;
+               if (phy->duplex == DUPLEX_FULL)
+                       full_duplex = true;
+               /* Set the RGMII cfg for gig en and full duplex */
+               icssg_update_rgmii_cfg(prueth->miig_rt, phy->speed, full_duplex,
+                                      priv->port_id, priv);
+               /* update the Tx IPG based on 100M/1G speed */
+               icssg_config_ipg(priv, phy->speed, priv->port_id);
+
+               /* Send command to firmware to update Speed setting */
+               icssg_config_set_speed(priv, phy->speed);
+
+               /* Enable PORT FORWARDING */
+               emac_set_port_state(priv, ICSSG_EMAC_PORT_FORWARD);
+
+               printf("link up on port %d, speed %d, %s duplex\n",
+                      priv->port_id, phy->speed,
+                      (phy->duplex == DUPLEX_FULL) ? "full" : "half");
+       } else {
+               emac_set_port_state(priv, ICSSG_EMAC_PORT_DISABLE);
+               printf("link down on port %d\n", priv->port_id);
+       }
+
+       return phy->link;
+}
+
+struct icssg_firmwares {
+       char *pru;
+       char *rtu;
+       char *txpru;
+};
+
+static struct icssg_firmwares icssg_emac_firmwares[] = {
+       {
+               .pru = "/lib/firmware/ti-pruss/am65x-sr2-pru0-prueth-fw.elf",
+               .rtu = "/lib/firmware/ti-pruss/am65x-sr2-rtu0-prueth-fw.elf",
+               .txpru = 
"/lib/firmware/ti-pruss/am65x-sr2-txpru0-prueth-fw.elf",
+       },
+       {
+               .pru = "/lib/firmware/ti-pruss/am65x-sr2-pru1-prueth-fw.elf",
+               .rtu = "/lib/firmware/ti-pruss/am65x-sr2-rtu1-prueth-fw.elf",
+               .txpru = 
"/lib/firmware/ti-pruss/am65x-sr2-txpru1-prueth-fw.elf",
+       }
+};
+
+static int icssg_start_pru_cores(struct udevice *dev)
+{
+       struct prueth_priv *priv = dev_get_priv(dev);
+       struct prueth *prueth = priv->prueth;
+       struct icssg_firmwares *firmwares;
+       struct udevice *rproc_dev = NULL;
+       int ret, slice;
+       u32 phandle;
+       u8 index;
+
+       slice = priv->port_id;
+       index = slice * ICSSG_NUM_PRU_CORES;
+       firmwares = icssg_emac_firmwares;
+
+       ofnode_read_u32_index(dev_ofnode(prueth->dev), "ti,prus", index, 
&phandle);
+       ret = uclass_get_device_by_phandle_id(UCLASS_REMOTEPROC, phandle, 
&rproc_dev);
+       if (ret) {
+               dev_err(dev, "Unknown remote processor with phandle '0x%x' 
requested(%d)\n",
+                       phandle, ret);
+               return ret;
+       }
+
+       prueth->pru_core_id = dev_seq(rproc_dev);
+       ret = rproc_set_firmware(rproc_dev, firmwares[slice].pru);
+       if (ret)
+               return ret;
+
+       ret = rproc_boot(rproc_dev);
+       if (ret) {
+               dev_err(dev, "failed to boot PRU%d: %d\n", slice, ret);
+               return -EINVAL;
+       }
+
+       ofnode_read_u32_index(dev_ofnode(prueth->dev), "ti,prus", index + 1, 
&phandle);
+       ret = uclass_get_device_by_phandle_id(UCLASS_REMOTEPROC, phandle, 
&rproc_dev);
+       if (ret) {
+               dev_err(dev, "Unknown remote processor with phandle '0x%x' 
requested(%d)\n",
+                       phandle, ret);
+               goto halt_pru;
+       }
+
+       prueth->rtu_core_id = dev_seq(rproc_dev);
+       ret = rproc_set_firmware(rproc_dev, firmwares[slice].rtu);
+       if (ret)
+               goto halt_pru;
+
+       ret = rproc_boot(rproc_dev);
+       if (ret) {
+               dev_err(dev, "failed to boot RTU%d: %d\n", slice, ret);
+               goto halt_pru;
+       }
+
+       ofnode_read_u32_index(dev_ofnode(prueth->dev), "ti,prus", index + 2, 
&phandle);
+       ret = uclass_get_device_by_phandle_id(UCLASS_REMOTEPROC, phandle, 
&rproc_dev);
+       if (ret) {
+               dev_err(dev, "Unknown remote processor with phandle '0x%x' 
requested(%d)\n",
+                       phandle, ret);
+               goto halt_rtu;
+       }
+
+       prueth->txpru_core_id = dev_seq(rproc_dev);
+       ret = rproc_set_firmware(rproc_dev, firmwares[slice].txpru);
+       if (ret)
+               goto halt_rtu;
+
+       ret = rproc_boot(rproc_dev);
+       if (ret) {
+               dev_err(dev, "failed to boot TXPRU%d: %d\n", slice, ret);
+               goto halt_rtu;
+       }
+
+       return 0;
+
+halt_rtu:
+       rproc_stop(prueth->rtu_core_id);
+
+halt_pru:
+       rproc_stop(prueth->pru_core_id);
+       return ret;
+}
+
+static int icssg_stop_pru_cores(struct udevice *dev)
+{
+       struct prueth_priv *priv = dev_get_priv(dev);
+       struct prueth *prueth = priv->prueth;
+
+       rproc_stop(prueth->pru_core_id);
+       rproc_stop(prueth->rtu_core_id);
+       rproc_stop(prueth->txpru_core_id);
+
+       return 0;
+}
+
+static int prueth_start(struct udevice *dev)
+{
+       struct ti_udma_drv_chan_cfg_data *dma_rx_cfg_data;
+       struct eth_pdata *pdata = dev_get_plat(dev);
+       struct prueth_priv *priv = dev_get_priv(dev);
+       struct prueth *prueth = priv->prueth;
+       struct icssg_flow_cfg *flow_cfg;
+       u8 *hwaddr = pdata->enetaddr;
+       char chn_name[16];
+       void *config;
+       int ret, i;
+
+       icssg_class_set_mac_addr(prueth->miig_rt, priv->port_id, hwaddr);
+       icssg_ft1_set_mac_addr(prueth->miig_rt, priv->port_id, hwaddr);
+       icssg_class_default(prueth->miig_rt, priv->port_id, 0);
+
+       /* Set Load time configuration */
+       icssg_config(priv);
+
+       ret = icssg_start_pru_cores(dev);
+       if (ret)
+               return ret;
+
+       /* To differentiate channels for SLICE0 vs SLICE1 */
+       snprintf(chn_name, sizeof(chn_name), "tx%d-0", priv->port_id);
+
+       ret = dma_get_by_name(prueth->dev, chn_name, &prueth->dma_tx);
+       if (ret)
+               dev_err(dev, "TX dma get failed %d\n", ret);
+
+       snprintf(chn_name, sizeof(chn_name), "rx%d", priv->port_id);
+       ret = dma_get_by_name(prueth->dev, chn_name, &prueth->dma_rx);
+       if (ret)
+               dev_err(dev, "RX dma get failed %d\n", ret);
+
+       for (i = 0; i < UDMA_RX_DESC_NUM; i++) {
+               ret = dma_prepare_rcv_buf(&prueth->dma_rx,
+                                         net_rx_packets[i],
+                                         UDMA_RX_BUF_SIZE);
+               if (ret)
+                       dev_err(dev, "RX dma add buf failed %d\n", ret);
+       }
+
+       ret = dma_enable(&prueth->dma_tx);
+       if (ret) {
+               dev_err(dev, "TX dma_enable failed %d\n", ret);
+               goto tx_fail;
+       }
+
+       ret = dma_enable(&prueth->dma_rx);
+       if (ret) {
+               dev_err(dev, "RX dma_enable failed %d\n", ret);
+               goto rx_fail;
+       }
+
+       /* check if the rx_flow_id of dma_rx is as expected since
+        * driver hardcode that value in config struct to firmware
+        * in probe. Just add this sanity check to catch any change
+        * to rx channel assignment in the future.
+        */
+       dma_get_cfg(&prueth->dma_rx, 0, (void **)&dma_rx_cfg_data);
+       config = (void *)(prueth->dram[priv->port_id].pa + ICSSG_CONFIG_OFFSET);
+
+       flow_cfg = config + PSI_L_REGULAR_FLOW_ID_BASE_OFFSET;
+       writew(dma_rx_cfg_data->flow_id_base, &flow_cfg->rx_base_flow);
+       writew(0, &flow_cfg->mgm_base_flow);
+
+       dev_info(dev, "K3 ICSSG: rflow_id_base: %u, chn_name = %s\n",
+                dma_rx_cfg_data->flow_id_base, chn_name);
+
+       ret = phy_startup(priv->phydev);
+       if (ret) {
+               dev_err(dev, "phy_startup failed\n");
+               goto phy_fail;
+       }
+
+       ret = icssg_update_link(priv);
+       if (!ret) {
+               ret = -ENODEV;
+               goto phy_shut;
+       }
+
+       return 0;
+
+phy_shut:
+       phy_shutdown(priv->phydev);
+phy_fail:
+       dma_disable(&prueth->dma_rx);
+       dma_free(&prueth->dma_rx);
+rx_fail:
+       dma_disable(&prueth->dma_tx);
+       dma_free(&prueth->dma_tx);
+
+tx_fail:
+       icssg_class_disable(prueth->miig_rt, priv->port_id);
+
+       return ret;
+}
+
+static int prueth_send(struct udevice *dev, void *packet, int length)
+{
+       struct prueth_priv *priv = dev_get_priv(dev);
+       struct prueth *prueth = priv->prueth;
+       int ret;
+
+       ret = dma_send(&prueth->dma_tx, packet, length, NULL);
+
+       return ret;
+}
+
+static int prueth_recv(struct udevice *dev, int flags, uchar **packetp)
+{
+       struct prueth_priv *priv = dev_get_priv(dev);
+       struct prueth *prueth = priv->prueth;
+       int ret;
+
+       /* try to receive a new packet */
+       ret = dma_receive(&prueth->dma_rx, (void **)packetp, NULL);
+
+       return ret;
+}
+
+static int prueth_free_pkt(struct udevice *dev, uchar *packet, int length)
+{
+       struct prueth_priv *priv = dev_get_priv(dev);
+       struct prueth *prueth = priv->prueth;
+       int ret = 0;
+
+       if (length > 0) {
+               u32 pkt = prueth->rx_next % UDMA_RX_DESC_NUM;
+
+               dev_dbg(dev, "%s length:%d pkt:%u\n", __func__, length, pkt);
+
+               ret = dma_prepare_rcv_buf(&prueth->dma_rx,
+                                         net_rx_packets[pkt],
+                                         UDMA_RX_BUF_SIZE);
+               prueth->rx_next++;
+       }
+
+       return ret;
+}
+
+static void prueth_stop(struct udevice *dev)
+{
+       struct prueth_priv *priv = dev_get_priv(dev);
+       struct prueth *prueth = priv->prueth;
+
+       phy_shutdown(priv->phydev);
+
+       dma_disable(&prueth->dma_tx);
+       dma_disable(&prueth->dma_rx);
+
+       icssg_stop_pru_cores(dev);
+
+       dma_free(&prueth->dma_tx);
+       dma_free(&prueth->dma_rx);
+}
+
+static const struct eth_ops prueth_ops = {
+       .start          = prueth_start,
+       .send           = prueth_send,
+       .recv           = prueth_recv,
+       .free_pkt       = prueth_free_pkt,
+       .stop           = prueth_stop,
+};
+
+static int icssg_ofdata_parse_phy(struct udevice *dev)
+{
+       struct prueth_priv *priv = dev_get_priv(dev);
+
+       dev_read_u32(dev, "reg", &priv->port_id);
+       priv->phy_interface = dev_read_phy_mode(dev);
+       if (priv->phy_interface == PHY_INTERFACE_MODE_NA) {
+               dev_err(dev, "Invalid PHY mode '%s', port %u\n",
+                       phy_string_for_interface(priv->phy_interface),
+                       priv->port_id);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int prueth_port_probe(struct udevice *dev)
+{
+       struct prueth_priv *priv = dev_get_priv(dev);
+       struct prueth *prueth;
+       char portname[15];
+       int ret;
+
+       priv->dev = dev;
+       prueth = dev_get_priv(dev->parent);
+       priv->prueth = prueth;
+
+       sprintf(portname, "%s-%s", dev->parent->name, dev->name);
+
+       device_set_name(dev, portname);
+
+       ret = icssg_ofdata_parse_phy(dev);
+       if (ret)
+               goto out;
+
+       ret = icssg_phy_init(dev);
+       if (ret)
+               goto out;
+
+       ret = pruss_request_mem_region(prueth->pruss,
+                                      priv->port_id ? PRUSS_MEM_DRAM1 : 
PRUSS_MEM_DRAM0,
+                                      &prueth->dram[priv->port_id]);
+       if (ret) {
+               dev_err(dev, "could not request DRAM%d region\n", 
priv->port_id);
+               return ret;
+       }
+out:
+       return ret;
+}
+
+static int prueth_probe(struct udevice *dev)
+{
+       ofnode node, pruss_node, mdio_node, sram_node, curr_sram_node;
+       struct prueth *prueth = dev_get_priv(dev);
+       u32 phandle, err, sp, prev_end_addr;
+       struct udevice **prussdev = NULL;
+       ofnode eth_ports_node, eth_node;
+       struct udevice *port_dev;
+       int ret = 0;
+
+       prueth->dev = dev;
+
+       err = ofnode_read_u32(dev_ofnode(dev), "ti,prus", &phandle);
+       if (err)
+               return err;
+
+       node = ofnode_get_by_phandle(phandle);
+       if (!ofnode_valid(node))
+               return -EINVAL;
+
+       pruss_node = ofnode_get_parent(node);
+       ret = device_get_global_by_ofnode(pruss_node, prussdev);
+       if (ret)
+               dev_err(dev, "error getting the pruss dev\n");
+       prueth->pruss = *prussdev;
+
+       ret = pruss_request_mem_region(*prussdev, PRUSS_MEM_SHRD_RAM2,
+                                      &prueth->shram);
+       if (ret)
+               return ret;
+
+       ret = pruss_request_tm_region(*prussdev, &prueth->tmaddr);
+       if (ret)
+               return ret;
+
+       prueth->miig_rt = syscon_regmap_lookup_by_phandle(dev, "ti,mii-g-rt");
+       if (!prueth->miig_rt) {
+               dev_err(dev, "couldn't get mii-g-rt syscon regmap\n");
+               return -ENODEV;
+       }
+
+       prueth->mii_rt = syscon_regmap_lookup_by_phandle(dev, "ti,mii-rt");
+       if (!prueth->mii_rt) {
+               dev_err(dev, "couldn't get mii-rt syscon regmap\n");
+               return -ENODEV;
+       }
+
+       ret = ofnode_read_u32(dev_ofnode(dev), "sram", &sp);
+       if (ret) {
+               dev_err(dev, "sram node fetch failed %d\n", ret);
+               return ret;
+       }
+
+       sram_node = ofnode_get_by_phandle(sp);
+       if (!ofnode_valid(sram_node))
+               return -EINVAL;
+
+       prev_end_addr = ofnode_get_addr(sram_node);
+
+       ofnode_for_each_subnode(curr_sram_node, sram_node) {
+               u32 start_addr, size, end_addr, avail;
+               const char *name;
+
+               name = ofnode_get_name(curr_sram_node);
+               start_addr = ofnode_get_addr(curr_sram_node);
+               size = ofnode_get_size(curr_sram_node);
+               end_addr = start_addr + size;
+               avail = start_addr - prev_end_addr;
+
+               if (avail > MSMC_RAM_SIZE)
+                       break;
+
+               prev_end_addr = end_addr;
+       }
+
+       prueth->sram_pa = prev_end_addr;
+       if (prueth->sram_pa % SZ_64K != 0) {
+               /* This is constraint for SR2.0 firmware */
+               dev_err(dev, "sram address needs to be 64KB aligned\n");
+               return -EINVAL;
+       }
+       dev_dbg(dev, "sram: addr %x size %x\n", prueth->sram_pa, MSMC_RAM_SIZE);
+
+       mdio_node = ofnode_find_subnode(pruss_node, "mdio");
+       prueth->mdio_base = ofnode_get_addr(mdio_node);
+       ofnode_read_u32(mdio_node, "bus_freq", &prueth->mdio_freq);
+
+       ret = clk_get_by_name_nodev(mdio_node, "fck", &prueth->mdiofck);
+       if (ret) {
+               dev_err(dev, "failed to get clock %d\n", ret);
+               return ret;
+       }
+
+       ret = clk_enable(&prueth->mdiofck);
+       if (ret) {
+               dev_err(dev, "clk_enable failed %d\n", ret);
+               return ret;
+       }
+
+       eth_ports_node = dev_read_subnode(dev, "ethernet-ports");
+       if (!ofnode_valid(eth_ports_node))
+               return -ENOENT;
+
+       ofnode_for_each_subnode(eth_node, eth_ports_node) {
+               const char *node_name;
+               u32 port_id;
+               bool disabled;
+
+               node_name = ofnode_get_name(eth_node);
+               disabled = !ofnode_is_enabled(eth_node);
+               ret = ofnode_read_u32(eth_node, "reg", &port_id);
+               if (ret)
+                       dev_err(dev, "%s: error reading port_id (%d)\n", 
node_name, ret);
+
+               if (port_id >= PRUETH_NUM_MACS) {
+                       dev_err(dev, "%s: invalid port_id (%d)\n", node_name, 
port_id);
+                       return -EINVAL;
+               }
+
+               if (port_id < 0)
+                       continue;
+               if (disabled)
+                       continue;
+
+               ret = device_bind_driver_to_node(dev, "prueth_port",
+                                                ofnode_get_name(eth_node),
+                                                eth_node, &port_dev);
+               if (ret) {
+                       dev_err(dev, "Failed to bind to %s node\n", 
ofnode_get_name(eth_node));
+                       goto out;
+               }
+       }
+
+       return 0;
+out:
+       clk_disable(&prueth->mdiofck);
+
+       return ret;
+}
+
+static const struct udevice_id prueth_ids[] = {
+       { .compatible = "ti,am654-icssg-prueth" },
+       { .compatible = "ti,am642-icssg-prueth" },
+       { }
+};
+
+U_BOOT_DRIVER(prueth) = {
+       .name   = "prueth",
+       .id     = UCLASS_MISC,
+       .of_match = prueth_ids,
+       .probe  = prueth_probe,
+       .priv_auto = sizeof(struct prueth),
+};
+
+U_BOOT_DRIVER(prueth_port) = {
+       .name   = "prueth_port",
+       .id     = UCLASS_ETH,
+       .probe  = prueth_port_probe,
+       .ops    = &prueth_ops,
+       .priv_auto = sizeof(struct prueth_priv),
+       .plat_auto = sizeof(struct eth_pdata),
+       .flags = DM_FLAG_ALLOC_PRIV_DMA,
+};
diff --git a/drivers/net/ti/icssg_prueth.h b/drivers/net/ti/icssg_prueth.h
index 42686b8c79..0c95fefbcb 100644
--- a/drivers/net/ti/icssg_prueth.h
+++ b/drivers/net/ti/icssg_prueth.h
@@ -62,6 +62,9 @@ struct prueth {
        bool                    mdio_manual_mode;
        int                     speed;
        int                     duplex;
+       u8                      pru_core_id;
+       u8                      rtu_core_id;
+       u8                      txpru_core_id;
 };
 
 struct prueth_priv {
-- 
2.34.1

Reply via email to