From patchwork Tue Aug 6 10:38:36 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Keerthy X-Patchwork-Id: 1142702 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=quarantine dis=none) header.from=ti.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ti.com header.i=@ti.com header.b="vtftEx45"; dkim-atps=neutral Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 462rpV2j0Rz9s7T for ; Tue, 6 Aug 2019 20:43:46 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 00676C21E31; Tue, 6 Aug 2019 10:39:59 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=T_DKIM_INVALID autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 2DA49C21E30; Tue, 6 Aug 2019 10:39:17 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 002DEC21E50; Tue, 6 Aug 2019 10:38:45 +0000 (UTC) Received: from lelv0142.ext.ti.com (lelv0142.ext.ti.com [198.47.23.249]) by lists.denx.de (Postfix) with ESMTPS id 89E9FC21E44 for ; Tue, 6 Aug 2019 10:38:44 +0000 (UTC) Received: from fllv0034.itg.ti.com ([10.64.40.246]) by lelv0142.ext.ti.com (8.15.2/8.15.2) with ESMTP id x76AcgJv074949; Tue, 6 Aug 2019 05:38:42 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1565087922; bh=LadpokPKhGauhUQNCunOgn25Awf8MWidBHQu5F9lNkU=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=vtftEx45ezhkavSF8huCLN8kzOO+O/3zbqtQvpguXPAMAP+emC2pY3P2jx6vYvcvD Md1uHC1m1sbSfc+y7/htzL1Q0Z2nrc0cQ/k/1/wezT+lM8WD7Zk7WgRdDAl7AEcK8K Q7V5B0TYTBMAQWUxijVfuwTiBXTV45FzzgTK8sKA= Received: from DFLE104.ent.ti.com (dfle104.ent.ti.com [10.64.6.25]) by fllv0034.itg.ti.com (8.15.2/8.15.2) with ESMTPS id x76Acg6L090364 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL); Tue, 6 Aug 2019 05:38:42 -0500 Received: from DFLE102.ent.ti.com (10.64.6.23) by DFLE104.ent.ti.com (10.64.6.25) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.1713.5; Tue, 6 Aug 2019 05:38:42 -0500 Received: from fllv0039.itg.ti.com (10.64.41.19) by DFLE102.ent.ti.com (10.64.6.23) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.1713.5 via Frontend Transport; Tue, 6 Aug 2019 05:38:42 -0500 Received: from a0393675ula.india.ti.com (ileax41-snat.itg.ti.com [10.172.224.153]) by fllv0039.itg.ti.com (8.15.2/8.15.2) with ESMTP id x76AcKvG013727; Tue, 6 Aug 2019 05:38:39 -0500 From: Keerthy To: , , , Date: Tue, 6 Aug 2019 16:08:36 +0530 Message-ID: <20190806103844.25277-7-j-keerthy@ti.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190806103844.25277-1-j-keerthy@ti.com> References: <20190806103844.25277-1-j-keerthy@ti.com> MIME-Version: 1.0 X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180 Cc: t-kristo@ti.com, u-boot@lists.denx.de, rogerq@ti.com Subject: [U-Boot] [PATCH 06/14] net: ti: icssg-prueth: Add ICSSG ethernet driver X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "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 --- drivers/net/ti/Kconfig | 8 + drivers/net/ti/Makefile | 1 + drivers/net/ti/icssg-prueth.c | 517 ++++++++++++++++++++++++++++++ drivers/net/ti/icssg.h | 31 ++ drivers/net/ti/icssg_classifier.c | 397 +++++++++++++++++++++++ 5 files changed, 954 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..f8935ee087 --- /dev/null +++ b/drivers/net/ti/icssg-prueth.c @@ -0,0 +1,517 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Texas Instruments K3 AM65 PRU Ethernet Driver + * + * Copyright (C) 2019, Texas Instruments, Incorporated + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + fdt_addr_t mdio_base; + phys_addr_t pruss_shrdram2; + 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; +}; + +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 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); + + ret = dma_get_by_name(dev, rx_chn_name, &priv->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(&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); + return ret; + } + + 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; + } + + return 0; +phy_fail: + dma_disable(&priv->dma_rx); +rx_fail: + dma_disable(&priv->dma_tx); + + return ret; +} + +void prueth_print_buf(ulong addr, const void *data, uint width, + uint count, uint linelen) +{ + print_buffer(addr, data, width, count, linelen); +} + +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); + + 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); +} + +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; + u32 val; + 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; + } + + if (ofnode_read_bool(eth_np, "enable-rgmii-delay")) + val = 0; + else + val = ICSSG_CTRL_RGMII_ID_MODE; + + regmap_update_bits(ctrl_mmr, tmp[1], ICSSG_CTRL_RGMII_ID_MODE, val); + + 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 = NULL; + 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); + err = misc_init_by_ofnode(pruss_node); + if (err) + return err; + + ret = device_find_global_by_ofnode(pruss_node, prussdev); + if (ret) + dev_err(dev, "error getting the pruss dev\n"); + + ret = pruss_request_shrmem_region(*prussdev, &prueth->pruss_shrdram2); + 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; + } + + 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 */ + + 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..2e881e6eb3 --- /dev/null +++ b/drivers/net/ti/icssg.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Texas Instruments K3 AM65 Ethernet Switch SubSystem Driver + * + * Copyright (C) 2019, Texas Instruments, Incorporated + * + */ +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); + +/* 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; diff --git a/drivers/net/ti/icssg_classifier.c b/drivers/net/ti/icssg_classifier.c new file mode 100644 index 0000000000..2af14ed5b2 --- /dev/null +++ b/drivers/net/ti/icssg_classifier.c @@ -0,0 +1,397 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Texas Instruments ICSSG Ethernet Driver + * + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +}