* [PATCH v2 03/10] remoteproc: pruss: add PRU remoteproc driver
2020-02-18 5:09 [PATCH v2 00/10] net: ti: icssg: Add prueth support Keerthy
2020-02-18 5:09 ` [PATCH v2 01/10] net: eth-uclass: eth_get_dev based on SEQ_ALIAS instead of probe order Keerthy
2020-02-18 5:09 ` [PATCH v2 02/10] soc: ti: pruss: add a misc driver for PRUSS in TI SoCs Keerthy
@ 2020-02-18 5:09 ` Keerthy
2020-02-18 5:09 ` [PATCH v2 04/10] net: ti: icssg-prueth: Add ICSSG ethernet driver Keerthy
` (7 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Keerthy @ 2020-02-18 5:09 UTC (permalink / raw)
To: u-boot
The Programmable Real-Time Unit Subsystem (PRUSS) consists of
dual 32-bit RISC cores (Programmable Real-Time Units, or PRUs)
for program execution. This patch adds a remoteproc platform
driver for managing the individual PRU RISC cores life cycle.
The driver currently supports the AM65xx SoC
Signed-off-by: Keerthy <j-keerthy@ti.com>
---
drivers/remoteproc/Kconfig | 11 +
drivers/remoteproc/Makefile | 1 +
drivers/remoteproc/pru_rproc.c | 405 +++++++++++++++++++++++++++++++++
3 files changed, 417 insertions(+)
create mode 100644 drivers/remoteproc/pru_rproc.c
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 7c2e4804b5..24e536463b 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -81,4 +81,15 @@ config REMOTEPROC_TI_POWER
help
Say 'y' here to add support for TI power processors such as those
found on certain TI keystone and OMAP generation SoCs.
+
+config REMOTEPROC_TI_PRU
+ bool "Support for TI's K3 based PRU remoteproc driver"
+ select REMOTEPROC
+ depends on DM
+ depends on TI_PRUSS
+ depends on ARCH_K3
+ depends on OF_CONTROL
+ help
+ Say 'y' here to add support for TI' K3 remoteproc driver.
+
endmenu
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 69ae7bd1e8..f0e83451d6 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -14,3 +14,4 @@ obj-$(CONFIG_REMOTEPROC_TI_K3_ARM64) += ti_k3_arm64_rproc.o
obj-$(CONFIG_REMOTEPROC_TI_K3_DSP) += ti_k3_dsp_rproc.o
obj-$(CONFIG_REMOTEPROC_TI_K3_R5F) += ti_k3_r5f_rproc.o
obj-$(CONFIG_REMOTEPROC_TI_POWER) += ti_power_proc.o
+obj-$(CONFIG_REMOTEPROC_TI_PRU) += pru_rproc.o
diff --git a/drivers/remoteproc/pru_rproc.c b/drivers/remoteproc/pru_rproc.c
new file mode 100644
index 0000000000..832cffb1a0
--- /dev/null
+++ b/drivers/remoteproc/pru_rproc.c
@@ -0,0 +1,405 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PRU-RTU remoteproc driver for various SoCs
+ *
+ * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
+ * Keerthy <j-keerthy@ti.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <elf.h>
+#include <dm/of_access.h>
+#include <remoteproc.h>
+#include <errno.h>
+#include <clk.h>
+#include <reset.h>
+#include <regmap.h>
+#include <syscon.h>
+#include <asm/io.h>
+#include <power-domain.h>
+#include <ti-pruss.h>
+
+/* PRU_ICSS_PRU_CTRL registers */
+#define PRU_CTRL_CTRL 0x0000
+#define PRU_CTRL_STS 0x0004
+#define PRU_CTRL_WAKEUP_EN 0x0008
+#define PRU_CTRL_CYCLE 0x000C
+#define PRU_CTRL_STALL 0x0010
+#define PRU_CTRL_CTBIR0 0x0020
+#define PRU_CTRL_CTBIR1 0x0024
+#define PRU_CTRL_CTPPR0 0x0028
+#define PRU_CTRL_CTPPR1 0x002C
+
+/* CTRL register bit-fields */
+#define CTRL_CTRL_SOFT_RST_N BIT(0)
+#define CTRL_CTRL_EN BIT(1)
+#define CTRL_CTRL_SLEEPING BIT(2)
+#define CTRL_CTRL_CTR_EN BIT(3)
+#define CTRL_CTRL_SINGLE_STEP BIT(8)
+#define CTRL_CTRL_RUNSTATE BIT(15)
+
+#define RPROC_FLAGS_SHIFT 16
+#define RPROC_FLAGS_NONE 0
+#define RPROC_FLAGS_ELF_PHDR BIT(0 + RPROC_FLAGS_SHIFT)
+#define RPROC_FLAGS_ELF_SHDR BIT(1 + RPROC_FLAGS_SHIFT)
+
+/**
+ * enum pru_mem - PRU core memory range identifiers
+ */
+enum pru_mem {
+ PRU_MEM_IRAM = 0,
+ PRU_MEM_CTRL,
+ PRU_MEM_DEBUG,
+ PRU_MEM_MAX,
+};
+
+struct pru_privdata {
+ phys_addr_t pru_iram;
+ phys_addr_t pru_ctrl;
+ phys_addr_t pru_debug;
+ fdt_size_t pru_iramsz;
+ fdt_size_t pru_ctrlsz;
+ fdt_size_t pru_debugsz;
+ const char *fw_name;
+ u32 iram_da;
+ u32 pdram_da;
+ u32 sdram_da;
+ u32 shrdram_da;
+ u32 bootaddr;
+ int id;
+ struct pruss *prusspriv;
+};
+
+/**
+ * pru_start() - start the pru processor
+ * @dev: corresponding k3 remote processor device
+ *
+ * Return: 0 if all goes good, else appropriate error message.
+ */
+static int pru_start(struct udevice *dev)
+{
+ struct pru_privdata *priv;
+ int val = 0;
+
+ priv = dev_get_priv(dev);
+
+ val = CTRL_CTRL_EN | ((priv->bootaddr >> 2) << 16);
+ writel(val, priv->pru_ctrl + PRU_CTRL_CTRL);
+
+ return 0;
+}
+
+/**
+ * pru_stop() - Stop pru processor
+ * @dev: corresponding k3 remote processor device
+ *
+ * Return: 0 if all goes good, else appropriate error message.
+ */
+static int pru_stop(struct udevice *dev)
+{
+ struct pru_privdata *priv;
+ int val = 0;
+
+ priv = dev_get_priv(dev);
+
+ val = readl(priv->pru_ctrl + PRU_CTRL_CTRL);
+ val &= ~CTRL_CTRL_EN;
+ writel(val, priv->pru_ctrl + PRU_CTRL_CTRL);
+
+ return 0;
+}
+
+/**
+ * pru_init() - Initialize the remote processor
+ * @dev: rproc device pointer
+ *
+ * Return: 0 if all went ok, else return appropriate error
+ */
+static int pru_init(struct udevice *dev)
+{
+ return 0;
+}
+
+/*
+ * Convert PRU device address (data spaces only) to kernel virtual address
+ *
+ * Each PRU has access to all data memories within the PRUSS, accessible at
+ * different ranges. So, look through both its primary and secondary Data
+ * RAMs as well as any shared Data RAM to convert a PRU device address to
+ * kernel virtual address. Data RAM0 is primary Data RAM for PRU0 and Data
+ * RAM1 is primary Data RAM for PRU1.
+ */
+static void *pru_d_da_to_pa(struct pru_privdata *priv, u32 da, int len)
+{
+ u32 offset;
+ void *pa = NULL;
+ phys_addr_t dram0, dram1, shrdram2;
+ u32 dram0sz, dram1sz, shrdram2sz;
+
+ if (len <= 0)
+ return NULL;
+
+ dram0 = priv->prusspriv->pruss_dram0;
+ dram1 = priv->prusspriv->pruss_dram1;
+ shrdram2 = priv->prusspriv->pruss_shrdram2;
+ dram0sz = priv->prusspriv->pruss_dram0sz;
+ dram1sz = priv->prusspriv->pruss_dram1sz;
+ shrdram2sz = priv->prusspriv->pruss_shrdram2sz;
+
+ /* PRU1 has its local RAM addresses reversed */
+ if (priv->id == 1) {
+ dram1 = priv->prusspriv->pruss_dram0;
+ dram0 = priv->prusspriv->pruss_dram1;
+ dram1sz = priv->prusspriv->pruss_dram0sz;
+ dram0sz = priv->prusspriv->pruss_dram1sz;
+ }
+
+ if (da >= priv->pdram_da && da + len <= priv->pdram_da + dram0sz) {
+ offset = da - priv->pdram_da;
+ pa = (__force void *)(dram0 + offset);
+ } else if (da >= priv->sdram_da &&
+ da + len <= priv->sdram_da + dram1sz) {
+ offset = da - priv->sdram_da;
+ pa = (__force void *)(dram1 + offset);
+ } else if (da >= priv->shrdram_da &&
+ da + len <= priv->shrdram_da + shrdram2sz) {
+ offset = da - priv->shrdram_da;
+ pa = (__force void *)(shrdram2 + offset);
+ }
+
+ return pa;
+}
+
+/*
+ * Convert PRU device address (instruction space) to kernel virtual address
+ *
+ * A PRU does not have an unified address space. Each PRU has its very own
+ * private Instruction RAM, and its device address is identical to that of
+ * its primary Data RAM device address.
+ */
+static void *pru_i_da_to_pa(struct pru_privdata *priv, u32 da, int len)
+{
+ u32 offset;
+ void *pa = NULL;
+
+ if (len <= 0)
+ return NULL;
+
+ if (da >= priv->iram_da &&
+ da + len <= priv->iram_da + priv->pru_iramsz) {
+ offset = da - priv->iram_da;
+ pa = (__force void *)(priv->pru_iram + offset);
+ }
+
+ return pa;
+}
+
+/* PRU-specific address translator */
+static void *pru_da_to_pa(struct pru_privdata *priv, u64 da, int len, u32 flags)
+{
+ void *pa;
+ u32 exec_flag;
+
+ exec_flag = ((flags & RPROC_FLAGS_ELF_SHDR) ? flags & SHF_EXECINSTR :
+ ((flags & RPROC_FLAGS_ELF_PHDR) ? flags & PF_X : 0));
+
+ if (exec_flag)
+ pa = pru_i_da_to_pa(priv, da, len);
+ else
+ pa = pru_d_da_to_pa(priv, da, len);
+
+ return pa;
+}
+
+/*
+ * Custom memory copy implementation for ICSSG PRU/RTU Cores
+ *
+ * The ICSSG PRU/RTU cores have a memory copying issue with IRAM memories, that
+ * is not seen on previous generation SoCs. The data is reflected properly in
+ * the IRAM memories only for integer (4-byte) copies. Any unaligned copies
+ * result in all the other pre-existing bytes zeroed out within that 4-byte
+ * boundary, thereby resulting in wrong text/code in the IRAMs. Also, the
+ * IRAM memory port interface does not allow any 8-byte copies (as commonly
+ * used by ARM64 memcpy implementation) and throws an exception. The DRAM
+ * memory ports do not show this behavior. Use this custom copying function
+ * to properly load the PRU/RTU firmware images on all memories for simplicity.
+ *
+ * TODO: Improve the function to deal with additional corner cases like
+ * unaligned copy sizes or sub-integer trailing bytes when the need arises.
+ */
+static int pru_rproc_memcpy(void *dest, void *src, size_t count)
+{
+ const int *s = src;
+ int *d = dest;
+ int size = count / 4;
+ int *tmp_src = NULL;
+
+ /* limited to 4-byte aligned addresses and copy sizes */
+ if ((long)dest % 4 || count % 4)
+ return -EINVAL;
+
+ /* src offsets in ELF firmware image can be non-aligned */
+ if ((long)src % 4) {
+ tmp_src = malloc(count);
+ if (!tmp_src)
+ return -ENOMEM;
+
+ memcpy(tmp_src, src, count);
+ s = tmp_src;
+ }
+
+ while (size--)
+ *d++ = *s++;
+
+ kfree(tmp_src);
+
+ return 0;
+}
+
+/**
+ * pru_load() - Load pru firmware
+ * @dev: corresponding k3 remote processor device
+ * @addr: Address on the RAM from which firmware is to be loaded
+ * @size: Size of the pru firmware in bytes
+ *
+ * Return: 0 if all goes good, else appropriate error message.
+ */
+static int pru_load(struct udevice *dev, ulong addr, ulong size)
+{
+ struct pru_privdata *priv;
+ Elf32_Ehdr *ehdr;
+ Elf32_Phdr *phdr;
+ int i, ret = 0;
+
+ priv = dev_get_priv(dev);
+
+ ehdr = (Elf32_Ehdr *)addr;
+ phdr = (Elf32_Phdr *)(addr + ehdr->e_phoff);
+
+ /* go through the available ELF segments */
+ for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
+ u32 da = phdr->p_paddr;
+ u32 memsz = phdr->p_memsz;
+ u32 filesz = phdr->p_filesz;
+ u32 offset = phdr->p_offset;
+ void *ptr;
+
+ if (phdr->p_type != PT_LOAD)
+ continue;
+
+ dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n",
+ phdr->p_type, da, memsz, filesz);
+
+ if (filesz > memsz) {
+ dev_dbg(dev, "bad phdr filesz 0x%x memsz 0x%x\n",
+ filesz, memsz);
+ ret = -EINVAL;
+ break;
+ }
+
+ if (offset + filesz > size) {
+ dev_dbg(dev, "truncated fw: need 0x%x avail 0x%zx\n",
+ offset + filesz, size);
+ ret = -EINVAL;
+ break;
+ }
+
+ /* grab the kernel address for this device address */
+ ptr = pru_da_to_pa(priv, da, memsz,
+ RPROC_FLAGS_ELF_PHDR | phdr->p_flags);
+ if (!ptr) {
+ dev_dbg(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz);
+ ret = -EINVAL;
+ break;
+ }
+
+ /* skip the memzero logic performed by remoteproc ELF loader */
+ if (!phdr->p_filesz)
+ continue;
+
+ ret = pru_rproc_memcpy(ptr,
+ (void *)addr + phdr->p_offset, filesz);
+ if (ret) {
+ dev_dbg(dev, "PRU custom memory copy failed for da 0x%x memsz 0x%x\n",
+ da, memsz);
+ break;
+ }
+ }
+
+ priv->bootaddr = ehdr->e_entry;
+
+ return ret;
+}
+
+static const struct dm_rproc_ops pru_ops = {
+ .init = pru_init,
+ .start = pru_start,
+ .stop = pru_stop,
+ .load = pru_load,
+};
+
+static void pru_set_id(struct pru_privdata *priv, struct udevice *dev)
+{
+ u32 mask2 = 0x38000;
+
+ if (device_is_compatible(dev, "ti,am654-rtu"))
+ mask2 = 0x6000;
+
+ if ((priv->pru_iram & mask2) == mask2)
+ priv->id = 1;
+ else
+ priv->id = 0;
+}
+
+/**
+ * pru_probe() - Basic probe
+ * @dev: corresponding k3 remote processor device
+ *
+ * Return: 0 if all goes good, else appropriate error message.
+ */
+static int pru_probe(struct udevice *dev)
+{
+ struct pru_privdata *priv;
+ int lenp;
+ ofnode node;
+
+ node = dev_ofnode(dev);
+
+ priv = dev_get_priv(dev);
+ priv->prusspriv = dev_get_priv(dev->parent);
+
+ priv->pru_iram = devfdt_get_addr_size_index(dev, PRU_MEM_IRAM,
+ &priv->pru_iramsz);
+ priv->pru_ctrl = devfdt_get_addr_size_index(dev, PRU_MEM_CTRL,
+ &priv->pru_ctrlsz);
+ priv->pru_debug = devfdt_get_addr_size_index(dev, PRU_MEM_DEBUG,
+ &priv->pru_debugsz);
+
+ priv->fw_name = ofnode_get_property(node, "firmware-name", &lenp);
+
+ priv->iram_da = 0;
+ priv->pdram_da = 0;
+ priv->sdram_da = 0x2000;
+ priv->shrdram_da = 0x10000;
+
+ pru_set_id(priv, dev);
+
+ return 0;
+}
+
+static const struct udevice_id pru_ids[] = {
+ { .compatible = "ti,am654-pru"},
+ { .compatible = "ti,am654-rtu"},
+ {}
+};
+
+U_BOOT_DRIVER(pru) = {
+ .name = "pru",
+ .of_match = pru_ids,
+ .id = UCLASS_REMOTEPROC,
+ .ops = &pru_ops,
+ .probe = pru_probe,
+ .priv_auto_alloc_size = sizeof(struct pru_privdata),
+};
--
2.17.1
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH v2 04/10] net: ti: icssg-prueth: Add ICSSG ethernet driver
2020-02-18 5:09 [PATCH v2 00/10] net: ti: icssg: Add prueth support Keerthy
` (2 preceding siblings ...)
2020-02-18 5:09 ` [PATCH v2 03/10] remoteproc: pruss: add PRU remoteproc driver Keerthy
@ 2020-02-18 5:09 ` Keerthy
2020-02-18 5:09 ` [PATCH v2 05/10] arm: dts: k3-am65-main: Add msmc_ram node Keerthy
` (6 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Keerthy @ 2020-02-18 5:09 UTC (permalink / raw)
To: u-boot
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 --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);
+}
--
2.17.1
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH v2 09/10] arm64: dts: ti: am654-base-board: add ICSSG2 Ethernet support
2020-02-18 5:09 [PATCH v2 00/10] net: ti: icssg: Add prueth support Keerthy
` (7 preceding siblings ...)
2020-02-18 5:10 ` [PATCH v2 08/10] arm: dts: k3-am65-main: Add pruss nodes for ICSSG2 Keerthy
@ 2020-02-18 5:10 ` Keerthy
2020-02-18 5:10 ` [PATCH v2 10/10] configs: am65x_evm_a53_defconfig: Enable CONFIG_TI_AM64_ICSSG_PRUETH Keerthy
2020-02-20 9:18 ` [PATCH v2 00/10] net: ti: icssg: Add prueth support Keerthy
10 siblings, 0 replies; 15+ messages in thread
From: Keerthy @ 2020-02-18 5:10 UTC (permalink / raw)
To: u-boot
ICSSG2 provide dual Gigabit Ethernet support.
Currently mdio clock is part of this node and also
the icssg2_rgmii_pins_default pinmux node has the
mdio pins as there is no davinci mdio driver.
Currently icssg2 instances are supported.
Either mii0 or mii1 can be enabled at a time.
Signed-off-by: Keerthy <j-keerthy@ti.com>
---
Changes in v2:
* Removed a bunch of inconsistent white space in pins definition.
arch/arm/dts/k3-am654-base-board-u-boot.dtsi | 117 +++++++++++++++++++
1 file changed, 117 insertions(+)
diff --git a/arch/arm/dts/k3-am654-base-board-u-boot.dtsi b/arch/arm/dts/k3-am654-base-board-u-boot.dtsi
index a5aee02eb8..31038b7802 100644
--- a/arch/arm/dts/k3-am654-base-board-u-boot.dtsi
+++ b/arch/arm/dts/k3-am654-base-board-u-boot.dtsi
@@ -16,6 +16,64 @@
serial2 = &main_uart0;
ethernet0 = &cpsw_port1;
};
+
+ /* Dual Ethernet application node on PRU-ICSSG2 */
+ pruss2_eth: pruss2_eth {
+ compatible = "ti,am654-icssg-prueth";
+ pinctrl-names = "default";
+ pinctrl-0 = <&icssg2_rgmii_pins_default>;
+ sram = <&icssg2_sram>;
+ clocks = <&k3_clks 64 3>;
+ clock-names = "mdio_fck";
+ u-boot,dm-spl;
+
+ prus = <&pru2_0>, <&rtu2_0>, <&pru2_1>, <&rtu2_1>;
+ firmware-name = "ti-pruss/am65x-pru0-prueth-fw.elf",
+ "ti-pruss/am65x-rtu0-prueth-fw.elf",
+ "ti-pruss/am65x-pru1-prueth-fw.elf",
+ "ti-pruss/am65x-rtu1-prueth-fw.elf";
+ mii-g-rt = <&icssg2_mii_g_rt>;
+ mii-rt = <&icssg2_mii_rt>;
+ dma-coherent;
+ dmas = <&mcu_udmap &icssg2 0 UDMA_DIR_TX>, /* egress slice 0 */
+ <&mcu_udmap &icssg2 1 UDMA_DIR_TX>, /* egress slice 0 */
+ <&mcu_udmap &icssg2 2 UDMA_DIR_TX>, /* egress slice 0 */
+ <&mcu_udmap &icssg2 3 UDMA_DIR_TX>, /* mgmnt cmd slice 0 */
+ <&mcu_udmap &icssg2 4 UDMA_DIR_TX>, /* egress slice 1 */
+ <&mcu_udmap &icssg2 5 UDMA_DIR_TX>, /* egress slice 1 */
+ <&mcu_udmap &icssg2 6 UDMA_DIR_TX>, /* egress slice 1 */
+ <&mcu_udmap &icssg2 7 UDMA_DIR_TX>, /* mgmnt cmd slice 1 */
+
+ <&mcu_udmap &icssg2 0 UDMA_DIR_RX>, /* ingress slice 0 */
+ <&mcu_udmap &icssg2 1 UDMA_DIR_RX>, /* ingress slice 1 */
+ <&mcu_udmap &icssg2 2 UDMA_DIR_RX>, /* mgmnt rsp slice 0 */
+ <&mcu_udmap &icssg2 3 UDMA_DIR_RX>; /* mgmnt rsp slice 1 */
+ dma-names = "tx0-0", "tx0-1", "tx0-2", "tx0-3",
+ "tx1-0", "tx1-1", "tx1-2", "tx1-3",
+ "rx0", "rx1",
+ "rxmgm0", "rxmgm1";
+
+ pruss2_emac0: ethernet-mii0 {
+ phy-handle = <&pruss2_eth0_phy>;
+ phy-mode = "rgmii-rxid";
+ syscon-rgmii-delay = <&scm_conf 0x4120>;
+ /* Filled in by bootloader */
+ local-mac-address = [00 00 00 00 00 00];
+ };
+
+/*
+ * Commenting out the second mii interface as the framework
+ * supports one interface in a single probe
+ * So either mii1 or mii2 can be used. In case mii1 is needed
+ * uncomment mii1 and comment out mii0
+ pruss2_emac1: ethernet-mii1 {
+ phy-handle = <&pruss2_eth1_phy>;
+ phy-mode = "rgmii-rxid";
+ syscon-rgmii-delay = <&scm_conf 0x4124>;
+ local-mac-address = [00 00 00 00 00 00];
+ };
+*/
+ };
};
&cbass_main{
@@ -275,6 +333,47 @@
u-boot,dm-spl;
};
+ icssg2_rgmii_pins_default: icssg2_rgmii_pins_default {
+ pinctrl-single,pins = <
+ AM65X_IOPAD(0x00ac, PIN_INPUT, 2) /* (AH15) PRG2_PRU1_GPO0.PRG2_RGMII2_RD0 */
+ AM65X_IOPAD(0x00b0, PIN_INPUT, 2) /* (AC16) PRG2_PRU1_GPO1.PRG2_RGMII2_RD1 */
+ AM65X_IOPAD(0x00b4, PIN_INPUT, 2) /* (AD17) PRG2_PRU1_GPO2.PRG2_RGMII2_RD2 */
+ AM65X_IOPAD(0x00b8, PIN_INPUT, 2) /* (AH14) PRG2_PRU1_GPO3.PRG2_RGMII2_RD3 */
+ AM65X_IOPAD(0x00cc, PIN_OUTPUT, 2) /* (AD15) PRG2_PRU1_GPO8.PRG2_RGMII2_TD0 */
+ AM65X_IOPAD(0x00d0, PIN_OUTPUT, 2) /* (AF14) PRG2_PRU1_GPO9.PRG2_RGMII2_TD1 */
+ AM65X_IOPAD(0x00d4, PIN_OUTPUT, 2) /* (AC15) PRG2_PRU1_GPO10.PRG2_RGMII2_TD2 */
+ AM65X_IOPAD(0x00d8, PIN_OUTPUT, 2) /* (AD14) PRG2_PRU1_GPO11.PRG2_RGMII2_TD3 */
+ AM65X_IOPAD(0x00dc, PIN_INPUT, 2) /* (AE14) PRG2_PRU1_GPO16.PRG2_RGMII2_TXC */
+ AM65X_IOPAD(0x00c4, PIN_OUTPUT, 2) /* (AC17) PRG2_PRU1_GPO6.PRG2_RGMII2_TX_CTL */
+ AM65X_IOPAD(0x00c0, PIN_INPUT, 2) /* (AG15) PRG2_PRU1_GPO5.PRG2_RGMII2_RXC */
+ AM65X_IOPAD(0x00bc, PIN_INPUT, 2) /* (AG14) PRG2_PRU1_GPO4.PRG2_RGMII2_RX_CTL */
+
+ AM65X_IOPAD(0x0078, PIN_INPUT, 2) /* (AF18) PRG2_PRU0_GPO0.PRG2_RGMII1_RD0 */
+ AM65X_IOPAD(0x007c, PIN_INPUT, 2) /* (AE18) PRG2_PRU0_GPO1.PRG2_RGMII1_RD1 */
+ AM65X_IOPAD(0x0080, PIN_INPUT, 2) /* (AH17) PRG2_PRU0_GPO2.PRG2_RGMII1_RD2 */
+ AM65X_IOPAD(0x0084, PIN_INPUT, 2) /* (AG18) PRG2_PRU0_GPO3.PRG2_RGMII1_RD3 */
+ AM65X_IOPAD(0x0098, PIN_OUTPUT, 2) /* (AH16) PRG2_PRU0_GPO8.PRG2_RGMII1_TD0 */
+ AM65X_IOPAD(0x009c, PIN_OUTPUT, 2) /* (AG16) PRG2_PRU0_GPO9.PRG2_RGMII1_TD1 */
+ AM65X_IOPAD(0x00a0, PIN_OUTPUT, 2) /* (AF16) PRG2_PRU0_GPO10.PRG2_RGMII1_TD2 */
+ AM65X_IOPAD(0x00a4, PIN_OUTPUT, 2) /* (AE16) PRG2_PRU0_GPO11.PRG2_RGMII1_TD3 */
+ AM65X_IOPAD(0x00a8, PIN_INPUT, 2) /* (AD16) PRG2_PRU0_GPO16.PRG2_RGMII1_TXC */
+ AM65X_IOPAD(0x0090, PIN_OUTPUT, 2) /* (AE17) PRG2_PRU0_GPO6.PRG2_RGMII1_TX_CTL */
+ AM65X_IOPAD(0x008c, PIN_INPUT, 2) /* (AF17) PRG2_PRU0_GPO5.PRG2_RGMII1_RXC */
+ AM65X_IOPAD(0x0088, PIN_INPUT, 2) /* (AG17) PRG2_PRU0_GPO4.PRG2_RGMII1_RX_CTL */
+ /* HACK to get MDIO Pins in the right state */
+ AM65X_IOPAD(0x0094, PIN_INPUT, 2) /* (AC19) PRG2_PRU0_GPO7.PRG2_MDIO0_MDIO */
+ AM65X_IOPAD(0x00c8, PIN_OUTPUT, 2) /* (AE15) PRG2_PRU1_GPO7.PRG2_MDIO0_MDC */
+ >;
+ u-boot,dm-spl;
+ };
+
+ icssg2_mdio_pins_default: icssg2_mdio_pins_default {
+ pinctrl-single,pins = <
+ AM65X_IOPAD(0x0094, PIN_INPUT, 2) /* (AC19) PRG2_PRU0_GPO7.PRG2_MDIO0_MDIO */
+ AM65X_IOPAD(0x00c8, PIN_OUTPUT, 2) /* (AE15) PRG2_PRU1_GPO7.PRG2_MDIO0_MDC */
+ >;
+ u-boot,dm-spl;
+ };
};
&main_pmx1 {
@@ -377,3 +476,21 @@
reg = <0x60000 0x10000>;
};
};
+
+&icssg2_mdio{
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&icssg2_mdio_pins_default>;
+
+ pruss2_eth0_phy: ethernet-phy at 0 {
+ reg = <0>;
+ ti,rx-internal-delay = <DP83867_RGMIIDCTL_2_00_NS>;
+ ti,fifo-depth = <DP83867_PHYCR_FIFO_DEPTH_4_B_NIB>;
+ };
+
+ pruss2_eth1_phy: ethernet-phy at 3 {
+ reg = <3>;
+ ti,rx-internal-delay = <DP83867_RGMIIDCTL_2_00_NS>;
+ ti,fifo-depth = <DP83867_PHYCR_FIFO_DEPTH_4_B_NIB>;
+ };
+};
--
2.17.1
^ permalink raw reply related [flat|nested] 15+ messages in thread