diff mbox series

[v2,04/10] net: ti: icssg-prueth: Add ICSSG ethernet driver

Message ID 20200218051004.32524-5-j-keerthy@ti.com
State Superseded
Delegated to: Lokesh Vutla
Headers show
Series net: ti: icssg: Add prueth support | expand

Commit Message

J, KEERTHY Feb. 18, 2020, 5:09 a.m. UTC
This is the Ethernet driver for TI SoCs with the
ICSSG PRU Sub-system running EMAC firmware.
This driver caters to either of the slices(pru/rtu pair)
of the icssg subsystem.

Following are the firmwares needed to run cores:

am65x-pru0-prueth-fw.elf for pru0 of slice0
am65x-rtu0-prueth-fw.elf for rtu0 of slice0
am65x-pru1-prueth-fw.elf for pru1 of slice1
am65x-rtu1-prueth-fw.elf for rtu1 of slice1

One and exactly one of the slices is supported
as the u-boot ethernet supports probing one interface
at a time.

Signed-off-by: Keerthy <j-keerthy@ti.com>
---

Changes in v2:

  * Added guards defines for icssg.h
  * returning errors which were missed in v1
  * Removed unused prueth_print_buf function.
  * Removed usage misc_of_init and used uclass_get_device_by_ofnode
    in place of that.
  * Introduced started variable to keep track of the status of the
    prueth device.
 
 drivers/net/ti/Kconfig            |   8 +
 drivers/net/ti/Makefile           |   1 +
 drivers/net/ti/icssg-prueth.c     | 652 ++++++++++++++++++++++++++++++
 drivers/net/ti/icssg.h            |  36 ++
 drivers/net/ti/icssg_classifier.c | 396 ++++++++++++++++++
 5 files changed, 1093 insertions(+)
 create mode 100644 drivers/net/ti/icssg-prueth.c
 create mode 100644 drivers/net/ti/icssg.h
 create mode 100644 drivers/net/ti/icssg_classifier.c
diff mbox series

Patch

diff --git a/drivers/net/ti/Kconfig b/drivers/net/ti/Kconfig
index ecf642de10..1b6285709f 100644
--- a/drivers/net/ti/Kconfig
+++ b/drivers/net/ti/Kconfig
@@ -26,3 +26,11 @@  config TI_AM65_CPSW_NUSS
 	help
 	  This driver supports TI K3 MCU CPSW Nuss Ethernet controller
 	  in Texas Instruments K3 AM65x SoCs.
+
+config TI_AM64_ICSSG_PRUETH
+	bool "TI Gigabit PRU Ethernet driver"
+	depends on ARCH_K3
+	select PHYLIB
+	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 8d3808bb4b..b486498909 100644
--- a/drivers/net/ti/Makefile
+++ b/drivers/net/ti/Makefile
@@ -6,3 +6,4 @@  obj-$(CONFIG_DRIVER_TI_CPSW) += cpsw.o cpsw-common.o cpsw_mdio.o
 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 cpsw_mdio.o
+obj-$(CONFIG_TI_AM64_ICSSG_PRUETH) += icssg-prueth.o cpsw_mdio.o icssg_classifier.o
diff --git a/drivers/net/ti/icssg-prueth.c b/drivers/net/ti/icssg-prueth.c
new file mode 100644
index 0000000000..0a85ca3ab5
--- /dev/null
+++ b/drivers/net/ti/icssg-prueth.c
@@ -0,0 +1,652 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Texas Instruments PRU Ethernet Driver
+ *
+ * Copyright (C) 2020, Texas Instruments, Incorporated
+ *
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <clk.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dm/lists.h>
+#include <dm/device.h>
+#include <dma-uclass.h>
+#include <dm/of_access.h>
+#include <fs_loader.h>
+#include <miiphy.h>
+#include <misc.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 <ti-pruss.h>
+
+#include "cpsw_mdio.h"
+#include "icssg.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
+
+enum prueth_mac {
+	PRUETH_MAC0 = 0,
+	PRUETH_MAC1,
+	PRUETH_NUM_MACS,
+};
+
+enum prueth_port {
+	PRUETH_PORT_HOST = 0,	/* host side port */
+	PRUETH_PORT_MII0,	/* physical port MII 0 */
+	PRUETH_PORT_MII1,	/* physical port MII 1 */
+};
+
+/* 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)
+
+/**
+ * enum pruss_pru_id - PRU core identifiers
+ */
+enum pruss_pru_id {
+	PRUSS_PRU0 = 0,
+	PRUSS_PRU1,
+	PRUSS_NUM_PRUS,
+};
+
+struct prueth {
+	struct udevice		*dev;
+	struct regmap		*miig_rt;
+	struct regmap		*mii_rt;
+	fdt_addr_t		mdio_base;
+	phys_addr_t		pruss_shrdram2;
+	phys_addr_t		tmaddr;
+	struct mii_dev		*bus;
+	u32			port_id;
+	u32			sram_pa;
+	struct phy_device	*phydev;
+	bool			has_phy;
+	ofnode			phy_node;
+	u32			phy_addr;
+	ofnode			eth_node[PRUETH_NUM_MACS];
+	struct icssg_config	config[PRUSS_NUM_PRUS];
+	u32			mdio_freq;
+	int			phy_interface;
+	struct			clk mdiofck;
+	struct dma		dma_tx;
+	struct dma		dma_rx;
+	u32			rx_next;
+	u32			rx_pend;
+	int			slice;
+	bool			started;
+};
+
+/**
+ * TX IPG Values to be set for 100M and 1G link speeds.  These values are
+ * in ocp_clk cycles. So need change if ocp_clk is changed for a specific
+ * h/w design.
+ */
+#define MII_RT_TX_IPG_100M	0x166
+#define MII_RT_TX_IPG_1G	0x18
+
+#define RGMII_CFG_OFFSET	4
+
+/* Constant to choose between MII0 and MII1 */
+#define ICSS_MII0	0
+#define ICSS_MII1	1
+
+/* RGMII CFG Register bits */
+#define RGMII_CFG_GIG_EN_MII0	BIT(17)
+#define RGMII_CFG_GIG_EN_MII1	BIT(21)
+#define RGMII_CFG_FULL_DUPLEX_MII0	BIT(18)
+#define RGMII_CFG_FULL_DUPLEX_MII1	BIT(22)
+
+/* PRUSS_MII_RT Registers */
+#define PRUSS_MII_RT_RXCFG0		0x0
+#define PRUSS_MII_RT_RXCFG1		0x4
+#define PRUSS_MII_RT_TXCFG0		0x10
+#define PRUSS_MII_RT_TXCFG1		0x14
+#define PRUSS_MII_RT_TX_CRC0		0x20
+#define PRUSS_MII_RT_TX_CRC1		0x24
+#define PRUSS_MII_RT_TX_IPG0		0x30
+#define PRUSS_MII_RT_TX_IPG1		0x34
+#define PRUSS_MII_RT_PRS0		0x38
+#define PRUSS_MII_RT_PRS1		0x3c
+#define PRUSS_MII_RT_RX_FRMS0		0x40
+#define PRUSS_MII_RT_RX_FRMS1		0x44
+#define PRUSS_MII_RT_RX_PCNT0		0x48
+#define PRUSS_MII_RT_RX_PCNT1		0x4c
+#define PRUSS_MII_RT_RX_ERR0		0x50
+#define PRUSS_MII_RT_RX_ERR1		0x54
+
+static inline void icssg_update_rgmii_cfg(struct regmap *miig_rt, bool gig_en,
+					  bool full_duplex, int mii)
+{
+	u32 gig_en_mask, gig_val = 0, full_duplex_mask, full_duplex_val = 0;
+
+	gig_en_mask = (mii == ICSS_MII0) ? RGMII_CFG_GIG_EN_MII0 :
+					RGMII_CFG_GIG_EN_MII1;
+	if (gig_en)
+		gig_val = gig_en_mask;
+	regmap_update_bits(miig_rt, RGMII_CFG_OFFSET, gig_en_mask, gig_val);
+
+	full_duplex_mask = (mii == ICSS_MII0) ? RGMII_CFG_FULL_DUPLEX_MII0 :
+					   RGMII_CFG_FULL_DUPLEX_MII1;
+	if (full_duplex)
+		full_duplex_val = full_duplex_mask;
+	regmap_update_bits(miig_rt, RGMII_CFG_OFFSET, full_duplex_mask,
+			   full_duplex_val);
+}
+
+static inline void icssg_update_mii_rt_cfg(struct regmap *mii_rt, int speed,
+					   int mii)
+{
+	u32 ipg_reg, val;
+
+	ipg_reg = (mii == ICSS_MII0) ? PRUSS_MII_RT_TX_IPG0 :
+				       PRUSS_MII_RT_TX_IPG1;
+	switch (speed) {
+	case SPEED_1000:
+		val = MII_RT_TX_IPG_1G;
+		break;
+	case SPEED_100:
+		val = MII_RT_TX_IPG_100M;
+		break;
+	default:
+		/* Other links speeds not supported */
+		pr_err("Unsupported link speed\n");
+		return;
+	}
+	regmap_write(mii_rt, ipg_reg, val);
+}
+
+static int icssg_phy_init(struct udevice *dev)
+{
+	struct prueth *priv = dev_get_priv(dev);
+	struct phy_device *phydev;
+	u32 supported = PHY_GBIT_FEATURES;
+	int ret;
+
+	phydev = phy_connect(priv->bus,
+			     priv->phy_addr,
+			     priv->dev,
+			     priv->phy_interface);
+
+	if (!phydev) {
+		dev_err(dev, "phy_connect() failed\n");
+		return -ENODEV;
+	}
+
+	phydev->supported &= supported;
+	phydev->advertising = phydev->supported;
+
+#ifdef CONFIG_DM_ETH
+	if (ofnode_valid(priv->phy_node))
+		phydev->node = priv->phy_node;
+#endif
+
+	priv->phydev = phydev;
+	ret = phy_config(phydev);
+	if (ret < 0)
+		pr_err("phy_config() failed: %d", ret);
+
+	return ret;
+}
+
+static int icssg_mdio_init(struct udevice *dev)
+{
+	struct prueth *prueth = dev_get_priv(dev);
+
+	prueth->bus = cpsw_mdio_init(dev->name, prueth->mdio_base,
+				     prueth->mdio_freq,
+				     clk_get_rate(&prueth->mdiofck));
+	if (!prueth->bus)
+		return -EFAULT;
+
+	return 0;
+}
+
+static void icssg_config_set(struct prueth *prueth)
+{
+	void __iomem *va;
+
+	va = (void __iomem *)prueth->pruss_shrdram2 + prueth->slice *
+			ICSSG_CONFIG_OFFSET_SLICE1;
+
+	memcpy_toio(va, &prueth->config[0], sizeof(prueth->config[0]));
+}
+
+static int icssg_update_link(struct prueth *priv)
+{
+	struct phy_device *phy = priv->phydev;
+	bool gig_en = false, full_duplex = false;
+
+	if (phy->link) { /* link up */
+		if (phy->speed == 1000)
+			gig_en = true;
+		if (phy->duplex == 0x1)
+			full_duplex = true;
+		if (phy->speed == 100)
+			gig_en = false;
+		/* Set the RGMII cfg for gig en and full duplex */
+		icssg_update_rgmii_cfg(priv->miig_rt, gig_en, full_duplex,
+				       priv->slice);
+		/* update the Tx IPG based on 100M/1G speed */
+		icssg_update_mii_rt_cfg(priv->mii_rt, phy->speed, priv->slice);
+
+		printf("link up on port %d, speed %d, %s duplex\n",
+		       priv->port_id, phy->speed,
+		       (phy->duplex == DUPLEX_FULL) ? "full" : "half");
+	} else {
+		printf("link down on port %d\n", priv->port_id);
+	}
+
+	return phy->link;
+}
+
+static int prueth_start(struct udevice *dev)
+{
+	struct prueth *priv = dev_get_priv(dev);
+	struct eth_pdata *pdata = dev->platdata;
+	int ret, i;
+	char tx_chn_name[16];
+	char rx_chn_name[16];
+
+	icssg_class_set_mac_addr(priv->miig_rt, priv->slice,
+				 (u8 *)pdata->enetaddr);
+	icssg_class_default(priv->miig_rt, priv->slice);
+
+	/* To differentiate channels for SLICE0 vs SLICE1 */
+	snprintf(tx_chn_name, sizeof(tx_chn_name), "tx%d-0", priv->slice);
+	snprintf(rx_chn_name, sizeof(rx_chn_name), "rx%d", priv->slice);
+
+	ret = dma_get_by_name(dev, tx_chn_name, &priv->dma_tx);
+	if (ret) {
+		dev_err(dev, "TX dma get failed %d\n", ret);
+		goto tx_fail;
+	}
+
+	ret = dma_get_by_name(dev, rx_chn_name, &priv->dma_rx);
+	if (ret) {
+		dev_err(dev, "RX dma get failed %d\n", ret);
+		goto tx_fail;
+	}
+
+	for (i = 0; i < UDMA_RX_DESC_NUM; i++) {
+		ret = dma_prepare_rcv_buf(&priv->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(&priv->dma_tx);
+	if (ret) {
+		dev_err(dev, "TX dma_enable failed %d\n", ret);
+		goto tx_fail;
+	}
+
+	ret = dma_enable(&priv->dma_rx);
+	if (ret) {
+		dev_err(dev, "RX dma_enable failed %d\n", ret);
+		goto rx_fail;
+	}
+
+	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;
+	}
+
+	priv->started = true;
+
+	return 0;
+
+phy_shut:
+	phy_shutdown(priv->phydev);
+phy_fail:
+	dma_disable(&priv->dma_rx);
+	dma_free(&priv->dma_rx);
+rx_fail:
+	dma_disable(&priv->dma_tx);
+	dma_free(&priv->dma_tx);
+
+tx_fail:
+	icssg_class_disable(priv->miig_rt, priv->slice);
+
+	return ret;
+}
+
+static int prueth_send(struct udevice *dev, void *packet, int length)
+{
+	struct prueth *priv = dev_get_priv(dev);
+	int ret;
+
+	ret = dma_send(&priv->dma_tx, packet, length, NULL);
+
+	return ret;
+}
+
+static int prueth_recv(struct udevice *dev, int flags, uchar **packetp)
+{
+	struct prueth *priv = dev_get_priv(dev);
+	int ret;
+
+	/* try to receive a new packet */
+	ret = dma_receive(&priv->dma_rx, (void **)packetp, NULL);
+
+	return ret;
+}
+
+static int prueth_free_pkt(struct udevice *dev, uchar *packet, int length)
+{
+	struct prueth *priv = dev_get_priv(dev);
+	int ret = 0;
+
+	if (length > 0) {
+		u32 pkt = priv->rx_next % UDMA_RX_DESC_NUM;
+
+		dev_dbg(dev, "%s length:%d pkt:%u\n", __func__, length, pkt);
+
+		ret = dma_prepare_rcv_buf(&priv->dma_rx,
+					  net_rx_packets[pkt],
+					  UDMA_RX_BUF_SIZE);
+		priv->rx_next++;
+	}
+
+	return ret;
+}
+
+static void prueth_stop(struct udevice *dev)
+{
+	struct prueth *priv = dev_get_priv(dev);
+
+	if (!priv->started)
+		return;
+
+	icssg_class_disable(priv->miig_rt, priv->slice);
+
+	phy_shutdown(priv->phydev);
+
+	dma_disable(&priv->dma_tx);
+	dma_free(&priv->dma_tx);
+
+	dma_disable(&priv->dma_rx);
+	dma_free(&priv->dma_rx);
+
+	/* Workaround for shutdown command */
+	writel(0x0, priv->tmaddr + priv->slice * 0x200);
+
+	priv->started = false;
+}
+
+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, ofnode port_np)
+{
+	struct prueth *priv = dev_get_priv(dev);
+	struct ofnode_phandle_args out_args;
+	const char *phy_mode;
+	int ret = 0;
+
+	phy_mode = ofnode_read_string(port_np, "phy-mode");
+	if (phy_mode) {
+		priv->phy_interface =
+				phy_get_interface_by_name(phy_mode);
+		if (priv->phy_interface == -1) {
+			dev_err(dev, "Invalid PHY mode '%s'\n",
+				phy_mode);
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	ret = ofnode_parse_phandle_with_args(port_np, "phy-handle",
+					     NULL, 0, 0, &out_args);
+	if (ret) {
+		dev_err(dev, "can't parse phy-handle port (%d)\n", ret);
+		ret = 0;
+	}
+
+	priv->phy_node = out_args.node;
+	ret = ofnode_read_u32(priv->phy_node, "reg", &priv->phy_addr);
+	if (ret)
+		dev_err(dev, "failed to get phy_addr port (%d)\n", ret);
+
+out:
+	return ret;
+}
+
+static int prueth_config_rgmiidelay(struct prueth *prueth,
+				    ofnode eth_np)
+{
+	struct regmap *ctrl_mmr;
+	int ret = 0;
+	ofnode node;
+	u32 tmp[2];
+
+	ret = ofnode_read_u32_array(eth_np, "syscon-rgmii-delay", tmp, 2);
+	if (ret) {
+		dev_err(dev, "no syscon-rgmii-delay\n");
+		return ret;
+	}
+
+	node = ofnode_get_by_phandle(tmp[0]);
+	if (!ofnode_valid(node)) {
+		dev_err(dev, "can't get syscon-rgmii-delay node\n");
+		return -EINVAL;
+	}
+
+	ctrl_mmr = syscon_node_to_regmap(node);
+	if (!ctrl_mmr) {
+		dev_err(dev, "can't get ctrl_mmr regmap\n");
+		return -EINVAL;
+	}
+
+	regmap_update_bits(ctrl_mmr, tmp[1], ICSSG_CTRL_RGMII_ID_MODE, 0);
+
+	return 0;
+}
+
+static int prueth_probe(struct udevice *dev)
+{
+	struct prueth *prueth;
+	int ret = 0, i;
+	ofnode eth0_node, eth1_node, node, pruss_node, mdio_node, sram_node;
+	u32 phandle, err, sp;
+	struct udevice *prussdev;
+	struct icssg_config *config;
+
+	prueth = dev_get_priv(dev);
+	prueth->dev = dev;
+	err = ofnode_read_u32(dev_ofnode(dev), "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 = uclass_get_device_by_ofnode(UCLASS_MISC, pruss_node, &prussdev);
+	if (ret)
+		return ret;
+
+	ret = pruss_request_shrmem_region(prussdev, &prueth->pruss_shrdram2);
+	if (ret)
+		return ret;
+
+	ret = pruss_request_tm_region(prussdev, &prueth->tmaddr);
+	if (ret)
+		return ret;
+
+	node = dev_ofnode(dev);
+	eth0_node = ofnode_find_subnode(node, "ethernet-mii0");
+	eth1_node = ofnode_find_subnode(node, "ethernet-mii1");
+	/* one node must be present and available else we fail */
+	if (!ofnode_valid(eth0_node) && !ofnode_valid(eth1_node)) {
+		dev_err(dev, "neither ethernet-mii0 nor ethernet-mii1 node available\n");
+		return -ENODEV;
+	}
+
+	/*
+	 * Exactly one node must be present as uboot ethernet framework does
+	 * not support two interfaces in a single probe. So Device Tree should
+	 * have exactly one of mii0 or mii1 interface.
+	 */
+	if (ofnode_valid(eth0_node) && ofnode_valid(eth1_node)) {
+		dev_err(dev, "Both slices cannot be supported\n");
+		return -EINVAL;
+	}
+
+	if (ofnode_valid(eth0_node)) {
+		prueth->slice = 0;
+		icssg_ofdata_parse_phy(dev, eth0_node);
+		prueth->eth_node[PRUETH_MAC0] = eth0_node;
+	}
+
+	if (ofnode_valid(eth1_node)) {
+		prueth->slice = 1;
+		icssg_ofdata_parse_phy(dev, eth1_node);
+		prueth->eth_node[PRUETH_MAC0] = eth1_node;
+	}
+
+	prueth->miig_rt = syscon_regmap_lookup_by_phandle(dev, "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, "mii-rt");
+	if (!prueth->mii_rt) {
+		dev_err(dev, "couldn't get mii-rt syscon regmap\n");
+		return -ENODEV;
+	}
+
+	ret = clk_get_by_name(dev, "mdio_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;
+	}
+
+	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(node))
+		return -EINVAL;
+
+	prueth->sram_pa = ofnode_get_addr(sram_node);
+
+	if (!prueth->slice) {
+		ret = prueth_config_rgmiidelay(prueth, eth0_node);
+		if (ret) {
+			dev_err(dev, "prueth_config_rgmiidelay failed\n");
+			return ret;
+		}
+	} else {
+		ret = prueth_config_rgmiidelay(prueth, eth1_node);
+		if (ret) {
+			dev_err(dev, "prueth_config_rgmiidelay failed\n");
+			return ret;
+		}
+	}
+
+	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 = icssg_mdio_init(dev);
+	if (ret)
+		return ret;
+
+	ret = icssg_phy_init(dev);
+	if (ret) {
+		dev_err(dev, "phy_init failed\n");
+		goto out;
+	}
+
+	/* Set Load time configuration */
+	config = &prueth->config[0];
+	memset(config, 0, sizeof(*config));
+	config->addr_lo = cpu_to_le32(lower_32_bits(prueth->sram_pa));
+	config->addr_hi = cpu_to_le32(upper_32_bits(prueth->sram_pa));
+	config->num_tx_threads = 0;
+	config->rx_flow_id = 0; /* flow id for host port */
+
+	/* set buffer sizes for the pools. 0-7 are not used for dual-emac */
+	for (i = 8; i < 16; i++)
+		config->tx_buf_sz[i] = cpu_to_le32(0x1800);
+
+	icssg_config_set(prueth);
+
+	return 0;
+out:
+	cpsw_mdio_free(prueth->bus);
+	clk_disable(&prueth->mdiofck);
+
+	return ret;
+}
+
+static const struct udevice_id prueth_ids[] = {
+	{ .compatible = "ti,am654-icssg-prueth" },
+	{ }
+};
+
+U_BOOT_DRIVER(prueth) = {
+	.name	= "prueth",
+	.id	= UCLASS_ETH,
+	.of_match = prueth_ids,
+	.probe	= prueth_probe,
+	.ops	= &prueth_ops,
+	.priv_auto_alloc_size = sizeof(struct prueth),
+	.platdata_auto_alloc_size = sizeof(struct eth_pdata),
+	.flags = DM_FLAG_ALLOC_PRIV_DMA,
+};
diff --git a/drivers/net/ti/icssg.h b/drivers/net/ti/icssg.h
new file mode 100644
index 0000000000..c01a9ac109
--- /dev/null
+++ b/drivers/net/ti/icssg.h
@@ -0,0 +1,36 @@ 
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Texas Instruments ICSSG PRU Ethernet Switch SubSystem Driver
+ *
+ * Copyright (C) 2020, Texas Instruments, Incorporated
+ *
+ */
+
+#ifndef _ICSSG_H_
+#define _ICSSG_H_
+
+/* Config area lies in shared RAM */
+#define ICSSG_CONFIG_OFFSET_SLICE0   0
+#define ICSSG_CONFIG_OFFSET_SLICE1   0x8000
+
+/* Load time Fiwmware Configuration */
+struct icssg_config {
+	__le32 status;	/* Firmware status */
+	__le32 addr_lo;	/* MSMC Buffer pool base address low. */
+	__le32 addr_hi;	/* MSMC Buffer pool base address high. Must be 0 */
+	__le32 tx_buf_sz[16];	/* Array of buffer pool sizes */
+	__le32 num_tx_threads;	/* Number of active egress threads, 1 to 4 */
+	__le32 tx_rate_lim_en;	/* Bitmask: Egress rate limit enable per thread */
+	__le32 rx_flow_id;	/* RX flow id for first rx ring */
+	__le32 rx_mgr_flow_id;	/* RX flow id for the first management ring */
+	__le32 flags;		/* TBD */
+	__le32 n_burst;		/* for debug */
+	__le32 rtu_status;	/* RTU status */
+	__le32 info;		/* reserved */
+} __packed;
+
+void icssg_class_set_mac_addr(struct regmap *miig_rt, int slice, u8 *mac);
+void icssg_class_disable(struct regmap *miig_rt, int slice);
+void icssg_class_default(struct regmap *miig_rt, int slice);
+void icssg_class_promiscuous(struct regmap *miig_rt, int slice);
+#endif /* _ICSSG_H_ */
diff --git a/drivers/net/ti/icssg_classifier.c b/drivers/net/ti/icssg_classifier.c
new file mode 100644
index 0000000000..0cb63c2173
--- /dev/null
+++ b/drivers/net/ti/icssg_classifier.c
@@ -0,0 +1,396 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/* Texas Instruments ICSSG Ethernet Driver
+ *
+ * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <clk.h>
+#include <dm.h>
+#include <dm/lists.h>
+#include <dm/device.h>
+#include <dma-uclass.h>
+#include <dm/of_access.h>
+#include <miiphy.h>
+#include <net.h>
+#include <phy.h>
+#include <power-domain.h>
+#include <linux/soc/ti/ti-udma.h>
+#include <syscon.h>
+#include <ti-pruss.h>
+#include <regmap.h>
+
+#include "cpsw_mdio.h"
+#include "icssg.h"
+
+#define ICSSG_NUM_CLASSIFIERS	16
+#define ICSSG_NUM_FT1_SLOTS	8
+#define ICSSG_NUM_FT3_SLOTS	16
+
+/* Filter 1 - FT1 */
+#define FT1_NUM_SLOTS	8
+#define FT1_SLOT_SIZE	0x10	/* bytes */
+
+/* offsets from FT1 slot base i.e. slot 1 start */
+#define FT1_DA0		0x0
+#define FT1_DA1		0x4
+#define FT1_DA0_MASK	0x8
+#define FT1_DA1_MASK	0xc
+
+#define FT1_N_REG(slize, n, reg)	(offs[slice].ft1_slot_base + FT1_SLOT_SIZE * (n) + (reg))
+
+#define FT1_LEN_MASK	GENMASK(19, 16)
+#define FT1_LEN_SHIFT	16
+#define FT1_LEN(len)	(((len) << FT1_LEN_SHIFT) & FT1_LEN_MASK)
+
+#define FT1_MATCH_SLOT(n)	(GENMASK(23, 16) & (BIT(n) << 16))
+
+enum ft1_cfg_type {
+	FT1_CFG_TYPE_DISABLED = 0,
+	FT1_CFG_TYPE_EQ,
+	FT1_CFG_TYPE_GT,
+	FT1_CFG_TYPE_LT,
+};
+
+#define FT1_CFG_SHIFT(n)	(2 * (n))
+#define FT1_CFG_MASK(n)	(0x3 << FT1_CFG_SHIFT((n)))
+
+/* Filter 3 -  FT3 */
+#define FT3_NUM_SLOTS	16
+#define FT3_SLOT_SIZE	0x20	/* bytes */
+
+/* offsets from FT3 slot n's base */
+#define FT3_START	0
+#define FT3_START_AUTO	0x4
+#define FT3_START_OFFSET	0x8
+#define FT3_JUMP_OFFSET	0xc
+#define FT3_LEN		0x10
+#define FT3_CFG		0x14
+#define FT3_T		0x18
+#define FT3_T_MASK	0x1c
+
+#define FT3_N_REG(slize, n, reg)	(offs[slice].ft3_slot_base + FT3_SLOT_SIZE * (n) + (reg))
+
+/* offsets from rx_class n's base */
+#define RX_CLASS_AND_EN	0
+#define RX_CLASS_OR_EN	0x4
+
+#define RX_CLASS_NUM_SLOTS	16
+#define RX_CLASS_EN_SIZE	0x8	/* bytes */
+
+#define RX_CLASS_N_REG(slice, n, reg)	(offs[slice].rx_class_base + RX_CLASS_EN_SIZE * (n) + (reg))
+
+/* RX Class Gates */
+#define RX_CLASS_GATES_SIZE	0x4	/* bytes */
+
+#define RX_CLASS_GATES_N_REG(slice, n)	(offs[slice].rx_class_gates_base + RX_CLASS_GATES_SIZE * (n))
+
+#define RX_CLASS_GATES_ALLOW_MASK	BIT(6)
+#define RX_CLASS_GATES_RAW_MASK		BIT(5)
+#define RX_CLASS_GATES_PHASE_MASK	BIT(4)
+
+/* RX Class traffic data matching bits */
+#define RX_CLASS_FT_UC		BIT(31)
+#define RX_CLASS_FT_MC		BIT(30)
+#define RX_CLASS_FT_BC		BIT(29)
+#define RX_CLASS_FT_FW		BIT(28)
+#define RX_CLASS_FT_RCV		BIT(27)
+#define RX_CLASS_FT_VLAN	BIT(26)
+#define RX_CLASS_FT_DA_P	BIT(25)
+#define RX_CLASS_FT_DA_I	BIT(24)
+#define RX_CLASS_FT_FT1_MATCH_MASK	GENMASK(23, 16)
+#define RX_CLASS_FT_FT1_MATCH_SHIFT	16
+#define RX_CLASS_FT_FT3_MATCH_MASK	GENMASK(15, 0)
+#define RX_CLASS_FT_FT3_MATCH_SHIFT	0
+
+enum rx_class_sel_type {
+	RX_CLASS_SEL_TYPE_OR = 0,
+	RX_CLASS_SEL_TYPE_AND = 1,
+	RX_CLASS_SEL_TYPE_OR_AND_AND = 2,
+	RX_CLASS_SEL_TYPE_OR_OR_AND = 3,
+};
+
+#define FT1_CFG_SHIFT(n)	(2 * (n))
+#define FT1_CFG_MASK(n)		(0x3 << FT1_CFG_SHIFT((n)))
+
+#define RX_CLASS_SEL_SHIFT(n)	(2 * (n))
+#define RX_CLASS_SEL_MASK(n)	(0x3 << RX_CLASS_SEL_SHIFT((n)))
+
+#define ICSSG_CFG_OFFSET	0
+#define RGMII_CFG_OFFSET	4
+
+#define ICSSG_CFG_RX_L2_G_EN	BIT(2)
+
+/* these are register offsets per PRU */
+struct miig_rt_offsets {
+	u32 mac0;
+	u32 mac1;
+	u32 ft1_start_len;
+	u32 ft1_cfg;
+	u32 ft1_slot_base;
+	u32 ft3_slot_base;
+	u32 ft3_p_base;
+	u32 ft_rx_ptr;
+	u32 rx_class_base;
+	u32 rx_class_cfg1;
+	u32 rx_class_cfg2;
+	u32 rx_class_gates_base;
+	u32 rx_green;
+	u32 rx_rate_cfg_base;
+	u32 rx_rate_src_sel0;
+	u32 rx_rate_src_sel1;
+	u32 tx_rate_cfg_base;
+	u32 stat_base;
+	u32 tx_hsr_tag;
+	u32 tx_hsr_seq;
+	u32 tx_vlan_type;
+	u32 tx_vlan_ins;
+};
+
+static struct miig_rt_offsets offs[] = {
+	/* PRU0 */
+	{
+		0x8,
+		0xc,
+		0x80,
+		0x84,
+		0x88,
+		0x108,
+		0x308,
+		0x408,
+		0x40c,
+		0x48c,
+		0x490,
+		0x494,
+		0x4d4,
+		0x4e4,
+		0x504,
+		0x508,
+		0x50c,
+		0x54c,
+		0x63c,
+		0x640,
+		0x644,
+		0x648,
+	},
+	/* PRU1 */
+	{
+		0x10,
+		0x14,
+		0x64c,
+		0x650,
+		0x654,
+		0x6d4,
+		0x8d4,
+		0x9d4,
+		0x9d8,
+		0xa58,
+		0xa5c,
+		0xa60,
+		0xaa0,
+		0xab0,
+		0xad0,
+		0xad4,
+		0xad8,
+		0xb18,
+		0xc08,
+		0xc0c,
+		0xc10,
+		0xc14,
+	},
+};
+
+static void rx_class_ft1_cfg_set_type(struct regmap *miig_rt, int slice, int n,
+				      enum ft1_cfg_type type)
+{
+	u32 offset;
+
+	offset = offs[slice].ft1_cfg;
+	regmap_update_bits(miig_rt, offset, FT1_CFG_MASK(n),
+			   type << FT1_CFG_SHIFT(n));
+}
+
+static void rx_class_sel_set_type(struct regmap *miig_rt, int slice, int n,
+				  enum rx_class_sel_type type)
+{
+	u32 offset;
+
+	offset = offs[slice].rx_class_cfg1;
+	regmap_update_bits(miig_rt, offset, RX_CLASS_SEL_MASK(n),
+			   type << RX_CLASS_SEL_SHIFT(n));
+}
+
+static void rx_class_set_and(struct regmap *miig_rt, int slice, int n,
+			     u32 data)
+{
+	u32 offset;
+
+	offset = RX_CLASS_N_REG(slice, n, RX_CLASS_AND_EN);
+	regmap_write(miig_rt, offset, data);
+}
+
+static void rx_class_set_or(struct regmap *miig_rt, int slice, int n,
+			    u32 data)
+{
+	u32 offset;
+
+	offset = RX_CLASS_N_REG(slice, n, RX_CLASS_OR_EN);
+	regmap_write(miig_rt, offset, data);
+}
+
+/* disable all RX traffic */
+void icssg_class_disable(struct regmap *miig_rt, int slice)
+{
+	u32 data, offset;
+	int n;
+
+	/* Enable RX_L2_G */
+	regmap_update_bits(miig_rt, ICSSG_CFG_OFFSET, ICSSG_CFG_RX_L2_G_EN,
+			   ICSSG_CFG_RX_L2_G_EN);
+
+	for (n = 0; n < ICSSG_NUM_CLASSIFIERS; n++) {
+		/* AND_EN = 0 */
+		rx_class_set_and(miig_rt, slice, n, 0);
+		/* OR_EN = 0 */
+		rx_class_set_or(miig_rt, slice, n, 0);
+
+		/* set CFG1 to OR */
+		rx_class_sel_set_type(miig_rt, slice, n, RX_CLASS_SEL_TYPE_OR);
+
+		/* configure gate */
+		offset = RX_CLASS_GATES_N_REG(slice, n);
+		regmap_read(miig_rt, offset, &data);
+		/* clear class_raw */
+		data &= ~RX_CLASS_GATES_RAW_MASK;
+		/* set allow and phase mask */
+		data |= RX_CLASS_GATES_ALLOW_MASK | RX_CLASS_GATES_PHASE_MASK;
+		regmap_write(miig_rt, offset, data);
+	}
+
+	/* FT1 uses 6 bytes of DA address */
+	offset = offs[slice].ft1_start_len;
+	regmap_write(miig_rt, offset, FT1_LEN(6));
+
+	/* FT1 type EQ */
+	for (n = 0; n < ICSSG_NUM_FT1_SLOTS; n++)
+		rx_class_ft1_cfg_set_type(miig_rt, slice, n, FT1_CFG_TYPE_EQ);
+
+	/* FT1[0] DA compare address 00-00-00-00-00-00 */
+	offset = FT1_N_REG(slice, 0, FT1_DA0);
+	regmap_write(miig_rt, offset, 0);
+	offset = FT1_N_REG(slice, 0, FT1_DA1);
+	regmap_write(miig_rt, offset, 0);
+
+	/* FT1[0] mask FE-FF-FF-FF-FF-FF */
+	offset = FT1_N_REG(slice, 0, FT1_DA0_MASK);
+	regmap_write(miig_rt, offset, 0);
+	offset = FT1_N_REG(slice, 0, FT1_DA1_MASK);
+	regmap_write(miig_rt, offset, 0);
+
+	/* clear CFG2 */
+	regmap_write(miig_rt, offs[slice].rx_class_cfg2, 0);
+}
+
+void icssg_class_default(struct regmap *miig_rt, int slice)
+{
+	u32 offset, data;
+	int n;
+
+	/* defaults */
+	icssg_class_disable(miig_rt, slice);
+
+	/* FT1 uses 6 bytes of DA address */
+	offset = offs[slice].ft1_start_len;
+	regmap_write(miig_rt, offset, FT1_LEN(6));
+
+	/* FT1 slots to EQ */
+	for (n = 0; n < ICSSG_NUM_FT1_SLOTS; n++)
+		rx_class_ft1_cfg_set_type(miig_rt, slice, n, FT1_CFG_TYPE_EQ);
+
+	/* FT1[0] DA compare address 00-00-00-00-00-00 */
+	offset = FT1_N_REG(slice, 0, FT1_DA0);
+	regmap_write(miig_rt, offset, 0);
+	offset = FT1_N_REG(slice, 0, FT1_DA1);
+	regmap_write(miig_rt, offset, 0);
+
+	/* FT1[0] mask 00-00-00-00-00-00 */
+	offset = FT1_N_REG(slice, 0, FT1_DA0_MASK);
+	regmap_write(miig_rt, offset, 0);
+	offset = FT1_N_REG(slice, 0, FT1_DA1_MASK);
+	regmap_write(miig_rt, offset, 0);
+
+	/* Setup Classifier 4 */
+	/* match on Broadcast or MAC_PRU address */
+	data = RX_CLASS_FT_BC | RX_CLASS_FT_DA_P;
+	rx_class_set_or(miig_rt, slice, 4, data);
+
+	/* set CFG1 for OR_OR_AND for classifier 4 */
+	rx_class_sel_set_type(miig_rt, slice, 4, RX_CLASS_SEL_TYPE_OR_OR_AND);
+
+	/* ungate classifier 4 */
+	offset = RX_CLASS_GATES_N_REG(slice, 4);
+	regmap_read(miig_rt, offset, &data);
+	data |= RX_CLASS_GATES_RAW_MASK;
+	regmap_write(miig_rt, offset, data);
+
+	/* clear CFG2 */
+	regmap_write(miig_rt, offs[slice].rx_class_cfg2, 0);
+}
+
+void icssg_class_promiscuous(struct regmap *miig_rt, int slice)
+{
+	u32 data;
+	u32 offset;
+	int n;
+
+	/* defaults */
+	icssg_class_disable(miig_rt, slice);
+
+	/* FT1 uses 6 bytes of DA address */
+	offset = offs[slice].ft1_start_len;
+	regmap_write(miig_rt, offset, FT1_LEN(6));
+
+	/* FT1 type EQ */
+	for (n = 0; n < ICSSG_NUM_FT1_SLOTS; n++)
+		rx_class_ft1_cfg_set_type(miig_rt, slice, n, FT1_CFG_TYPE_EQ);
+
+	/* FT1[0] DA compare address 00-00-00-00-00-00 */
+	offset = FT1_N_REG(slice, 0, FT1_DA0);
+	regmap_write(miig_rt, offset, 0);
+	offset = FT1_N_REG(slice, 0, FT1_DA1);
+	regmap_write(miig_rt, offset, 0);
+
+	/* FT1[0] mask FE-FF-FF-FF-FF-FF */
+	offset = FT1_N_REG(slice, 0, FT1_DA0_MASK);
+	regmap_write(miig_rt, offset, 0xfffffffe);
+	offset = FT1_N_REG(slice, 0, FT1_DA1_MASK);
+	regmap_write(miig_rt, offset, 0xffff);
+
+	/* Setup Classifier 4 */
+	/* match on multicast, broadcast or unicast (ft1-0 address) */
+	data = RX_CLASS_FT_MC | RX_CLASS_FT_BC | FT1_MATCH_SLOT(0);
+	rx_class_set_or(miig_rt, slice, 4, data);
+
+	/* set CFG1 for OR_OR_AND for classifier 4 */
+	rx_class_sel_set_type(miig_rt, slice, 4, RX_CLASS_SEL_TYPE_OR_OR_AND);
+
+	/* ungate classifier 4 */
+	offset = RX_CLASS_GATES_N_REG(slice, 4);
+	regmap_read(miig_rt, offset, &data);
+	data |= RX_CLASS_GATES_RAW_MASK;
+	regmap_write(miig_rt, offset, data);
+}
+
+void icssg_class_set_mac_addr(struct regmap *miig_rt, int slice, u8 *mac)
+{
+	u32 mac0, mac1;
+
+	mac0 = mac[0] | mac[1] << 8 | mac[2] << 16 | mac[3] << 24;
+	mac1 = mac[4] | mac[5] << 8;
+
+	regmap_write(miig_rt, offs[slice].mac0, mac0);
+	regmap_write(miig_rt, offs[slice].mac1, mac1);
+}