From nobody Thu Oct 2 00:54:03 2025 Received: from server.couthit.com (server.couthit.com [162.240.164.96]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 40C3D28C2BF; Thu, 25 Sep 2025 14:13:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=162.240.164.96 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758809632; cv=none; b=oXDiOC9Jdna/n7Xpv3rd/SU+KQwAoHKV//jmlC93wNNdnwDT1kEyFGAhDvxe00lVgUQqZry+oVT+OMgSj5dtzuA1nIWrxqaM/W1sOoCHiON0FIVSTISClP3iGMBAiZTuJnbPoW+EKOWPHDlwpUOGuhpMLAJ+Yx8YX9rz2E3E6JU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758809632; c=relaxed/simple; bh=lsjFp9DqgYkTGOA3bWIKbyxbQlWpbuc9HPX7YKm3a4M=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=QiFQenV5dhB1rhLSQXbMuyGzy3iyBgVj9KjdYbkNxZVrJ/IHCAaqtMs9geiCaHRdI8GordHy1Go0nlZMJxr17MZll12NXHSj7dfC4iYbG7kwSpz9AAgN43DVCnFDIUhUR5ftXpojtbHuH6de1RP6a5Zhvjdx/e//Z6mRZL5UMrg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=couthit.com; spf=pass smtp.mailfrom=couthit.com; dkim=pass (2048-bit key) header.d=couthit.com header.i=@couthit.com header.b=Y2qQYkfG; arc=none smtp.client-ip=162.240.164.96 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=couthit.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=couthit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=couthit.com header.i=@couthit.com header.b="Y2qQYkfG" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=couthit.com ; s=default; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=nDQAkKTcojayWtyAlO9HNMge1AGUfVuKebx/G8Kcra0=; b=Y2qQYkfGVZCa1D7fahG5CmzAzP wSdngK2aIQYgYpryvThQDqCVh9Bv+6llhzpBHw6G8DlAPdlqBJ06ztxJyY6WA6ewRGCkMEqM5evVJ LFvWlKovF2AQEiDMLAc6VvXVSW7ItH5ZQ2/GhwuIVBa9ZC+PiDoxGjguDIdyBzL6VMX9S1BB5l306 7SZhAlMC5yC78sJakcvSQgsww1bkEHZ0lGxm9CePuopWJdvx/qJ4Rro/Wt7ngfMDER9uNfzSWSL6O 5ft5j+47V+hlCpXUABv4kC30ImoKmI8TlLh12t8JnBh7zaNljxSsWADtdSzXRc52k4h3nLXZCsOGQ vKxcGagg==; Received: from [122.175.9.182] (port=9537 helo=cypher.couthit.local) by server.couthit.com with esmtpa (Exim 4.98.1) (envelope-from ) id 1v1mjD-00000005fRb-3ee9; Thu, 25 Sep 2025 10:13:48 -0400 From: Parvathi Pudi To: andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, danishanwar@ti.com, parvathi@couthit.com, rogerq@kernel.org, pmohan@couthit.com, basharath@couthit.com, afd@ti.com Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org, linux-arm-kernel@lists.infradead.org, pratheesh@ti.com, prajith@ti.com, vigneshr@ti.com, praneeth@ti.com, srk@ti.com, rogerq@ti.com, krishna@couthit.com, mohan@couthit.com Subject: [PATCH net-next 1/3] net: ti: icssm-prueth: Adds helper functions to configure and maintain FDB Date: Thu, 25 Sep 2025 19:32:10 +0530 Message-ID: <20250925141246.3433603-2-parvathi@couthit.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250925141246.3433603-1-parvathi@couthit.com> References: <20250925141246.3433603-1-parvathi@couthit.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-AntiAbuse: This header was added to track abuse, please include it with any abuse report X-AntiAbuse: Primary Hostname - server.couthit.com X-AntiAbuse: Original Domain - vger.kernel.org X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] X-AntiAbuse: Sender Address Domain - couthit.com X-Get-Message-Sender-Via: server.couthit.com: authenticated_id: parvathi@couthit.com X-Authenticated-Sender: server.couthit.com: parvathi@couthit.com X-Source: X-Source-Args: X-Source-Dir: Content-Type: text/plain; charset="utf-8" From: Roger Quadros This patch introduces helper functions to configure and maintain Forwarding Database (FDB) tables to aid with the switch mode feature for PRU-ICSS ports. The PRU-ICSS FDB is maintained such that it is always in sync with the Linux bridge driver FDB. The FDB is used by the driver to determine whether to flood a packet, received from the user plane, to both ports or direct it to a specific port using the flags in the FDB table entry. The FDB is implemented in two main components: the Index table and the MAC Address table. Adding, deleting, and maintaining entries are handled by the PRUETH driver. There are two types of entries: Dynamic: created from the received packets and are subject to aging. Static: created by the user and these entries never age out. 8-bit hash value obtained using the source MAC address is used to identify the index to the Index/Hash table. A bucket-based approach is used to collate source MAC addresses with the same hash value. The Index/Hash table holds the bucket index (16-bit value) and the number of entries in the bucket with the same hash value (16-bit value). This table can hold up to 256 entries, with each entry consuming 4 bytes of memory. The bucket index value points to the MAC address table indicating the start of MAC addresses having the same hash values. Each entry in the MAC Address table consists of: 1. 6 bytes of the MAC address, 2. 2-byte aging time, and 3. 1-byte each for port information and flags respectively. When a new entry is added to the FDB, the hash value is calculated using an XOR operation on the 6-byte MAC address. The result is used as an index into the Hash/Index table to check if any entries exist. If no entries are present, the first available empty slot in the MAC Address table is allocated to insert this MAC address. If entries with the same hash value are already present, the new MAC address entry is added to the MAC Address table in such a way that it ensures all entries are grouped together and sorted in ascending MAC address order. This approach helps efficiently manage FDB entries. Reviewed-by: Mohan Reddy Putluru Signed-off-by: Roger Quadros Signed-off-by: Andrew F. Davis Signed-off-by: Basharath Hussain Khaja Signed-off-by: Parvathi Pudi --- drivers/net/ethernet/ti/Makefile | 2 +- drivers/net/ethernet/ti/icssm/icssm_prueth.h | 3 + .../ethernet/ti/icssm/icssm_prueth_fdb_tbl.h | 66 +++ .../ethernet/ti/icssm/icssm_prueth_switch.c | 536 ++++++++++++++++++ .../ethernet/ti/icssm/icssm_prueth_switch.h | 20 + drivers/net/ethernet/ti/icssm/icssm_switch.h | 5 + 6 files changed, 631 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/ti/icssm/icssm_prueth_fdb_tbl.h create mode 100644 drivers/net/ethernet/ti/icssm/icssm_prueth_switch.c create mode 100644 drivers/net/ethernet/ti/icssm/icssm_prueth_switch.h diff --git a/drivers/net/ethernet/ti/Makefile b/drivers/net/ethernet/ti/Mak= efile index 93c0a4d0e33a..1fd149dd6115 100644 --- a/drivers/net/ethernet/ti/Makefile +++ b/drivers/net/ethernet/ti/Makefile @@ -4,7 +4,7 @@ # =20 obj-$(CONFIG_TI_PRUETH) +=3D icssm-prueth.o -icssm-prueth-y :=3D icssm/icssm_prueth.o +icssm-prueth-y :=3D icssm/icssm_prueth.o icssm/icssm_prueth_switch.o =20 obj-$(CONFIG_TI_CPSW) +=3D cpsw-common.o obj-$(CONFIG_TI_DAVINCI_EMAC) +=3D cpsw-common.o diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth.h b/drivers/net/eth= ernet/ti/icssm/icssm_prueth.h index 8e7e0af08144..4b50133c5a72 100644 --- a/drivers/net/ethernet/ti/icssm/icssm_prueth.h +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth.h @@ -15,6 +15,7 @@ =20 #include "icssm_switch.h" #include "icssm_prueth_ptp.h" +#include "icssm_prueth_fdb_tbl.h" =20 /* ICSSM size of redundancy tag */ #define ICSSM_LRE_TAG_SIZE 6 @@ -248,6 +249,8 @@ struct prueth { struct prueth_emac *emac[PRUETH_NUM_MACS]; struct net_device *registered_netdevs[PRUETH_NUM_MACS]; =20 + struct fdb_tbl *fdb_tbl; + unsigned int eth_type; size_t ocmc_ram_size; u8 emac_configured; diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth_fdb_tbl.h b/drivers= /net/ethernet/ti/icssm/icssm_prueth_fdb_tbl.h new file mode 100644 index 000000000000..e8ebc013fd24 --- /dev/null +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth_fdb_tbl.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019-2021 Texas Instruments Incorporated - https://www.ti= .com */ +#ifndef __NET_TI_PRUSS_FDB_TBL_H +#define __NET_TI_PRUSS_FDB_TBL_H + +#include +#include +#include "icssm_prueth.h" + +#define ETHER_ADDR_LEN 6 + +/* 4 bytes */ +struct fdb_index_tbl_entry_t { + /* Bucket Table index of first Bucket with this MAC address */ + u16 bucket_idx; + u16 bucket_entries; /* Number of entries in this bucket */ +}; + +/* 4 * 256 =3D 1024 =3D 0x200 bytes */ +struct fdb_index_array_t { + struct fdb_index_tbl_entry_t index_tbl_entry[FDB_INDEX_TBL_MAX_ENTRIES]; +}; + +/* 10 bytes */ +struct fdb_mac_tbl_entry_t { + u8 mac[ETHER_ADDR_LEN]; + u16 age; + u8 port; /* 0 based: 0=3Dport1, 1=3Dport2 */ + u8 is_static:1; + u8 active:1; +}; + +/* 10 * 256 =3D 2560 =3D 0xa00 bytes */ +struct fdb_mac_tbl_array_t { + struct fdb_mac_tbl_entry_t mac_tbl_entry[FDB_MAC_TBL_MAX_ENTRIES]; +}; + +/* 1 byte */ +struct fdb_stp_config { + u8 state; /* per-port STP state (defined in FW header) */ +}; + +/* 1 byte */ +struct fdb_flood_config { + u8 host_flood_enable:1; + u8 port1_flood_enable:1; + u8 port2_flood_enable:1; +}; + +/* 2 byte */ +struct fdb_arbitration { + u8 host_lock; + u8 pru_locks; +}; + +struct fdb_tbl { + struct fdb_index_array_t *index_a; /* fdb index table */ + struct fdb_mac_tbl_array_t *mac_tbl_a; /* fdb MAC table */ + struct fdb_stp_config *port1_stp_cfg; /* port 1 stp config */ + struct fdb_stp_config *port2_stp_cfg; /* port 2 stp config */ + struct fdb_flood_config *flood_enable_flags; /* per-port flood enable */ + struct fdb_arbitration *locks; /* fdb locking mechanism */ + u16 total_entries; /* total number of entries in hash table */ +}; + +#endif diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth_switch.c b/drivers/= net/ethernet/ti/icssm/icssm_prueth_switch.c new file mode 100644 index 000000000000..edde05a3f050 --- /dev/null +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth_switch.c @@ -0,0 +1,536 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Texas Instruments PRUETH Switch Driver + * + * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti= .com + */ +#include +#include +#include +#include +#include "icssm_prueth.h" +#include "icssm_prueth_switch.h" +#include "icssm_prueth_fdb_tbl.h" + +#define FDB_IDX_TBL_ENTRY(n) (&prueth->fdb_tbl->index_a->index_tbl_entry[n= ]) + +#define FDB_MAC_TBL_ENTRY(n) (&prueth->fdb_tbl->mac_tbl_a->mac_tbl_entry[n= ]) + +void icssm_prueth_sw_free_fdb_table(struct prueth *prueth) +{ + if (prueth->emac_configured) + return; + + kfree(prueth->fdb_tbl); + prueth->fdb_tbl =3D NULL; +} + +void icssm_prueth_sw_fdb_tbl_init(struct prueth *prueth) +{ + struct fdb_tbl *t =3D prueth->fdb_tbl; + + t->index_a =3D (struct fdb_index_array_t *)((__force const void *) + prueth->mem[V2_1_FDB_TBL_LOC].va + + V2_1_FDB_TBL_OFFSET); + t->mac_tbl_a =3D (struct fdb_mac_tbl_array_t *)((__force const void *) + t->index_a + FDB_INDEX_TBL_MAX_ENTRIES * + sizeof(struct fdb_index_tbl_entry_t)); + t->port1_stp_cfg =3D (struct fdb_stp_config *)((__force const void *) + t->mac_tbl_a + FDB_MAC_TBL_MAX_ENTRIES * + sizeof(struct fdb_mac_tbl_entry_t)); + t->port2_stp_cfg =3D (struct fdb_stp_config *)((__force const void *) + t->port1_stp_cfg + sizeof(struct fdb_stp_config)); + t->flood_enable_flags =3D + (struct fdb_flood_config *)((__force const void *) + t->port2_stp_cfg + sizeof(struct fdb_stp_config)); + t->locks =3D (struct fdb_arbitration *)((__force const void *) + t->flood_enable_flags + + sizeof(struct fdb_flood_config)); + + t->flood_enable_flags->host_flood_enable =3D 1; + t->flood_enable_flags->port1_flood_enable =3D 1; + t->flood_enable_flags->port2_flood_enable =3D 1; + t->locks->host_lock =3D 0; + t->total_entries =3D 0; +} + +static void icssm_prueth_sw_fdb_spin_lock(struct fdb_tbl *fdb_tbl) +{ + /* Take the host lock */ + writeb(1, (u8 __iomem *)&fdb_tbl->locks->host_lock); + + /* Wait for the PRUs to release their locks */ + while (readb((u8 __iomem *)&fdb_tbl->locks->pru_locks)) + ; +} + +static void icssm_prueth_sw_fdb_spin_unlock(struct fdb_tbl *fdb_tbl) +{ + writeb(0, (u8 __iomem *)&fdb_tbl->locks->host_lock); +} + +static void icssm_mac_copy(u8 *dst, const u8 *src) +{ + u8 i; + + for (i =3D 0; i < ETHER_ADDR_LEN; i++) { + *(dst) =3D *(src); + dst++; + src++; + } +} + +static s8 icssm_mac_cmp(const u8 *mac_a, const u8 *mac_b) +{ + s8 ret =3D 0, i; + + for (i =3D 0; i < ETHER_ADDR_LEN; i++) { + if (mac_a[i] =3D=3D mac_b[i]) + continue; + + ret =3D mac_a[i] < mac_b[i] ? -1 : 1; + break; + } + + return ret; +} + +static u8 icssm_prueth_sw_fdb_hash(const u8 *mac) +{ + return (mac[0] ^ mac[1] ^ mac[2] ^ mac[3] ^ mac[4] ^ mac[5]); +} + +static s16 +icssm_prueth_sw_fdb_search(struct fdb_mac_tbl_array_t *mac_tbl, + struct fdb_index_tbl_entry_t *bucket_info, + const u8 *mac) +{ + u8 mac_tbl_idx =3D bucket_info->bucket_idx; + int i; + + for (i =3D 0; i < bucket_info->bucket_entries; i++, mac_tbl_idx++) { + if (!icssm_mac_cmp(mac, + mac_tbl->mac_tbl_entry[mac_tbl_idx].mac)) + return mac_tbl_idx; + } + + return -ENODATA; +} + +static u16 icssm_prueth_sw_fdb_find_open_slot(struct fdb_tbl *fdb_tbl) +{ + u16 i; + + for (i =3D 0; i < FDB_MAC_TBL_MAX_ENTRIES; i++) { + if (!fdb_tbl->mac_tbl_a->mac_tbl_entry[i].active) + break; + } + + return i; +} + +static s16 +icssm_prueth_sw_fdb_find_bucket_insert_point(struct fdb_tbl *fdb, + struct fdb_index_tbl_entry_t + *bkt_info, + const u8 *mac, const u8 port) +{ + struct fdb_mac_tbl_array_t *mac_tbl =3D fdb->mac_tbl_a; + struct fdb_mac_tbl_entry_t *e; + u8 mac_tbl_idx; + s8 cmp; + int i; + + mac_tbl_idx =3D bkt_info->bucket_idx; + + for (i =3D 0; i < bkt_info->bucket_entries; i++, mac_tbl_idx++) { + e =3D &mac_tbl->mac_tbl_entry[mac_tbl_idx]; + cmp =3D icssm_mac_cmp(mac, e->mac); + if (cmp < 0) { + return mac_tbl_idx; + } else if (cmp =3D=3D 0) { + if (e->port !=3D port) { + /* MAC is already in FDB, only port is + * different. So just update the port. + * Note: total_entries and bucket_entries + * remain the same. + */ + icssm_prueth_sw_fdb_spin_lock(fdb); + e->port =3D port; + icssm_prueth_sw_fdb_spin_unlock(fdb); + } + + /* MAC and port are the same, touch the fdb */ + e->age =3D 0; + return -1; + } + } + + return mac_tbl_idx; +} + +static s16 +icssm_prueth_sw_fdb_check_empty_slot_left(struct fdb_mac_tbl_array_t *mac_= tbl, + u8 mac_tbl_idx) +{ + s16 i; + + for (i =3D mac_tbl_idx - 1; i > -1; i--) { + if (!mac_tbl->mac_tbl_entry[i].active) + break; + } + + return i; +} + +static s16 +icssm_prueth_sw_fdb_check_empty_slot_right(struct fdb_mac_tbl_array_t *mac= _tbl, + u8 mac_tbl_idx) +{ + s16 i; + + for (i =3D mac_tbl_idx; i < FDB_MAC_TBL_MAX_ENTRIES; i++) { + if (!mac_tbl->mac_tbl_entry[i].active) + return i; + } + + return -1; +} + +static void icssm_prueth_sw_fdb_move_range_left(struct prueth *prueth, + u16 left, u16 right) +{ + u8 *src, *dst; + u32 sz =3D 0; + u16 i; + + for (i =3D left; i < right; i++) { + dst =3D (u8 *)FDB_MAC_TBL_ENTRY(i); + src =3D (u8 *)FDB_MAC_TBL_ENTRY(i + 1); + sz =3D sizeof(struct fdb_mac_tbl_entry_t); + memcpy_toio((void __iomem *)dst, src, sz); + } +} + +static void icssm_prueth_sw_fdb_move_range_right(struct prueth *prueth, + u16 left, u16 right) +{ + u8 *src, *dst; + u32 sz =3D 0; + u16 i; + + for (i =3D right; i > left; i--) { + dst =3D (u8 *)FDB_MAC_TBL_ENTRY(i); + src =3D (u8 *)FDB_MAC_TBL_ENTRY(i - 1); + sz =3D sizeof(struct fdb_mac_tbl_entry_t); + memcpy_toio((void __iomem *)dst, src, sz); + } +} + +static void icssm_prueth_sw_fdb_update_index_tbl(struct prueth *prueth, + u16 left, u16 right) +{ + u8 hash, hash_prev; + u16 i; + + /* To ensure we don't improperly update the + * bucket index, initialize with an invalid + * hash in case we are in leftmost slot + */ + hash_prev =3D 0xff; + + if (left > 0) { + hash_prev =3D + icssm_prueth_sw_fdb_hash + (FDB_MAC_TBL_ENTRY(left - 1)->mac); + } + + /* For each moved element, update the bucket index */ + for (i =3D left; i <=3D right; i++) { + hash =3D icssm_prueth_sw_fdb_hash(FDB_MAC_TBL_ENTRY(i)->mac); + + /* Only need to update buckets once */ + if (hash !=3D hash_prev) + FDB_IDX_TBL_ENTRY(hash)->bucket_idx =3D i; + + hash_prev =3D hash; + } +} + +static struct fdb_mac_tbl_entry_t * +icssm_prueth_sw_get_empty_mac_tbl_entry(struct prueth *prueth, + struct fdb_index_tbl_entry_t + *bucket_info, u8 suggested_mac_tbl_idx, + bool *update_indexes, const u8 *mac) +{ + s16 empty_slot_idx =3D 0, left =3D 0, right =3D 0; + u8 mti =3D suggested_mac_tbl_idx; + struct fdb_mac_tbl_array_t *mt; + struct fdb_tbl *fdb; + + fdb =3D prueth->fdb_tbl; + mt =3D fdb->mac_tbl_a; + + if (!FDB_MAC_TBL_ENTRY(mti)->active) { + /* Claim the entry */ + FDB_MAC_TBL_ENTRY(mti)->active =3D 1; + + return FDB_MAC_TBL_ENTRY(mti); + } + + if (fdb->total_entries =3D=3D FDB_MAC_TBL_MAX_ENTRIES) + return NULL; + + empty_slot_idx =3D icssm_prueth_sw_fdb_check_empty_slot_left(mt, mti); + if (empty_slot_idx =3D=3D -1) { + /* Nothing available on the left. But table isn't full + * so there must be space to the right, + */ + empty_slot_idx =3D + icssm_prueth_sw_fdb_check_empty_slot_right(mt, mti); + + /* Shift right */ + left =3D mti; + right =3D empty_slot_idx; + icssm_prueth_sw_fdb_move_range_right(prueth, left, right); + + /* Claim the entry */ + FDB_MAC_TBL_ENTRY(mti)->active =3D 1; + + icssm_mac_copy(FDB_MAC_TBL_ENTRY(mti)->mac, mac); + + /* There is a chance we moved something in a + * different bucket, update index table + */ + icssm_prueth_sw_fdb_update_index_tbl(prueth, left, right); + + return FDB_MAC_TBL_ENTRY(mti); + } + + if (empty_slot_idx =3D=3D mti - 1) { + /* There is space immediately left of the open slot, + * which means the inserted MAC address + * must be the lowest-valued MAC address in bucket. + * Update bucket pointer accordingly. + */ + bucket_info->bucket_idx =3D empty_slot_idx; + + /* Claim the entry */ + FDB_MAC_TBL_ENTRY(empty_slot_idx)->active =3D 1; + + return FDB_MAC_TBL_ENTRY(empty_slot_idx); + } + + /* There is empty space to the left, shift MAC table entries left */ + left =3D empty_slot_idx; + right =3D mti - 1; + icssm_prueth_sw_fdb_move_range_left(prueth, left, right); + + /* Claim the entry */ + FDB_MAC_TBL_ENTRY(mti - 1)->active =3D 1; + + icssm_mac_copy(FDB_MAC_TBL_ENTRY(mti - 1)->mac, mac); + + /* There is a chance we moved something in a + * different bucket, update index table + */ + icssm_prueth_sw_fdb_update_index_tbl(prueth, left, right); + + return FDB_MAC_TBL_ENTRY(mti - 1); +} + +static int icssm_prueth_sw_insert_fdb_entry(struct prueth_emac *emac, + const u8 *mac, u8 is_static) +{ + struct fdb_index_tbl_entry_t *bucket_info; + struct fdb_mac_tbl_entry_t *mac_info; + struct prueth *prueth =3D emac->prueth; + struct prueth_emac *other_emac; + enum prueth_port other_port_id; + u8 hash_val, mac_tbl_idx; + struct fdb_tbl *fdb; + s16 ret; + + fdb =3D prueth->fdb_tbl; + other_port_id =3D (emac->port_id =3D=3D PRUETH_PORT_MII0) ? + PRUETH_PORT_MII1 : PRUETH_PORT_MII0; + + other_emac =3D prueth->emac[other_port_id - 1]; + + if (fdb->total_entries =3D=3D FDB_MAC_TBL_MAX_ENTRIES) + return -ENOMEM; + + if (icssm_mac_cmp(mac, emac->mac_addr) =3D=3D 0 || + icssm_mac_cmp(mac, other_emac->mac_addr) =3D=3D 0) { + /* Don't insert fdb of own mac addr */ + return -EINVAL; + } + + /* Get the bucket that the mac belongs to */ + hash_val =3D icssm_prueth_sw_fdb_hash(mac); + bucket_info =3D FDB_IDX_TBL_ENTRY(hash_val); + + if (!bucket_info->bucket_entries) { + mac_tbl_idx =3D icssm_prueth_sw_fdb_find_open_slot(fdb); + bucket_info->bucket_idx =3D mac_tbl_idx; + } + + ret =3D icssm_prueth_sw_fdb_find_bucket_insert_point(fdb, + bucket_info, mac, + emac->port_id - 1); + if (ret < 0) + /* mac is already in fdb table */ + return 0; + + mac_tbl_idx =3D ret; + + icssm_prueth_sw_fdb_spin_lock(fdb); + + mac_info =3D icssm_prueth_sw_get_empty_mac_tbl_entry(prueth, bucket_info, + mac_tbl_idx, NULL, + mac); + if (!mac_info) { + /* Should not happen */ + dev_warn(prueth->dev, "OUT of FDB MEM\n"); + return -ENOMEM; + } + + icssm_mac_copy(mac_info->mac, mac); + mac_info->active =3D 1; + mac_info->age =3D 0; + mac_info->port =3D emac->port_id - 1; + mac_info->is_static =3D is_static; + + bucket_info->bucket_entries++; + fdb->total_entries++; + + icssm_prueth_sw_fdb_spin_unlock(fdb); + + dev_dbg(prueth->dev, "added fdb: %pM port=3D%d total_entries=3D%u\n", + mac, emac->port_id, fdb->total_entries); + + return 0; +} + +static int icssm_prueth_sw_delete_fdb_entry(struct prueth_emac *emac, + const u8 *mac, u8 is_static) +{ + struct fdb_index_tbl_entry_t *bucket_info; + struct fdb_mac_tbl_entry_t *mac_info; + struct fdb_mac_tbl_array_t *mt; + u8 hash_val, mac_tbl_idx; + struct prueth *prueth; + s16 ret, left, right; + struct fdb_tbl *fdb; + + prueth =3D emac->prueth; + fdb =3D prueth->fdb_tbl; + mt =3D fdb->mac_tbl_a; + + if (fdb->total_entries =3D=3D 0) + return 0; + + /* Get the bucket that the mac belongs to */ + hash_val =3D icssm_prueth_sw_fdb_hash(mac); + bucket_info =3D FDB_IDX_TBL_ENTRY(hash_val); + + ret =3D icssm_prueth_sw_fdb_search(mt, bucket_info, mac); + if (ret < 0) + return ret; + + mac_tbl_idx =3D ret; + mac_info =3D FDB_MAC_TBL_ENTRY(mac_tbl_idx); + + icssm_prueth_sw_fdb_spin_lock(fdb); + + /* Shift all elements in bucket to the left. No need to + * update index table since only shifting within bucket. + */ + left =3D mac_tbl_idx; + right =3D bucket_info->bucket_idx + bucket_info->bucket_entries - 1; + icssm_prueth_sw_fdb_move_range_left(prueth, left, right); + + /* Remove end of bucket from table */ + mac_info =3D FDB_MAC_TBL_ENTRY(right); + mac_info->active =3D 0; + bucket_info->bucket_entries--; + fdb->total_entries--; + + icssm_prueth_sw_fdb_spin_unlock(fdb); + + dev_dbg(prueth->dev, "del fdb: %pM total_entries=3D%u\n", + mac, fdb->total_entries); + + return 0; +} + +int icssm_prueth_sw_do_purge_fdb(struct prueth_emac *emac) +{ + struct fdb_index_tbl_entry_t *bucket_info; + struct prueth *prueth =3D emac->prueth; + struct fdb_tbl *fdb; + u8 hash_val; + s16 i; + + fdb =3D prueth->fdb_tbl; + if (fdb->total_entries =3D=3D 0) + return 0; + + icssm_prueth_sw_fdb_spin_lock(fdb); + + for (i =3D 0; i < FDB_MAC_TBL_MAX_ENTRIES; i++) { + if (fdb->mac_tbl_a->mac_tbl_entry[i].active) { + if (!fdb->mac_tbl_a->mac_tbl_entry[i].is_static) { + /* Get the bucket that the mac belongs to */ + hash_val =3D icssm_prueth_sw_fdb_hash + (FDB_MAC_TBL_ENTRY(i)->mac); + bucket_info =3D FDB_IDX_TBL_ENTRY(hash_val); + fdb->mac_tbl_a->mac_tbl_entry[i].active =3D 0; + bucket_info->bucket_entries--; + fdb->total_entries--; + } + } + } + + icssm_prueth_sw_fdb_spin_unlock(fdb); + return 0; +} + +int icssm_prueth_sw_init_fdb_table(struct prueth *prueth) +{ + if (prueth->emac_configured) + return 0; + + prueth->fdb_tbl =3D kmalloc(sizeof(*prueth->fdb_tbl), GFP_KERNEL); + if (!prueth->fdb_tbl) + return -ENOMEM; + + icssm_prueth_sw_fdb_tbl_init(prueth); + + return 0; +} + +/** + * icssm_prueth_sw_fdb_add - insert fdb entry + * + * @emac: EMAC data structure + * @fdb: fdb info + * + */ +void icssm_prueth_sw_fdb_add(struct prueth_emac *emac, + struct switchdev_notifier_fdb_info *fdb) +{ + icssm_prueth_sw_insert_fdb_entry(emac, fdb->addr, 1); +} + +/** + * icssm_prueth_sw_fdb_del - delete fdb entry + * + * @emac: EMAC data structure + * @fdb: fdb info + * + */ +void icssm_prueth_sw_fdb_del(struct prueth_emac *emac, + struct switchdev_notifier_fdb_info *fdb) +{ + icssm_prueth_sw_delete_fdb_entry(emac, fdb->addr, 1); +} diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth_switch.h b/drivers/= net/ethernet/ti/icssm/icssm_prueth_switch.h new file mode 100644 index 000000000000..fd013ecdc707 --- /dev/null +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth_switch.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti= .com + */ + +#ifndef __NET_TI_PRUETH_SWITCH_H +#define __NET_TI_PRUETH_SWITCH_H + +#include "icssm_prueth.h" +#include "icssm_prueth_fdb_tbl.h" + +void icssm_prueth_sw_fdb_tbl_init(struct prueth *prueth); +int icssm_prueth_sw_init_fdb_table(struct prueth *prueth); +void icssm_prueth_sw_free_fdb_table(struct prueth *prueth); +int icssm_prueth_sw_do_purge_fdb(struct prueth_emac *emac); +void icssm_prueth_sw_fdb_add(struct prueth_emac *emac, + struct switchdev_notifier_fdb_info *fdb); +void icssm_prueth_sw_fdb_del(struct prueth_emac *emac, + struct switchdev_notifier_fdb_info *fdb); + +#endif /* __NET_TI_PRUETH_SWITCH_H */ diff --git a/drivers/net/ethernet/ti/icssm/icssm_switch.h b/drivers/net/eth= ernet/ti/icssm/icssm_switch.h index 8b494ffdcde7..44b8ae06df9c 100644 --- a/drivers/net/ethernet/ti/icssm/icssm_switch.h +++ b/drivers/net/ethernet/ti/icssm/icssm_switch.h @@ -254,4 +254,9 @@ #define P0_COL_BUFFER_OFFSET 0xEE00 #define P0_Q1_BUFFER_OFFSET 0x0000 =20 +#define V2_1_FDB_TBL_LOC PRUETH_MEM_SHARED_RAM +#define V2_1_FDB_TBL_OFFSET 0x2000 + +#define FDB_INDEX_TBL_MAX_ENTRIES 256 +#define FDB_MAC_TBL_MAX_ENTRIES 256 #endif /* __ICSS_SWITCH_H */ --=20 2.43.0 From nobody Thu Oct 2 00:54:03 2025 Received: from server.couthit.com (server.couthit.com [162.240.164.96]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3C9E429A30E; Thu, 25 Sep 2025 14:14:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=162.240.164.96 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758809642; cv=none; b=PtFapLq8sApPLymwRcRbO6kz74e5Ah4z4qkYeNyMw+Bm5KoNgF3IMbZen8JgyiQ1yR1bdaAPct5jwZD9WXg4d3jLWBO0/ZkSNpLm3Vv+CnbhxqKKZz2vOFeKwUsKwTrsjpLitgxX/94z84KKedjVELZePAAfJc04v+io5U4UR9s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758809642; c=relaxed/simple; bh=l36hrQaaYTEIGwZ5IwJuOYz4+Vzmp4J/Y0SFkHoxyXk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=fod51FzJcbIl24Vcx27K0gFdHKmJ3ue0xUfnmBveHpPKLFmTsAeSVT+5IxuwVLNk33rNGTVm9rQVMy3BTBvlA7cZFUOQ8vdC5qWR/7A1G6RdMb1ypTdx5P/yj+wItzK/TqxcDBWPKtv+WvbVzBurFEouDD3nQgAHGWzjsROIpow= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=couthit.com; spf=pass smtp.mailfrom=couthit.com; dkim=pass (2048-bit key) header.d=couthit.com header.i=@couthit.com header.b=e34kTb8i; arc=none smtp.client-ip=162.240.164.96 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=couthit.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=couthit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=couthit.com header.i=@couthit.com header.b="e34kTb8i" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=couthit.com ; s=default; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=5crgUMlCCbsSimhYiGfTmnMOTiSDqjkT6qKhE8um01g=; b=e34kTb8i0MyG7p4Rp0pNeTYa8F nFZOlr7TSaVm1KgxcFJMApK9C7cZ4eEeTcIWMfGHWzhwjGhqgXds05uZv94XGQ5F4Apsl8rn/VhdJ jlUyUEbE2rBROrAuWLrSi+jBRHfWZnTOWMud69I3Hm1GximPhSU3W8WLMuFne797O3NJxcZF8YVbb qWrEPoeYSETmX3+kPgOoANeJ/Ph7FBR7MBeB+/atp1ayG3HZkrWOEbRKdz+abl2Vo1EldMvL7lwgS Jvha5uCKDI3ZjOwc8RGk3S7/blFstnRgcZTjqgS88DpFzbjRvwLYgEWWIfh+iVtaPdWor+0XiTpEN sMq0Lpeg==; Received: from [122.175.9.182] (port=9537 helo=cypher.couthit.local) by server.couthit.com with esmtpa (Exim 4.98.1) (envelope-from ) id 1v1mjN-00000005fRb-3zSp; Thu, 25 Sep 2025 10:13:58 -0400 From: Parvathi Pudi To: andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, danishanwar@ti.com, parvathi@couthit.com, rogerq@kernel.org, pmohan@couthit.com, basharath@couthit.com, afd@ti.com Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org, linux-arm-kernel@lists.infradead.org, pratheesh@ti.com, prajith@ti.com, vigneshr@ti.com, praneeth@ti.com, srk@ti.com, rogerq@ti.com, krishna@couthit.com, mohan@couthit.com Subject: [PATCH net-next 2/3] net: ti: icssm-prueth: Adds switchdev support for icssm_prueth driver Date: Thu, 25 Sep 2025 19:32:11 +0530 Message-ID: <20250925141246.3433603-3-parvathi@couthit.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250925141246.3433603-1-parvathi@couthit.com> References: <20250925141246.3433603-1-parvathi@couthit.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-AntiAbuse: This header was added to track abuse, please include it with any abuse report X-AntiAbuse: Primary Hostname - server.couthit.com X-AntiAbuse: Original Domain - vger.kernel.org X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] X-AntiAbuse: Sender Address Domain - couthit.com X-Get-Message-Sender-Via: server.couthit.com: authenticated_id: parvathi@couthit.com X-Authenticated-Sender: server.couthit.com: parvathi@couthit.com X-Source: X-Source-Args: X-Source-Dir: Content-Type: text/plain; charset="utf-8" From: Roger Quadros This patch adds support for offloading the RSTP switch feature to the PRU-ICSS subsystem by adding switchdev support. PRU-ICSS is capable of operating in RSTP switch mode with two external ports and one host port. PRUETH driver and firmware interface support will be added into icssm_prueth in the subsequent commits. Reviewed-by: Mohan Reddy Putluru Signed-off-by: Roger Quadros Signed-off-by: Andrew F. Davis Signed-off-by: Basharath Hussain Khaja Signed-off-by: Parvathi Pudi --- drivers/net/ethernet/ti/Makefile | 2 +- drivers/net/ethernet/ti/icssm/icssm_prueth.c | 199 +++++++++++ drivers/net/ethernet/ti/icssm/icssm_prueth.h | 18 +- .../ethernet/ti/icssm/icssm_prueth_switch.c | 78 ++++ .../ethernet/ti/icssm/icssm_prueth_switch.h | 11 +- .../net/ethernet/ti/icssm/icssm_switchdev.c | 332 ++++++++++++++++++ .../net/ethernet/ti/icssm/icssm_switchdev.h | 13 + .../ti/icssm/icssm_vlan_mcast_filter_mmap.h | 120 +++++++ 8 files changed, 770 insertions(+), 3 deletions(-) create mode 100644 drivers/net/ethernet/ti/icssm/icssm_switchdev.c create mode 100644 drivers/net/ethernet/ti/icssm/icssm_switchdev.h create mode 100644 drivers/net/ethernet/ti/icssm/icssm_vlan_mcast_filter_m= map.h diff --git a/drivers/net/ethernet/ti/Makefile b/drivers/net/ethernet/ti/Mak= efile index 1fd149dd6115..6da50f4b7c2e 100644 --- a/drivers/net/ethernet/ti/Makefile +++ b/drivers/net/ethernet/ti/Makefile @@ -4,7 +4,7 @@ # =20 obj-$(CONFIG_TI_PRUETH) +=3D icssm-prueth.o -icssm-prueth-y :=3D icssm/icssm_prueth.o icssm/icssm_prueth_switch.o +icssm-prueth-y :=3D icssm/icssm_prueth.o icssm/icssm_prueth_switch.o icssm= /icssm_switchdev.o =20 obj-$(CONFIG_TI_CPSW) +=3D cpsw-common.o obj-$(CONFIG_TI_DAVINCI_EMAC) +=3D cpsw-common.o diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth.c b/drivers/net/eth= ernet/ti/icssm/icssm_prueth.c index 293b7af04263..9bcece1b09fc 100644 --- a/drivers/net/ethernet/ti/icssm/icssm_prueth.c +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth.c @@ -29,6 +29,8 @@ #include =20 #include "icssm_prueth.h" +#include "icssm_prueth_switch.h" +#include "icssm_vlan_mcast_filter_mmap.h" #include "../icssg/icssg_mii_rt.h" #include "../icssg/icss_iep.h" =20 @@ -38,6 +40,21 @@ #define TX_CLK_DELAY_100M 0x6 #define HR_TIMER_TX_DELAY_US 100 =20 +static struct prueth_fw_offsets fw_offsets_v2_1; + +static void icssm_prueth_set_fw_offsets(struct prueth *prueth) +{ + /* Set VLAN/Multicast filter control and table offsets */ + if (PRUETH_IS_EMAC(prueth)) { + prueth->fw_offsets->mc_ctrl_byte =3D + ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_OFFSET; + prueth->fw_offsets->mc_filter_mask =3D + ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OFFSET; + prueth->fw_offsets->mc_filter_tbl =3D + ICSS_EMAC_FW_MULTICAST_FILTER_TABLE; + } +} + static void icssm_prueth_write_reg(struct prueth *prueth, enum prueth_mem region, unsigned int reg, u32 val) @@ -303,6 +320,7 @@ static void icssm_prueth_hostinit(struct prueth *prueth) */ static void icssm_prueth_init_ethernet_mode(struct prueth *prueth) { + icssm_prueth_set_fw_offsets(prueth); icssm_prueth_hostinit(prueth); } =20 @@ -1131,11 +1149,180 @@ static void icssm_emac_ndo_get_stats64(struct net_= device *ndev, stats->rx_length_errors =3D emac->stats.rx_length_errors; } =20 +/* enable/disable MC filter */ +static void icssm_emac_mc_filter_ctrl(struct prueth_emac *emac, bool enabl= e) +{ + struct prueth *prueth =3D emac->prueth; + void __iomem *mc_filter_ctrl; + void __iomem *ram; + u32 mc_ctrl_byte; + u32 reg; + + ram =3D prueth->mem[emac->dram].va; + mc_ctrl_byte =3D prueth->fw_offsets->mc_ctrl_byte; + mc_filter_ctrl =3D ram + mc_ctrl_byte; + + if (enable) + reg =3D ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_ENABLED; + else + reg =3D ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_DISABLED; + + writeb(reg, mc_filter_ctrl); +} + +/* reset MC filter bins */ +static void icssm_emac_mc_filter_reset(struct prueth_emac *emac) +{ + struct prueth *prueth =3D emac->prueth; + void __iomem *mc_filter_tbl; + u32 mc_filter_tbl_base; + void __iomem *ram; + + ram =3D prueth->mem[emac->dram].va; + mc_filter_tbl_base =3D prueth->fw_offsets->mc_filter_tbl; + + mc_filter_tbl =3D ram + mc_filter_tbl_base; + memset_io(mc_filter_tbl, 0, ICSS_EMAC_FW_MULTICAST_TABLE_SIZE_BYTES); +} + +/* set MC filter hashmask */ +static void icssm_emac_mc_filter_hashmask + (struct prueth_emac *emac, + u8 mask[ICSS_EMAC_FW_MULTICAST_FILTER_MASK_SIZE_BYTES]) +{ + struct prueth *prueth =3D emac->prueth; + void __iomem *mc_filter_mask; + u32 mc_filter_mask_base; + void __iomem *ram; + + ram =3D prueth->mem[emac->dram].va; + mc_filter_mask_base =3D prueth->fw_offsets->mc_filter_mask; + + mc_filter_mask =3D ram + mc_filter_mask_base; + memcpy_toio(mc_filter_mask, mask, + ICSS_EMAC_FW_MULTICAST_FILTER_MASK_SIZE_BYTES); +} + +static void icssm_emac_mc_filter_bin_update(struct prueth_emac *emac, u8 h= ash, + u8 val) +{ + struct prueth *prueth =3D emac->prueth; + void __iomem *mc_filter_tbl; + u32 mc_filter_tbl_base; + void __iomem *ram; + + ram =3D prueth->mem[emac->dram].va; + mc_filter_tbl_base =3D prueth->fw_offsets->mc_filter_tbl; + + mc_filter_tbl =3D ram + mc_filter_tbl_base; + writeb(val, mc_filter_tbl + hash); +} + +void icssm_emac_mc_filter_bin_allow(struct prueth_emac *emac, u8 hash) +{ + icssm_emac_mc_filter_bin_update + (emac, hash, + ICSS_EMAC_FW_MULTICAST_FILTER_HOST_RCV_ALLOWED); +} + +void icssm_emac_mc_filter_bin_disallow(struct prueth_emac *emac, u8 hash) +{ + icssm_emac_mc_filter_bin_update + (emac, hash, + ICSS_EMAC_FW_MULTICAST_FILTER_HOST_RCV_NOT_ALLOWED); +} + +u8 icssm_emac_get_mc_hash(u8 *mac, u8 *mask) +{ + u8 hash; + int j; + + for (j =3D 0, hash =3D 0; j < ETH_ALEN; j++) + hash ^=3D (mac[j] & mask[j]); + + return hash; +} + +/** + * icssm_emac_ndo_set_rx_mode - EMAC set receive mode function + * @ndev: The EMAC network adapter + * + * Called when system wants to set the receive mode of the device. + * + */ +static void icssm_emac_ndo_set_rx_mode(struct net_device *ndev) +{ + struct prueth_emac *emac =3D netdev_priv(ndev); + bool promisc =3D ndev->flags & IFF_PROMISC; + struct netdev_hw_addr *ha; + struct prueth *prueth; + unsigned long flags; + void __iomem *sram; + u32 mask, reg; + u8 hash; + + prueth =3D emac->prueth; + sram =3D prueth->mem[PRUETH_MEM_SHARED_RAM].va; + reg =3D readl(sram + EMAC_PROMISCUOUS_MODE_OFFSET); + + /* It is a shared table. So lock the access */ + spin_lock_irqsave(&emac->addr_lock, flags); + + /* Disable and reset multicast filter, allows allmulti */ + icssm_emac_mc_filter_ctrl(emac, false); + icssm_emac_mc_filter_reset(emac); + icssm_emac_mc_filter_hashmask(emac, emac->mc_filter_mask); + + if (PRUETH_IS_EMAC(prueth)) { + switch (emac->port_id) { + case PRUETH_PORT_MII0: + mask =3D EMAC_P1_PROMISCUOUS_BIT; + break; + case PRUETH_PORT_MII1: + mask =3D EMAC_P2_PROMISCUOUS_BIT; + break; + default: + netdev_err(ndev, "%s: invalid port\n", __func__); + goto unlock; + } + + if (promisc) { + /* Enable promiscuous mode */ + reg |=3D mask; + } else { + /* Disable promiscuous mode */ + reg &=3D ~mask; + } + + writel(reg, sram + EMAC_PROMISCUOUS_MODE_OFFSET); + + if (promisc) + goto unlock; + } + + if (ndev->flags & IFF_ALLMULTI && !PRUETH_IS_SWITCH(prueth)) + goto unlock; + + icssm_emac_mc_filter_ctrl(emac, true); /* all multicast blocked */ + + if (netdev_mc_empty(ndev)) + goto unlock; + + netdev_for_each_mc_addr(ha, ndev) { + hash =3D icssm_emac_get_mc_hash(ha->addr, emac->mc_filter_mask); + icssm_emac_mc_filter_bin_allow(emac, hash); + } + +unlock: + spin_unlock_irqrestore(&emac->addr_lock, flags); +} + static const struct net_device_ops emac_netdev_ops =3D { .ndo_open =3D icssm_emac_ndo_open, .ndo_stop =3D icssm_emac_ndo_stop, .ndo_start_xmit =3D icssm_emac_ndo_start_xmit, .ndo_get_stats64 =3D icssm_emac_ndo_get_stats64, + .ndo_set_rx_mode =3D icssm_emac_ndo_set_rx_mode, }; =20 /* get emac_port corresponding to eth_node name */ @@ -1310,6 +1497,17 @@ static void icssm_prueth_netdev_exit(struct prueth *= prueth, prueth->emac[mac] =3D NULL; } =20 +bool icssm_prueth_sw_port_dev_check(const struct net_device *ndev) +{ + if (ndev->netdev_ops !=3D &emac_netdev_ops) + return false; + + if (ndev->features & NETIF_F_HW_L2FW_DOFFLOAD) + return true; + + return false; +} + static int icssm_prueth_probe(struct platform_device *pdev) { struct device_node *eth0_node =3D NULL, *eth1_node =3D NULL; @@ -1332,6 +1530,7 @@ static int icssm_prueth_probe(struct platform_device = *pdev) platform_set_drvdata(pdev, prueth); prueth->dev =3D dev; prueth->fw_data =3D device_get_match_data(dev); + prueth->fw_offsets =3D &fw_offsets_v2_1; =20 eth_ports_node =3D of_get_child_by_name(np, "ethernet-ports"); if (!eth_ports_node) diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth.h b/drivers/net/eth= ernet/ti/icssm/icssm_prueth.h index 4b50133c5a72..c98ed2cd76e7 100644 --- a/drivers/net/ethernet/ti/icssm/icssm_prueth.h +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth.h @@ -178,6 +178,13 @@ enum pruss_device { PRUSS_K2G }; =20 +/* Firmware offsets/size information */ +struct prueth_fw_offsets { + u32 mc_ctrl_byte; + u32 mc_filter_mask; + u32 mc_filter_tbl; +}; + /** * struct prueth_private_data - PRU Ethernet private data * @driver_data: PRU Ethernet device name @@ -222,12 +229,14 @@ struct prueth_emac { const char *phy_id; u32 msg_enable; u8 mac_addr[6]; + unsigned char mc_filter_mask[ETH_ALEN]; /* for multicast filtering */ phy_interface_t phy_if; =20 /* spin lock used to protect * during link configuration */ spinlock_t lock; + spinlock_t addr_lock; /* serialize access to VLAN/MC filter table */ =20 struct hrtimer tx_hrtimer; struct prueth_emac_stats stats; @@ -249,8 +258,13 @@ struct prueth { struct prueth_emac *emac[PRUETH_NUM_MACS]; struct net_device *registered_netdevs[PRUETH_NUM_MACS]; =20 + struct net_device *hw_bridge_dev; struct fdb_tbl *fdb_tbl; =20 + struct notifier_block prueth_netdevice_nb; + struct notifier_block prueth_switchdev_nb; + struct notifier_block prueth_switchdev_bl_nb; + unsigned int eth_type; size_t ocmc_ram_size; u8 emac_configured; @@ -261,5 +275,7 @@ void icssm_parse_packet_info(struct prueth *prueth, u32= buffer_descriptor, int icssm_emac_rx_packet(struct prueth_emac *emac, u16 *bd_rd_ptr, struct prueth_packet_info *pkt_info, const struct prueth_queue_info *rxqueue); - +void icssm_emac_mc_filter_bin_allow(struct prueth_emac *emac, u8 hash); +void icssm_emac_mc_filter_bin_disallow(struct prueth_emac *emac, u8 hash); +u8 icssm_emac_get_mc_hash(u8 *mac, u8 *mask); #endif /* __NET_TI_PRUETH_H */ diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth_switch.c b/drivers/= net/ethernet/ti/icssm/icssm_prueth_switch.c index edde05a3f050..5e11a8ced402 100644 --- a/drivers/net/ethernet/ti/icssm/icssm_prueth_switch.c +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth_switch.c @@ -15,6 +15,17 @@ =20 #define FDB_MAC_TBL_ENTRY(n) (&prueth->fdb_tbl->mac_tbl_a->mac_tbl_entry[n= ]) =20 +#define FDB_LEARN 1 +#define FDB_DELETE 2 +#define FDB_PURGE 3 + +struct icssm_prueth_sw_fdb_work { + struct work_struct work; + struct prueth_emac *emac; + u8 addr[ETH_ALEN]; + int event; +}; + void icssm_prueth_sw_free_fdb_table(struct prueth *prueth) { if (prueth->emac_configured) @@ -534,3 +545,70 @@ void icssm_prueth_sw_fdb_del(struct prueth_emac *emac, { icssm_prueth_sw_delete_fdb_entry(emac, fdb->addr, 1); } + +static void icssm_prueth_sw_fdb_work(struct work_struct *work) +{ + struct icssm_prueth_sw_fdb_work *fdb_work =3D + container_of(work, struct icssm_prueth_sw_fdb_work, work); + struct prueth_emac *emac =3D fdb_work->emac; + + rtnl_lock(); + + /* Interface is not up */ + if (!emac->prueth->fdb_tbl) { + rtnl_unlock(); + return; + } + + switch (fdb_work->event) { + case FDB_LEARN: + icssm_prueth_sw_insert_fdb_entry(emac, fdb_work->addr, 0); + break; + case FDB_PURGE: + icssm_prueth_sw_do_purge_fdb(emac); + break; + default: + break; + } + rtnl_unlock(); + + kfree(fdb_work); + dev_put(emac->ndev); +} + +int icssm_prueth_sw_learn_fdb(struct prueth_emac *emac, u8 *src_mac) +{ + struct icssm_prueth_sw_fdb_work *fdb_work; + + fdb_work =3D kzalloc(sizeof(*fdb_work), GFP_ATOMIC); + if (WARN_ON(!fdb_work)) + return -ENOMEM; + + INIT_WORK(&fdb_work->work, icssm_prueth_sw_fdb_work); + + fdb_work->event =3D FDB_LEARN; + fdb_work->emac =3D emac; + ether_addr_copy(fdb_work->addr, src_mac); + + dev_hold(emac->ndev); + queue_work(system_long_wq, &fdb_work->work); + return 0; +} + +int icssm_prueth_sw_purge_fdb(struct prueth_emac *emac) +{ + struct icssm_prueth_sw_fdb_work *fdb_work; + + fdb_work =3D kzalloc(sizeof(*fdb_work), GFP_ATOMIC); + if (WARN_ON(!fdb_work)) + return -ENOMEM; + + INIT_WORK(&fdb_work->work, icssm_prueth_sw_fdb_work); + + fdb_work->event =3D FDB_PURGE; + fdb_work->emac =3D emac; + + dev_hold(emac->ndev); + queue_work(system_long_wq, &fdb_work->work); + return 0; +} diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth_switch.h b/drivers/= net/ethernet/ti/icssm/icssm_prueth_switch.h index fd013ecdc707..d1a092b173a5 100644 --- a/drivers/net/ethernet/ti/icssm/icssm_prueth_switch.h +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth_switch.h @@ -5,8 +5,16 @@ #ifndef __NET_TI_PRUETH_SWITCH_H #define __NET_TI_PRUETH_SWITCH_H =20 +#include + #include "icssm_prueth.h" #include "icssm_prueth_fdb_tbl.h" +#include "icssm_switchdev.h" + +void icssm_prueth_sw_port_set_stp_state(struct prueth *prueth, + enum prueth_port port, u8 state); +u8 icssm_prueth_sw_port_get_stp_state(struct prueth *prueth, + enum prueth_port port); =20 void icssm_prueth_sw_fdb_tbl_init(struct prueth *prueth); int icssm_prueth_sw_init_fdb_table(struct prueth *prueth); @@ -16,5 +24,6 @@ void icssm_prueth_sw_fdb_add(struct prueth_emac *emac, struct switchdev_notifier_fdb_info *fdb); void icssm_prueth_sw_fdb_del(struct prueth_emac *emac, struct switchdev_notifier_fdb_info *fdb); - +int icssm_prueth_sw_learn_fdb(struct prueth_emac *emac, u8 *src_mac); +int icssm_prueth_sw_purge_fdb(struct prueth_emac *emac); #endif /* __NET_TI_PRUETH_SWITCH_H */ diff --git a/drivers/net/ethernet/ti/icssm/icssm_switchdev.c b/drivers/net/= ethernet/ti/icssm/icssm_switchdev.c new file mode 100644 index 000000000000..57b83d790ebb --- /dev/null +++ b/drivers/net/ethernet/ti/icssm/icssm_switchdev.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* Texas Instruments ICSSM Ethernet Driver + * + * Copyright (C) 2018-2022 Texas Instruments Incorporated - https://www.ti= .com/ + * + */ + +#include +#include +#include +#include + +#include "icssm_prueth.h" +#include "icssm_prueth_switch.h" +#include "icssm_prueth_fdb_tbl.h" + +/* switchev event work */ +struct icssm_prueth_sw_switchdev_event_work { + struct work_struct work; + struct switchdev_notifier_fdb_info fdb_info; + struct prueth_emac *emac; + unsigned long event; +}; + +void icssm_prueth_sw_port_set_stp_state(struct prueth *prueth, + enum prueth_port port, u8 state) +{ + struct fdb_tbl *t =3D prueth->fdb_tbl; + + writeb(state, port - 1 ? (void __iomem *)&t->port2_stp_cfg->state : + (void __iomem *)&t->port1_stp_cfg->state); +} + +u8 icssm_prueth_sw_port_get_stp_state(struct prueth *prueth, + enum prueth_port port) +{ + struct fdb_tbl *t =3D prueth->fdb_tbl; + u8 state; + + state =3D readb(port - 1 ? (void __iomem *)&t->port2_stp_cfg->state : + (void __iomem *)&t->port1_stp_cfg->state); + return state; +} + +static int icssm_prueth_switchdev_attr_set(struct net_device *ndev, + const void *ctx, + const struct switchdev_attr *attr, + struct netlink_ext_ack *extack) +{ + struct prueth_emac *emac =3D netdev_priv(ndev); + struct prueth *prueth =3D emac->prueth; + int err =3D 0; + u8 o_state; + + /* Interface is not up */ + if (!prueth->fdb_tbl) + return 0; + + switch (attr->id) { + case SWITCHDEV_ATTR_ID_PORT_STP_STATE: + o_state =3D icssm_prueth_sw_port_get_stp_state(prueth, + emac->port_id); + icssm_prueth_sw_port_set_stp_state(prueth, emac->port_id, + attr->u.stp_state); + + if (o_state !=3D attr->u.stp_state) + icssm_prueth_sw_purge_fdb(emac); + + dev_dbg(prueth->dev, "attr set: stp state:%u port:%u\n", + attr->u.stp_state, emac->port_id); + break; + default: + err =3D -EOPNOTSUPP; + break; + } + + return err; +} + +static void +icssm_prueth_sw_fdb_offload_notify(struct net_device *ndev, + struct switchdev_notifier_fdb_info *rcv) +{ + struct switchdev_notifier_fdb_info info; + + info.addr =3D rcv->addr; + info.vid =3D rcv->vid; + call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, ndev, &info.info, + NULL); +} + +/** + * icssm_prueth_sw_switchdev_event_work - insert/delete fdb entry + * + * @work: work structure + * + */ +static void icssm_prueth_sw_switchdev_event_work(struct work_struct *work) +{ + struct icssm_prueth_sw_switchdev_event_work *switchdev_work =3D + container_of(work, + struct icssm_prueth_sw_switchdev_event_work, work); + struct prueth_emac *emac =3D switchdev_work->emac; + struct switchdev_notifier_fdb_info *fdb; + struct prueth *prueth =3D emac->prueth; + int port =3D emac->port_id; + + rtnl_lock(); + + /* Interface is not up */ + if (!emac->prueth->fdb_tbl) { + rtnl_unlock(); + return; + } + + switch (switchdev_work->event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: + fdb =3D &switchdev_work->fdb_info; + dev_dbg(prueth->dev, + "prueth fdb add: MACID =3D %pM vid =3D %u flags =3D %u -- port %d\n", + fdb->addr, fdb->vid, fdb->added_by_user, port); + + if (!fdb->added_by_user) + break; + + if (fdb->is_local) + break; + + icssm_prueth_sw_fdb_add(emac, fdb); + icssm_prueth_sw_fdb_offload_notify(emac->ndev, fdb); + break; + case SWITCHDEV_FDB_DEL_TO_DEVICE: + fdb =3D &switchdev_work->fdb_info; + dev_dbg(prueth->dev, + "prueth fdb del: MACID =3D %pM vid =3D %u flags =3D %u -- port %d\n", + fdb->addr, fdb->vid, fdb->added_by_user, port); + + if (fdb->is_local) + break; + + icssm_prueth_sw_fdb_del(emac, fdb); + break; + default: + break; + } + rtnl_unlock(); + + kfree(switchdev_work->fdb_info.addr); + kfree(switchdev_work); + dev_put(emac->ndev); +} + +/* called under rcu_read_lock() */ +static int icssm_prueth_sw_switchdev_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *ndev =3D switchdev_notifier_info_to_dev(ptr); + struct icssm_prueth_sw_switchdev_event_work *switchdev_work; + struct switchdev_notifier_fdb_info *fdb_info =3D ptr; + struct prueth_emac *emac =3D netdev_priv(ndev); + int err; + + if (!icssm_prueth_sw_port_dev_check(ndev)) + return NOTIFY_DONE; + + if (event =3D=3D SWITCHDEV_PORT_ATTR_SET) { + err =3D switchdev_handle_port_attr_set + (ndev, ptr, icssm_prueth_sw_port_dev_check, + icssm_prueth_switchdev_attr_set); + return notifier_from_errno(err); + } + + switchdev_work =3D kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); + if (WARN_ON(!switchdev_work)) + return NOTIFY_BAD; + + INIT_WORK(&switchdev_work->work, icssm_prueth_sw_switchdev_event_work); + switchdev_work->emac =3D emac; + switchdev_work->event =3D event; + + switch (event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: + case SWITCHDEV_FDB_DEL_TO_DEVICE: + memcpy(&switchdev_work->fdb_info, ptr, + sizeof(switchdev_work->fdb_info)); + switchdev_work->fdb_info.addr =3D kzalloc(ETH_ALEN, GFP_ATOMIC); + if (!switchdev_work->fdb_info.addr) + goto err_addr_alloc; + ether_addr_copy((u8 *)switchdev_work->fdb_info.addr, + fdb_info->addr); + dev_hold(ndev); + break; + default: + kfree(switchdev_work); + return NOTIFY_DONE; + } + + queue_work(system_long_wq, &switchdev_work->work); + + return NOTIFY_DONE; + +err_addr_alloc: + kfree(switchdev_work); + return NOTIFY_BAD; +} + +static int icssm_prueth_switchdev_obj_add(struct net_device *ndev, + const void *ctx, + const struct switchdev_obj *obj, + struct netlink_ext_ack *extack) +{ + struct switchdev_obj_port_mdb *mdb =3D SWITCHDEV_OBJ_PORT_MDB(obj); + struct prueth_emac *emac =3D netdev_priv(ndev); + struct prueth *prueth =3D emac->prueth; + int ret =3D 0; + u8 hash; + + switch (obj->id) { + case SWITCHDEV_OBJ_ID_HOST_MDB: + dev_dbg(prueth->dev, "MDB add: %s: vid %u:%pM port: %x\n", + ndev->name, mdb->vid, mdb->addr, emac->port_id); + hash =3D icssm_emac_get_mc_hash(mdb->addr, emac->mc_filter_mask); + icssm_emac_mc_filter_bin_allow(emac, hash); + break; + default: + ret =3D -EOPNOTSUPP; + break; + } + + return ret; +} + +static int icssm_prueth_switchdev_obj_del(struct net_device *ndev, + const void *ctx, + const struct switchdev_obj *obj) +{ + struct switchdev_obj_port_mdb *mdb =3D SWITCHDEV_OBJ_PORT_MDB(obj); + struct prueth_emac *emac =3D netdev_priv(ndev); + struct prueth *prueth =3D emac->prueth; + struct netdev_hw_addr *ha; + u8 hash, tmp_hash; + int ret =3D 0; + + switch (obj->id) { + case SWITCHDEV_OBJ_ID_HOST_MDB: + dev_dbg(prueth->dev, "MDB del: %s: vid %u:%pM port: %x\n", + ndev->name, mdb->vid, mdb->addr, emac->port_id); + hash =3D icssm_emac_get_mc_hash(mdb->addr, emac->mc_filter_mask); + netdev_for_each_mc_addr(ha, prueth->hw_bridge_dev) { + tmp_hash =3D icssm_emac_get_mc_hash(ha->addr, + emac->mc_filter_mask); + /* Another MC address is in the bin. Don't disable. */ + if (tmp_hash =3D=3D hash) + return 0; + } + icssm_emac_mc_filter_bin_disallow(emac, hash); + break; + default: + ret =3D -EOPNOTSUPP; + break; + } + + return ret; +} + +/* switchdev notifiers */ +static int +icssm_prueth_sw_switchdev_blocking_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *ndev =3D switchdev_notifier_info_to_dev(ptr); + int err; + + switch (event) { + case SWITCHDEV_PORT_OBJ_ADD: + err =3D switchdev_handle_port_obj_add + (ndev, ptr, icssm_prueth_sw_port_dev_check, + icssm_prueth_switchdev_obj_add); + return notifier_from_errno(err); + + case SWITCHDEV_PORT_OBJ_DEL: + err =3D switchdev_handle_port_obj_del + (ndev, ptr, icssm_prueth_sw_port_dev_check, + icssm_prueth_switchdev_obj_del); + return notifier_from_errno(err); + + case SWITCHDEV_PORT_ATTR_SET: + err =3D switchdev_handle_port_attr_set + (ndev, ptr, icssm_prueth_sw_port_dev_check, + icssm_prueth_switchdev_attr_set); + return notifier_from_errno(err); + + default: + break; + } + + return NOTIFY_DONE; +} + +int icssm_prueth_sw_register_notifiers(struct prueth *prueth) +{ + int ret =3D 0; + + prueth->prueth_switchdev_nb.notifier_call =3D + &icssm_prueth_sw_switchdev_event; + ret =3D register_switchdev_notifier(&prueth->prueth_switchdev_nb); + if (ret) { + dev_err(prueth->dev, + "register switchdev notifier failed ret:%d\n", ret); + return ret; + } + + prueth->prueth_switchdev_bl_nb.notifier_call =3D + &icssm_prueth_sw_switchdev_blocking_event; + ret =3D register_switchdev_blocking_notifier + (&prueth->prueth_switchdev_bl_nb); + if (ret) { + dev_err(prueth->dev, + "register switchdev blocking notifier failed ret:%d\n", + ret); + unregister_switchdev_notifier(&prueth->prueth_switchdev_nb); + } + + return ret; +} + +void icssm_prueth_sw_unregister_notifiers(struct prueth *prueth) +{ + unregister_switchdev_blocking_notifier(&prueth->prueth_switchdev_bl_nb); + unregister_switchdev_notifier(&prueth->prueth_switchdev_nb); +} diff --git a/drivers/net/ethernet/ti/icssm/icssm_switchdev.h b/drivers/net/= ethernet/ti/icssm/icssm_switchdev.h new file mode 100644 index 000000000000..b03a98e3472e --- /dev/null +++ b/drivers/net/ethernet/ti/icssm/icssm_switchdev.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti= .com + */ + +#ifndef __NET_TI_ICSSM_SWITCHDEV_H +#define __NET_TI_ICSSM_SWITCHDEV_H + +#include "icssm_prueth.h" + +int icssm_prueth_sw_register_notifiers(struct prueth *prueth); +void icssm_prueth_sw_unregister_notifiers(struct prueth *prueth); +bool icssm_prueth_sw_port_dev_check(const struct net_device *ndev); +#endif /* __NET_TI_ICSSM_SWITCHDEV_H */ diff --git a/drivers/net/ethernet/ti/icssm/icssm_vlan_mcast_filter_mmap.h b= /drivers/net/ethernet/ti/icssm/icssm_vlan_mcast_filter_mmap.h new file mode 100644 index 000000000000..c177c19a36ef --- /dev/null +++ b/drivers/net/ethernet/ti/icssm/icssm_vlan_mcast_filter_mmap.h @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* Copyright (C) 2015-2021 Texas Instruments Incorporated - https://www.ti= .com + * + * This file contains VLAN/Multicast filtering feature memory map + * + */ + +#ifndef ICSS_VLAN_MULTICAST_FILTER_MM_H +#define ICSS_VLAN_MULTICAST_FILTER_MM_H + +/* VLAN/Multicast filter defines & offsets, + * present on both PRU0 and PRU1 DRAM + */ + +/* Feature enable/disable values for multicast filtering */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_DISABLED 0x00 +#define ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_ENABLED 0x01 + +/* Feature enable/disable values for VLAN filtering */ +#define ICSS_EMAC_FW_VLAN_FILTER_CTRL_DISABLED 0x00 +#define ICSS_EMAC_FW_VLAN_FILTER_CTRL_ENABLED 0x01 + +/* Add/remove multicast mac id for filtering bin */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_HOST_RCV_ALLOWED 0x01 +#define ICSS_EMAC_FW_MULTICAST_FILTER_HOST_RCV_NOT_ALLOWED 0x00 + +/* Default HASH value for the multicast filtering Mask */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_INIT_VAL 0xFF + +/* Size requirements for Multicast filtering feature */ +#define ICSS_EMAC_FW_MULTICAST_TABLE_SIZE_BYTES 256 +#define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_SIZE_BYTES 6 +#define ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_SIZE_BYTES 1 +#define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OVERRIDE_STATUS_SIZE_BYTES 1 +#define ICSS_EMAC_FW_MULTICAST_FILTER_DROP_CNT_SIZE_BYTES 4 + +/* Size requirements for VLAN filtering feature : 4096 bits =3D 512 bytes = */ +#define ICSS_EMAC_FW_VLAN_FILTER_TABLE_SIZE_BYTES 512 +#define ICSS_EMAC_FW_VLAN_FILTER_CTRL_SIZE_BYTES 1 +#define ICSS_EMAC_FW_VLAN_FILTER_DROP_CNT_SIZE_BYTES 4 + +/* Mask override set status */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OVERRIDE_SET 1 +/* Mask override not set status */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OVERRIDE_NOT_SET 0 +/* 6 bytes HASH Mask for the MAC */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OFFSET 0xF4 +/* 0 -> multicast filtering disabled | 1 -> multicast filtering enabled */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_OFFSET \ + (ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OFFSET + \ + ICSS_EMAC_FW_MULTICAST_FILTER_MASK_SIZE_BYTES) +/* Status indicating if the HASH override is done or not: 0: no, 1: yes */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_OVERRIDE_STATUS \ + (ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_OFFSET + \ + ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_SIZE_BYTES) +/* Multicast drop statistics */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_DROP_CNT_OFFSET \ + (ICSS_EMAC_FW_MULTICAST_FILTER_OVERRIDE_STATUS +\ + ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OVERRIDE_STATUS_SIZE_BYTES) +/* Multicast table */ +#define ICSS_EMAC_FW_MULTICAST_FILTER_TABLE \ + (ICSS_EMAC_FW_MULTICAST_FILTER_DROP_CNT_OFFSET +\ + ICSS_EMAC_FW_MULTICAST_FILTER_DROP_CNT_SIZE_BYTES) + +/* Multicast filter defines & offsets for LRE + */ +#define ICSS_LRE_FW_MULTICAST_TABLE_SEARCH_OP_CONTROL_BIT 0xE0 +/* one byte field : + * 0 -> multicast filtering disabled + * 1 -> multicast filtering enabled + */ +#define ICSS_LRE_FW_MULTICAST_FILTER_MASK 0xE4 +#define ICSS_LRE_FW_MULTICAST_FILTER_TABLE 0x100 + +/* VLAN table Offsets */ +#define ICSS_EMAC_FW_VLAN_FLTR_TBL_BASE_ADDR 0x200 +#define ICSS_EMAC_FW_VLAN_FILTER_CTRL_BITMAP_OFFSET 0xEF +#define ICSS_EMAC_FW_VLAN_FILTER_DROP_CNT_OFFSET \ + (ICSS_EMAC_FW_VLAN_FILTER_CTRL_BITMAP_OFFSET + \ + ICSS_EMAC_FW_VLAN_FILTER_CTRL_SIZE_BYTES) + +/* VLAN filter Control Bit maps */ +/* one bit field, bit 0: | 0 : VLAN filter disabled (default), + * 1: VLAN filter enabled + */ +#define ICSS_EMAC_FW_VLAN_FILTER_CTRL_ENABLE_BIT 0 +/* one bit field, bit 1: | 0 : untagged host rcv allowed (default), + * 1: untagged host rcv not allowed + */ +#define ICSS_EMAC_FW_VLAN_FILTER_UNTAG_HOST_RCV_ALLOW_CTRL_BIT 1 +/* one bit field, bit 1: | 0 : priotag host rcv allowed (default), + * 1: priotag host rcv not allowed + */ +#define ICSS_EMAC_FW_VLAN_FILTER_PRIOTAG_HOST_RCV_ALLOW_CTRL_BIT 2 +/* one bit field, bit 1: | 0 : skip sv vlan flow + * :1 : take sv vlan flow (not applicable for dual emac ) + */ +#define ICSS_EMAC_FW_VLAN_FILTER_SV_VLAN_FLOW_HOST_RCV_ALLOW_CTRL_BIT 3 + +/* VLAN IDs */ +#define ICSS_EMAC_FW_VLAN_FILTER_PRIOTAG_VID 0 +#define ICSS_EMAC_FW_VLAN_FILTER_VID_MIN 0x0000 +#define ICSS_EMAC_FW_VLAN_FILTER_VID_MAX 0x0FFF + +/* VLAN Filtering Commands */ +#define ICSS_EMAC_FW_VLAN_FILTER_ADD_VLAN_VID_CMD 0x00 +#define ICSS_EMAC_FW_VLAN_FILTER_REMOVE_VLAN_VID_CMD 0x01 + +/* Switch defines for VLAN/MC filtering */ +/* SRAM + * VLAN filter defines & offsets + */ +#define ICSS_LRE_FW_VLAN_FLTR_CTRL_BYTE 0x1FE +/* one bit field | 0 : VLAN filter disabled + * | 1 : VLAN filter enabled + */ +#define ICSS_LRE_FW_VLAN_FLTR_TBL_BASE_ADDR 0x200 + +#endif /* ICSS_MULTICAST_FILTER_MM_H */ --=20 2.43.0 From nobody Thu Oct 2 00:54:03 2025 Received: from server.couthit.com (server.couthit.com [162.240.164.96]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E0ACB2D3218; Thu, 25 Sep 2025 14:14:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=162.240.164.96 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758809654; cv=none; b=PZ7DjkWZkI3KAgd5ybnS8Ql4VkJh2cscrFe0899j4izbDHzwOjN6YV0ItVnWJOa8vvo/dcFBao7elIatUsRh2yGeSb9EtSgBpUtDU/i5lMLHwLjFOCba6HsxitAAxolkeM36vYxa6L2r+VS2Blszp8Q2xCgL7uGx+PnDSU6GNmY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758809654; c=relaxed/simple; bh=4wyllUebp4zaWD5pOU/r/ZgFsmYyRfYQWlDa/VP4KDY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RVKAEWgHy4rP6O5faahM2zUrDdmW8pCKfgdbcVgJENYCQ2+CauOlxYOMzMWOREvO21g1DdJ3T6CACrVOJusorH2cXlHNViR4WVHgl/zHPnntYVJ9opU1LMG0WNdyr0CVcP1WIvosCkzSg25nmbACMwYlFwqq3e2FoRiQ1lCLb3o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=couthit.com; spf=pass smtp.mailfrom=couthit.com; dkim=pass (2048-bit key) header.d=couthit.com header.i=@couthit.com header.b=ClLFFcC/; arc=none smtp.client-ip=162.240.164.96 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=couthit.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=couthit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=couthit.com header.i=@couthit.com header.b="ClLFFcC/" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=couthit.com ; s=default; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=bk5jk0vsvFtv4x/oPe+wCVd8A4f8uj++aUvoNhyoqoE=; b=ClLFFcC/qtFWtHsjFAR/1zVX+I wLNS8H5zboK8gkKBgAo3bJI3+7BjCNd67qljPqUtkofW2AGE86Ld/bUN9LuG0k7r/INkETtPC78br VgBL90/GKCCrzIbyBei2+0h/X7MuOCFPkZ1TUUJZaKYFAzqaem+yVQ14cvmzMKG3GqI84VQApViD/ SeH3NRY7mHWOB6XNI9GH5fbdsoPF7pBHieC6a+vj+KXVFYP2m2YfRqhw9FUOFwy0bETcpYyE2EsB0 m9qr7leDSSAopi708sWTUqZMwoe8GDofTYGmrF1Gk/xyQOgrBt19lmUnwta1AREZcnqzi2d8wL/zT n2ZcTOLg==; Received: from [122.175.9.182] (port=9537 helo=cypher.couthit.local) by server.couthit.com with esmtpa (Exim 4.98.1) (envelope-from ) id 1v1mjZ-00000005fRb-1pDJ; Thu, 25 Sep 2025 10:14:09 -0400 From: Parvathi Pudi To: andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, danishanwar@ti.com, parvathi@couthit.com, rogerq@kernel.org, pmohan@couthit.com, basharath@couthit.com, afd@ti.com Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org, linux-arm-kernel@lists.infradead.org, pratheesh@ti.com, prajith@ti.com, vigneshr@ti.com, praneeth@ti.com, srk@ti.com, rogerq@ti.com, krishna@couthit.com, mohan@couthit.com Subject: [PATCH net-next 3/3] net: ti: icssm-prueth: Adds support for ICSSM RSTP switch Date: Thu, 25 Sep 2025 19:32:12 +0530 Message-ID: <20250925141246.3433603-4-parvathi@couthit.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250925141246.3433603-1-parvathi@couthit.com> References: <20250925141246.3433603-1-parvathi@couthit.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-AntiAbuse: This header was added to track abuse, please include it with any abuse report X-AntiAbuse: Primary Hostname - server.couthit.com X-AntiAbuse: Original Domain - vger.kernel.org X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] X-AntiAbuse: Sender Address Domain - couthit.com X-Get-Message-Sender-Via: server.couthit.com: authenticated_id: parvathi@couthit.com X-Authenticated-Sender: server.couthit.com: parvathi@couthit.com X-Source: X-Source-Args: X-Source-Dir: Content-Type: text/plain; charset="utf-8" From: Roger Quadros Adds support for RSTP switch mode by enhancing the existing ICSSM dual EMAC driver with switchdev support. With this patch, the PRU-ICSSM is now capable of operating in switch mode with the 2 PRU ports acting as external ports and the host acting as an internal port. Packets received from the PRU ports will be forwarded to the host (store and forward mode) and also to the other PRU port (either using store and forward mode or via cut-through mode). Packets coming from the host will be transmitted either from one or both of the PRU ports (depending on the FDB decision). By default, the dual EMAC firmware will be loaded in the PRU-ICSS subsystem. To configure the PRU-ICSS to operate as a switch, a different firmware must to be loaded. Reviewed-by: Mohan Reddy Putluru Signed-off-by: Roger Quadros Signed-off-by: Andrew F. Davis Signed-off-by: Basharath Hussain Khaja Signed-off-by: Parvathi Pudi --- drivers/net/ethernet/ti/icssm/icssm_prueth.c | 355 +++++++++++++++- drivers/net/ethernet/ti/icssm/icssm_prueth.h | 6 + .../ethernet/ti/icssm/icssm_prueth_switch.c | 385 ++++++++++++++++++ .../ethernet/ti/icssm/icssm_prueth_switch.h | 8 + drivers/net/ethernet/ti/icssm/icssm_switch.h | 77 ++++ 5 files changed, 810 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth.c b/drivers/net/eth= ernet/ti/icssm/icssm_prueth.c index 9bcece1b09fc..3c8befdea1e7 100644 --- a/drivers/net/ethernet/ti/icssm/icssm_prueth.c +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth.c @@ -162,7 +162,7 @@ static const struct prueth_queue_info queue_infos[][NUM= _QUEUES] =3D { }, }; =20 -static const struct prueth_queue_desc queue_descs[][NUM_QUEUES] =3D { +const struct prueth_queue_desc queue_descs[][NUM_QUEUES] =3D { [PRUETH_PORT_QUEUE_HOST] =3D { { .rd_ptr =3D P0_Q1_BD_OFFSET, .wr_ptr =3D P0_Q1_BD_OFFSET, }, { .rd_ptr =3D P0_Q2_BD_OFFSET, .wr_ptr =3D P0_Q2_BD_OFFSET, }, @@ -222,9 +222,9 @@ static void icssm_prueth_hostconfig(struct prueth *prue= th) =20 static void icssm_prueth_mii_init(struct prueth *prueth) { + u32 txcfg_reg, txcfg, txcfg2; struct regmap *mii_rt; u32 rxcfg_reg, rxcfg; - u32 txcfg_reg, txcfg; =20 mii_rt =3D prueth->mii_rt; =20 @@ -252,17 +252,23 @@ static void icssm_prueth_mii_init(struct prueth *prue= th) (TX_START_DELAY << PRUSS_MII_RT_TXCFG_TX_START_DELAY_SHIFT) | (TX_CLK_DELAY_100M << PRUSS_MII_RT_TXCFG_TX_CLK_DELAY_SHIFT); =20 + txcfg2 =3D txcfg; + if (!PRUETH_IS_EMAC(prueth)) + txcfg2 |=3D PRUSS_MII_RT_TXCFG_TX_MUX_SEL; + /* Configuration of Port 0 Tx */ txcfg_reg =3D PRUSS_MII_RT_TXCFG0; =20 - regmap_write(mii_rt, txcfg_reg, txcfg); + regmap_write(mii_rt, txcfg_reg, txcfg2); =20 - txcfg |=3D PRUSS_MII_RT_TXCFG_TX_MUX_SEL; + txcfg2 =3D txcfg; + if (PRUETH_IS_EMAC(prueth)) + txcfg2 |=3D PRUSS_MII_RT_TXCFG_TX_MUX_SEL; =20 /* Configuration of Port 1 Tx */ txcfg_reg =3D PRUSS_MII_RT_TXCFG1; =20 - regmap_write(mii_rt, txcfg_reg, txcfg); + regmap_write(mii_rt, txcfg_reg, txcfg2); =20 txcfg_reg =3D PRUSS_MII_RT_RX_FRMS0; =20 @@ -309,7 +315,10 @@ static void icssm_prueth_hostinit(struct prueth *pruet= h) icssm_prueth_clearmem(prueth, PRUETH_MEM_DRAM1); =20 /* Initialize host queues in shared RAM */ - icssm_prueth_hostconfig(prueth); + if (!PRUETH_IS_EMAC(prueth)) + icssm_prueth_sw_hostconfig(prueth); + else + icssm_prueth_hostconfig(prueth); =20 /* Configure MII_RT */ icssm_prueth_mii_init(prueth); @@ -517,19 +526,24 @@ static int icssm_prueth_tx_enqueue(struct prueth_emac= *emac, struct prueth_queue_desc __iomem *queue_desc; const struct prueth_queue_info *txqueue; struct net_device *ndev =3D emac->ndev; + struct prueth *prueth =3D emac->prueth; unsigned int buffer_desc_count; int free_blocks, update_block; bool buffer_wrapped =3D false; int write_block, read_block; void *src_addr, *dst_addr; int pkt_block_size; + void __iomem *sram; void __iomem *dram; int txport, pktlen; u16 update_wr_ptr; u32 wr_buf_desc; void *ocmc_ram; =20 - dram =3D emac->prueth->mem[emac->dram].va; + if (!PRUETH_IS_EMAC(prueth)) + dram =3D prueth->mem[PRUETH_MEM_DRAM1].va; + else + dram =3D emac->prueth->mem[emac->dram].va; if (eth_skb_pad(skb)) { if (netif_msg_tx_err(emac) && net_ratelimit()) netdev_err(ndev, "packet pad failed\n"); @@ -542,7 +556,10 @@ static int icssm_prueth_tx_enqueue(struct prueth_emac = *emac, pktlen =3D skb->len; /* Get the tx queue */ queue_desc =3D emac->tx_queue_descs + queue_id; - txqueue =3D &queue_infos[txport][queue_id]; + if (!PRUETH_IS_EMAC(prueth)) + txqueue =3D &sw_queue_infos[txport][queue_id]; + else + txqueue =3D &queue_infos[txport][queue_id]; =20 buffer_desc_count =3D icssm_get_buff_desc_count(txqueue); =20 @@ -608,7 +625,11 @@ static int icssm_prueth_tx_enqueue(struct prueth_emac = *emac, /* update first buffer descriptor */ wr_buf_desc =3D (pktlen << PRUETH_BD_LENGTH_SHIFT) & PRUETH_BD_LENGTH_MASK; - writel(wr_buf_desc, dram + readw(&queue_desc->wr_ptr)); + sram =3D prueth->mem[PRUETH_MEM_SHARED_RAM].va; + if (!PRUETH_IS_EMAC(prueth)) + writel(wr_buf_desc, sram + readw(&queue_desc->wr_ptr)); + else + writel(wr_buf_desc, dram + readw(&queue_desc->wr_ptr)); =20 /* update the write pointer in this queue descriptor, the firmware * polls for this change so this will signal the start of transmission @@ -622,7 +643,6 @@ static int icssm_prueth_tx_enqueue(struct prueth_emac *= emac, void icssm_parse_packet_info(struct prueth *prueth, u32 buffer_descriptor, struct prueth_packet_info *pkt_info) { - pkt_info->shadow =3D !!(buffer_descriptor & PRUETH_BD_SHADOW_MASK); pkt_info->port =3D (buffer_descriptor & PRUETH_BD_PORT_MASK) >> PRUETH_BD_PORT_SHIFT; pkt_info->length =3D (buffer_descriptor & PRUETH_BD_LENGTH_MASK) >> @@ -731,11 +751,19 @@ int icssm_emac_rx_packet(struct prueth_emac *emac, u1= 6 *bd_rd_ptr, src_addr +=3D actual_pkt_len; } =20 + if (PRUETH_IS_SWITCH(emac->prueth)) { + skb->offload_fwd_mark =3D emac->offload_fwd_mark; + if (!pkt_info->lookup_success) + icssm_prueth_sw_learn_fdb(emac, skb->data + ETH_ALEN); + } + skb_put(skb, actual_pkt_len); =20 /* send packet up the stack */ skb->protocol =3D eth_type_trans(skb, ndev); + local_bh_disable(); netif_receive_skb(skb); + local_bh_enable(); =20 /* update stats */ emac->stats.rx_bytes +=3D actual_pkt_len; @@ -761,6 +789,7 @@ static int icssm_emac_rx_packets(struct prueth_emac *em= ac, int budget) =20 shared_ram =3D emac->prueth->mem[PRUETH_MEM_SHARED_RAM].va; =20 + /* Start and end queue is made common for EMAC, RSTP */ start_queue =3D emac->rx_queue_start; end_queue =3D emac->rx_queue_end; =20 @@ -771,8 +800,10 @@ static int icssm_emac_rx_packets(struct prueth_emac *e= mac, int budget) /* search host queues for packets */ for (i =3D start_queue; i <=3D end_queue; i++) { queue_desc =3D emac->rx_queue_descs + i; - rxqueue =3D &queue_infos[PRUETH_PORT_HOST][i]; - + if (PRUETH_IS_SWITCH(emac->prueth)) + rxqueue =3D &sw_queue_infos[PRUETH_PORT_HOST][i]; + else + rxqueue =3D &queue_infos[PRUETH_PORT_HOST][i]; overflow_cnt =3D readb(&queue_desc->overflow_cnt); if (overflow_cnt > 0) { emac->stats.rx_over_errors +=3D overflow_cnt; @@ -897,6 +928,13 @@ static int icssm_emac_request_irqs(struct prueth_emac = *emac) return ret; } =20 +/* Function to free memory related to sw */ +static void icssm_prueth_free_memory(struct prueth *prueth) +{ + if (PRUETH_IS_SWITCH(prueth)) + icssm_prueth_sw_free_fdb_table(prueth); +} + static void icssm_ptp_dram_init(struct prueth_emac *emac) { void __iomem *sram =3D emac->prueth->mem[PRUETH_MEM_SHARED_RAM].va; @@ -959,20 +997,38 @@ static int icssm_emac_ndo_open(struct net_device *nde= v) if (!prueth->emac_configured) icssm_prueth_init_ethernet_mode(prueth); =20 - icssm_prueth_emac_config(emac); + /* reset and start PRU firmware */ + if (PRUETH_IS_SWITCH(prueth)) { + ret =3D icssm_prueth_sw_emac_config(emac); + if (ret) + return ret; + + ret =3D icssm_prueth_sw_init_fdb_table(prueth); + if (ret) + return ret; + } else { + icssm_prueth_emac_config(emac); + } =20 if (!prueth->emac_configured) { icssm_ptp_dram_init(emac); ret =3D icss_iep_init(prueth->iep, NULL, NULL, 0); if (ret) { netdev_err(ndev, "Failed to initialize iep: %d\n", ret); - goto iep_exit; + goto free_mem; } } =20 - ret =3D icssm_emac_set_boot_pru(emac, ndev); - if (ret) - goto iep_exit; + if (!PRUETH_IS_EMAC(prueth)) { + ret =3D icssm_prueth_sw_boot_prus(prueth, ndev); + if (ret) + goto iep_exit; + } else { + /* boot the PRU */ + ret =3D icssm_emac_set_boot_pru(emac, ndev); + if (ret) + goto iep_exit; + } =20 ret =3D icssm_emac_request_irqs(emac); if (ret) @@ -987,19 +1043,25 @@ static int icssm_emac_ndo_open(struct net_device *nd= ev) icssm_prueth_port_enable(emac, true); =20 prueth->emac_configured |=3D BIT(emac->port_id); - + if (PRUETH_IS_SWITCH(prueth)) + icssm_prueth_sw_port_set_stp_state(prueth, emac->port_id, + BR_STATE_LEARNING); if (netif_msg_drv(emac)) dev_notice(&ndev->dev, "started\n"); =20 return 0; =20 rproc_shutdown: - rproc_shutdown(emac->pru); + if (!PRUETH_IS_EMAC(prueth)) + icssm_prueth_sw_shutdown_prus(emac, ndev); + else + rproc_shutdown(emac->pru); =20 iep_exit: if (!prueth->emac_configured) icss_iep_exit(prueth->iep); - +free_mem: + icssm_prueth_free_memory(emac->prueth); return ret; } =20 @@ -1028,17 +1090,109 @@ static int icssm_emac_ndo_stop(struct net_device *= ndev) hrtimer_cancel(&emac->tx_hrtimer); =20 /* stop the PRU */ - rproc_shutdown(emac->pru); + if (!PRUETH_IS_EMAC(prueth)) + icssm_prueth_sw_shutdown_prus(emac, ndev); + else + rproc_shutdown(emac->pru); + + /* free table memory of the switch */ + if (PRUETH_IS_SWITCH(emac->prueth)) + icssm_prueth_sw_free_fdb_table(prueth); =20 /* free rx interrupts */ free_irq(emac->rx_irq, ndev); =20 + /* free memory related to sw */ + icssm_prueth_free_memory(emac->prueth); + if (netif_msg_drv(emac)) dev_notice(&ndev->dev, "stopped\n"); =20 return 0; } =20 +static void icssm_prueth_change_to_switch_mode(struct prueth *prueth) +{ + bool portstatus[PRUETH_NUM_MACS]; + struct prueth_emac *emac; + struct net_device *ndev; + int i, ret; + + for (i =3D 0; i < PRUETH_NUM_MACS; i++) { + emac =3D prueth->emac[i]; + ndev =3D emac->ndev; + + portstatus[i] =3D netif_running(ndev); + if (!portstatus[i]) + continue; + + ret =3D ndev->netdev_ops->ndo_stop(ndev); + if (ret < 0) { + netdev_err(ndev, "failed to stop: %d", ret); + return; + } + } + + prueth->eth_type =3D PRUSS_ETHTYPE_SWITCH; + + for (i =3D 0; i < PRUETH_NUM_MACS; i++) { + emac =3D prueth->emac[i]; + ndev =3D emac->ndev; + + if (!portstatus[i]) + continue; + + ret =3D ndev->netdev_ops->ndo_open(ndev); + if (ret < 0) { + netdev_err(ndev, "failed to start: %d", ret); + return; + } + } + + dev_info(prueth->dev, "TI PRU ethernet now in Switch mode\n"); +} + +static void icssm_prueth_change_to_emac_mode(struct prueth *prueth) +{ + bool portstatus[PRUETH_NUM_MACS]; + struct prueth_emac *emac; + struct net_device *ndev; + int i, ret; + + for (i =3D 0; i < PRUETH_NUM_MACS; i++) { + emac =3D prueth->emac[i]; + ndev =3D emac->ndev; + + portstatus[i] =3D netif_running(ndev); + if (!portstatus[i]) + continue; + + ret =3D ndev->netdev_ops->ndo_stop(ndev); + if (ret < 0) { + netdev_err(ndev, "failed to stop: %d", ret); + return; + } + } + + prueth->eth_type =3D PRUSS_ETHTYPE_EMAC; + + for (i =3D 0; i < PRUETH_NUM_MACS; i++) { + emac =3D prueth->emac[i]; + ndev =3D emac->ndev; + + if (!portstatus[i]) + continue; + + ret =3D ndev->netdev_ops->ndo_open(ndev); + if (ret < 0) { + netdev_err(ndev, "failed to start: %d", ret); + return; + } + } + + dev_info(prueth->dev, "TI PRU ethernet now in Dual EMAC mode\n"); +} + /* VLAN-tag PCP to priority queue map for EMAC/Switch/HSR/PRP used by driv= er * Index is PCP val / 2. * low - pcp 0..3 maps to Q4 for Host @@ -1313,6 +1467,15 @@ static void icssm_emac_ndo_set_rx_mode(struct net_de= vice *ndev) icssm_emac_mc_filter_bin_allow(emac, hash); } =20 + /* Add bridge device's MC addresses as well */ + if (prueth->hw_bridge_dev) { + netdev_for_each_mc_addr(ha, prueth->hw_bridge_dev) { + hash =3D icssm_emac_get_mc_hash(ha->addr, + emac->mc_filter_mask); + icssm_emac_mc_filter_bin_allow(emac, hash); + } + } + unlock: spin_unlock_irqrestore(&emac->addr_lock, flags); } @@ -1375,6 +1538,7 @@ static enum hrtimer_restart icssm_emac_tx_timer_callb= ack(struct hrtimer *timer) static int icssm_prueth_netdev_init(struct prueth *prueth, struct device_node *eth_node) { + const struct prueth_private_data *fw_data =3D prueth->fw_data; struct prueth_emac *emac; struct net_device *ndev; enum prueth_port port; @@ -1461,6 +1625,14 @@ static int icssm_prueth_netdev_init(struct prueth *p= rueth, phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_Pause_BIT); phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_Asym_Pause_BIT); =20 + /* Protocol switching + * Enabling L2 Firmware offloading + */ + if (fw_data->support_switch) { + ndev->features |=3D NETIF_F_HW_L2FW_DOFFLOAD; + ndev->hw_features |=3D NETIF_F_HW_L2FW_DOFFLOAD; + } + ndev->dev.of_node =3D eth_node; ndev->netdev_ops =3D &emac_netdev_ops; =20 @@ -1508,6 +1680,122 @@ bool icssm_prueth_sw_port_dev_check(const struct ne= t_device *ndev) return false; } =20 +static void icssm_prueth_port_offload_fwd_mark_update(struct prueth *pruet= h) +{ + int set_val =3D 0; + u8 all_slaves; + int i; + + all_slaves =3D BIT(PRUETH_PORT_MII0) | BIT(PRUETH_PORT_MII1); + + if (prueth->br_members =3D=3D all_slaves) + set_val =3D 1; + + dev_dbg(prueth->dev, "set offload_fwd_mark %d, mbrs=3D0x%x\n", + set_val, prueth->br_members); + + for (i =3D 0; i < PRUETH_NUM_MACS; i++) + prueth->emac[i]->offload_fwd_mark =3D set_val; + + /* Bridge is created, load switch firmware, + * if not already in that mode + */ + if (set_val && !PRUETH_IS_SWITCH(prueth)) + icssm_prueth_change_to_switch_mode(prueth); + + /* Bridge is deleted, switch to Dual EMAC mode */ + if (!prueth->br_members && !PRUETH_IS_EMAC(prueth)) + icssm_prueth_change_to_emac_mode(prueth); +} + +static int icssm_prueth_ndev_port_link(struct net_device *ndev, + struct net_device *br_ndev) +{ + struct prueth_emac *emac =3D netdev_priv(ndev); + struct prueth *prueth =3D emac->prueth; + + dev_dbg(prueth->dev, "%s: br_mbrs=3D0x%x %s\n", + __func__, prueth->br_members, ndev->name); + + if (!prueth->br_members) { + prueth->hw_bridge_dev =3D br_ndev; + } else { + /* This is adding the port to a second bridge, + * this is unsupported + */ + if (prueth->hw_bridge_dev !=3D br_ndev) + return -EOPNOTSUPP; + } + + prueth->br_members |=3D BIT(emac->port_id); + + icssm_prueth_port_offload_fwd_mark_update(prueth); + + return NOTIFY_DONE; +} + +static void icssm_prueth_ndev_port_unlink(struct net_device *ndev) +{ + struct prueth_emac *emac =3D netdev_priv(ndev); + struct prueth *prueth =3D emac->prueth; + + dev_dbg(prueth->dev, "emac_sw_ndev_port_unlink\n"); + + prueth->br_members &=3D ~BIT(emac->port_id); + + icssm_prueth_port_offload_fwd_mark_update(prueth); + + if (!prueth->br_members) + prueth->hw_bridge_dev =3D NULL; +} + +static int icssm_prueth_ndev_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *ndev =3D netdev_notifier_info_to_dev(ptr); + struct netdev_notifier_changeupper_info *info; + int ret =3D NOTIFY_DONE; + + if (!icssm_prueth_sw_port_dev_check(ndev)) + return NOTIFY_DONE; + + switch (event) { + case NETDEV_CHANGEUPPER: + info =3D ptr; + if (netif_is_bridge_master(info->upper_dev)) { + if (info->linking) + ret =3D icssm_prueth_ndev_port_link + (ndev, info->upper_dev); + else + icssm_prueth_ndev_port_unlink(ndev); + } + break; + default: + return NOTIFY_DONE; + } + + return notifier_from_errno(ret); +} + +static int icssm_prueth_register_notifiers(struct prueth *prueth) +{ + int ret =3D 0; + + prueth->prueth_netdevice_nb.notifier_call =3D icssm_prueth_ndev_event; + ret =3D register_netdevice_notifier(&prueth->prueth_netdevice_nb); + if (ret) { + dev_err(prueth->dev, + "register netdevice notifier failed ret: %d\n", ret); + return ret; + } + + ret =3D icssm_prueth_sw_register_notifiers(prueth); + if (ret) + unregister_netdevice_notifier(&prueth->prueth_netdevice_nb); + + return ret; +} + static int icssm_prueth_probe(struct platform_device *pdev) { struct device_node *eth0_node =3D NULL, *eth1_node =3D NULL; @@ -1728,6 +2016,12 @@ static int icssm_prueth_probe(struct platform_device= *pdev) prueth->emac[PRUETH_MAC1]->ndev; } =20 + ret =3D icssm_prueth_register_notifiers(prueth); + if (ret) { + dev_err(dev, "can't register switchdev notifiers"); + goto netdev_unregister; + } + dev_info(dev, "TI PRU ethernet driver initialized: %s EMAC mode\n", (!eth0_node || !eth1_node) ? "single" : "dual"); =20 @@ -1788,6 +2082,9 @@ static void icssm_prueth_remove(struct platform_devic= e *pdev) struct device_node *eth_node; int i; =20 + unregister_netdevice_notifier(&prueth->prueth_netdevice_nb); + icssm_prueth_sw_unregister_notifiers(prueth); + for (i =3D 0; i < PRUETH_NUM_MACS; i++) { if (!prueth->registered_netdevs[i]) continue; @@ -1887,11 +2184,16 @@ static struct prueth_private_data am335x_prueth_pda= ta =3D { .fw_pru[PRUSS_PRU0] =3D { .fw_name[PRUSS_ETHTYPE_EMAC] =3D "ti-pruss/am335x-pru0-prueth-fw.elf", + .fw_name[PRUSS_ETHTYPE_SWITCH] =3D + "ti-pruss/am335x-pru0-prusw-fw.elf", }, .fw_pru[PRUSS_PRU1] =3D { .fw_name[PRUSS_ETHTYPE_EMAC] =3D "ti-pruss/am335x-pru1-prueth-fw.elf", + .fw_name[PRUSS_ETHTYPE_SWITCH] =3D + "ti-pruss/am335x-pru1-prusw-fw.elf", }, + .support_switch =3D true, }; =20 /* AM437x SoC-specific firmware data */ @@ -1900,11 +2202,16 @@ static struct prueth_private_data am437x_prueth_pda= ta =3D { .fw_pru[PRUSS_PRU0] =3D { .fw_name[PRUSS_ETHTYPE_EMAC] =3D "ti-pruss/am437x-pru0-prueth-fw.elf", + .fw_name[PRUSS_ETHTYPE_SWITCH] =3D + "ti-pruss/am437x-pru0-prusw-fw.elf", }, .fw_pru[PRUSS_PRU1] =3D { .fw_name[PRUSS_ETHTYPE_EMAC] =3D "ti-pruss/am437x-pru1-prueth-fw.elf", + .fw_name[PRUSS_ETHTYPE_SWITCH] =3D + "ti-pruss/am437x-pru1-prusw-fw.elf", }, + .support_switch =3D true, }; =20 /* AM57xx SoC-specific firmware data */ @@ -1913,11 +2220,17 @@ static struct prueth_private_data am57xx_prueth_pda= ta =3D { .fw_pru[PRUSS_PRU0] =3D { .fw_name[PRUSS_ETHTYPE_EMAC] =3D "ti-pruss/am57xx-pru0-prueth-fw.elf", + .fw_name[PRUSS_ETHTYPE_SWITCH] =3D + "ti-pruss/am57xx-pru0-prusw-fw.elf", }, .fw_pru[PRUSS_PRU1] =3D { .fw_name[PRUSS_ETHTYPE_EMAC] =3D "ti-pruss/am57xx-pru1-prueth-fw.elf", + .fw_name[PRUSS_ETHTYPE_SWITCH] =3D + "ti-pruss/am57xx-pru1-prusw-fw.elf", + }, + .support_switch =3D true, }; =20 static const struct of_device_id prueth_dt_match[] =3D { diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth.h b/drivers/net/eth= ernet/ti/icssm/icssm_prueth.h index c98ed2cd76e7..a4e3d0ac96c7 100644 --- a/drivers/net/ethernet/ti/icssm/icssm_prueth.h +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth.h @@ -189,10 +189,12 @@ struct prueth_fw_offsets { * struct prueth_private_data - PRU Ethernet private data * @driver_data: PRU Ethernet device name * @fw_pru: firmware names to be used for PRUSS ethernet usecases + * @support_switch: boolean to indicate if switch is enabled */ struct prueth_private_data { enum pruss_device driver_data; const struct prueth_firmware fw_pru[PRUSS_NUM_PRUS]; + bool support_switch; }; =20 struct prueth_emac_stats { @@ -240,6 +242,7 @@ struct prueth_emac { =20 struct hrtimer tx_hrtimer; struct prueth_emac_stats stats; + int offload_fwd_mark; }; =20 struct prueth { @@ -268,8 +271,11 @@ struct prueth { unsigned int eth_type; size_t ocmc_ram_size; u8 emac_configured; + u8 br_members; }; =20 +extern const struct prueth_queue_desc queue_descs[][NUM_QUEUES]; + void icssm_parse_packet_info(struct prueth *prueth, u32 buffer_descriptor, struct prueth_packet_info *pkt_info); int icssm_emac_rx_packet(struct prueth_emac *emac, u16 *bd_rd_ptr, diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth_switch.c b/drivers/= net/ethernet/ti/icssm/icssm_prueth_switch.c index 5e11a8ced402..1b0a5beec1b2 100644 --- a/drivers/net/ethernet/ti/icssm/icssm_prueth_switch.c +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth_switch.c @@ -26,6 +26,176 @@ struct icssm_prueth_sw_fdb_work { int event; }; =20 +const struct prueth_queue_info sw_queue_infos[][NUM_QUEUES] =3D { + [PRUETH_PORT_QUEUE_HOST] =3D { + [PRUETH_QUEUE1] =3D { + P0_Q1_BUFFER_OFFSET, + P0_QUEUE_DESC_OFFSET, + P0_Q1_BD_OFFSET, + P0_Q1_BD_OFFSET + ((HOST_QUEUE_1_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE2] =3D { + P0_Q2_BUFFER_OFFSET, + P0_QUEUE_DESC_OFFSET + 8, + P0_Q2_BD_OFFSET, + P0_Q2_BD_OFFSET + ((HOST_QUEUE_2_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE3] =3D { + P0_Q3_BUFFER_OFFSET, + P0_QUEUE_DESC_OFFSET + 16, + P0_Q3_BD_OFFSET, + P0_Q3_BD_OFFSET + ((HOST_QUEUE_3_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE4] =3D { + P0_Q4_BUFFER_OFFSET, + P0_QUEUE_DESC_OFFSET + 24, + P0_Q4_BD_OFFSET, + P0_Q4_BD_OFFSET + ((HOST_QUEUE_4_SIZE - 1) * BD_SIZE), + }, + }, + [PRUETH_PORT_QUEUE_MII0] =3D { + [PRUETH_QUEUE1] =3D { + P1_Q1_BUFFER_OFFSET, + P1_Q1_BUFFER_OFFSET + + ((QUEUE_1_SIZE - 1) * ICSS_BLOCK_SIZE), + P1_Q1_BD_OFFSET, + P1_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE2] =3D { + P1_Q2_BUFFER_OFFSET, + P1_Q2_BUFFER_OFFSET + + ((QUEUE_2_SIZE - 1) * ICSS_BLOCK_SIZE), + P1_Q2_BD_OFFSET, + P1_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE3] =3D { + P1_Q3_BUFFER_OFFSET, + P1_Q3_BUFFER_OFFSET + + ((QUEUE_3_SIZE - 1) * ICSS_BLOCK_SIZE), + P1_Q3_BD_OFFSET, + P1_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE4] =3D { + P1_Q4_BUFFER_OFFSET, + P1_Q4_BUFFER_OFFSET + + ((QUEUE_4_SIZE - 1) * ICSS_BLOCK_SIZE), + P1_Q4_BD_OFFSET, + P1_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE), + }, + }, + [PRUETH_PORT_QUEUE_MII1] =3D { + [PRUETH_QUEUE1] =3D { + P2_Q1_BUFFER_OFFSET, + P2_Q1_BUFFER_OFFSET + + ((QUEUE_1_SIZE - 1) * ICSS_BLOCK_SIZE), + P2_Q1_BD_OFFSET, + P2_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE2] =3D { + P2_Q2_BUFFER_OFFSET, + P2_Q2_BUFFER_OFFSET + + ((QUEUE_2_SIZE - 1) * ICSS_BLOCK_SIZE), + P2_Q2_BD_OFFSET, + P2_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE3] =3D { + P2_Q3_BUFFER_OFFSET, + P2_Q3_BUFFER_OFFSET + + ((QUEUE_3_SIZE - 1) * ICSS_BLOCK_SIZE), + P2_Q3_BD_OFFSET, + P2_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE4] =3D { + P2_Q4_BUFFER_OFFSET, + P2_Q4_BUFFER_OFFSET + + ((QUEUE_4_SIZE - 1) * ICSS_BLOCK_SIZE), + P2_Q4_BD_OFFSET, + P2_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE), + }, + }, +}; + +static const struct prueth_queue_info rx_queue_infos[][NUM_QUEUES] =3D { + [PRUETH_PORT_QUEUE_HOST] =3D { + [PRUETH_QUEUE1] =3D { + P0_Q1_BUFFER_OFFSET, + HOST_QUEUE_DESC_OFFSET, + P0_Q1_BD_OFFSET, + P0_Q1_BD_OFFSET + ((HOST_QUEUE_1_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE2] =3D { + P0_Q2_BUFFER_OFFSET, + HOST_QUEUE_DESC_OFFSET + 8, + P0_Q2_BD_OFFSET, + P0_Q2_BD_OFFSET + ((HOST_QUEUE_2_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE3] =3D { + P0_Q3_BUFFER_OFFSET, + HOST_QUEUE_DESC_OFFSET + 16, + P0_Q3_BD_OFFSET, + P0_Q3_BD_OFFSET + ((HOST_QUEUE_3_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE4] =3D { + P0_Q4_BUFFER_OFFSET, + HOST_QUEUE_DESC_OFFSET + 24, + P0_Q4_BD_OFFSET, + P0_Q4_BD_OFFSET + ((HOST_QUEUE_4_SIZE - 1) * BD_SIZE), + }, + }, + [PRUETH_PORT_QUEUE_MII0] =3D { + [PRUETH_QUEUE1] =3D { + P1_Q1_BUFFER_OFFSET, + P1_QUEUE_DESC_OFFSET, + P1_Q1_BD_OFFSET, + P1_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE2] =3D { + P1_Q2_BUFFER_OFFSET, + P1_QUEUE_DESC_OFFSET + 8, + P1_Q2_BD_OFFSET, + P1_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE3] =3D { + P1_Q3_BUFFER_OFFSET, + P1_QUEUE_DESC_OFFSET + 16, + P1_Q3_BD_OFFSET, + P1_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE4] =3D { + P1_Q4_BUFFER_OFFSET, + P1_QUEUE_DESC_OFFSET + 24, + P1_Q4_BD_OFFSET, + P1_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE), + }, + }, + [PRUETH_PORT_QUEUE_MII1] =3D { + [PRUETH_QUEUE1] =3D { + P2_Q1_BUFFER_OFFSET, + P2_QUEUE_DESC_OFFSET, + P2_Q1_BD_OFFSET, + P2_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE2] =3D { + P2_Q2_BUFFER_OFFSET, + P2_QUEUE_DESC_OFFSET + 8, + P2_Q2_BD_OFFSET, + P2_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE3] =3D { + P2_Q3_BUFFER_OFFSET, + P2_QUEUE_DESC_OFFSET + 16, + P2_Q3_BD_OFFSET, + P2_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE), + }, + [PRUETH_QUEUE4] =3D { + P2_Q4_BUFFER_OFFSET, + P2_QUEUE_DESC_OFFSET + 24, + P2_Q4_BD_OFFSET, + P2_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE), + }, + }, +}; + void icssm_prueth_sw_free_fdb_table(struct prueth *prueth) { if (prueth->emac_configured) @@ -612,3 +782,218 @@ int icssm_prueth_sw_purge_fdb(struct prueth_emac *ema= c) queue_work(system_long_wq, &fdb_work->work); return 0; } + +void icssm_prueth_sw_hostconfig(struct prueth *prueth) +{ + void __iomem *dram1_base =3D prueth->mem[PRUETH_MEM_DRAM1].va; + void __iomem *dram; + + /* queue information table */ + dram =3D dram1_base + P0_Q1_RX_CONTEXT_OFFSET; + memcpy_toio(dram, sw_queue_infos[PRUETH_PORT_QUEUE_HOST], + sizeof(sw_queue_infos[PRUETH_PORT_QUEUE_HOST])); + + /* buffer descriptor offset table*/ + dram =3D dram1_base + QUEUE_DESCRIPTOR_OFFSET_ADDR; + writew(P0_Q1_BD_OFFSET, dram); + writew(P0_Q2_BD_OFFSET, dram + 2); + writew(P0_Q3_BD_OFFSET, dram + 4); + writew(P0_Q4_BD_OFFSET, dram + 6); + + /* buffer offset table */ + dram =3D dram1_base + QUEUE_OFFSET_ADDR; + writew(P0_Q1_BUFFER_OFFSET, dram); + writew(P0_Q2_BUFFER_OFFSET, dram + 2); + writew(P0_Q3_BUFFER_OFFSET, dram + 4); + writew(P0_Q4_BUFFER_OFFSET, dram + 6); + + /* queue size lookup table */ + dram =3D dram1_base + QUEUE_SIZE_ADDR; + writew(HOST_QUEUE_1_SIZE, dram); + writew(HOST_QUEUE_1_SIZE, dram + 2); + writew(HOST_QUEUE_1_SIZE, dram + 4); + writew(HOST_QUEUE_1_SIZE, dram + 6); + + /* queue table */ + dram =3D dram1_base + P0_QUEUE_DESC_OFFSET; + memcpy_toio(dram, queue_descs[PRUETH_PORT_QUEUE_HOST], + sizeof(queue_descs[PRUETH_PORT_QUEUE_HOST])); +} + +static int icssm_prueth_sw_port_config(struct prueth *prueth, + enum prueth_port port_id) +{ + unsigned int tx_context_ofs_addr, rx_context_ofs, queue_desc_ofs; + void __iomem *dram, *dram_base, *dram_mac; + struct prueth_emac *emac; + void __iomem *dram1_base; + + dram1_base =3D prueth->mem[PRUETH_MEM_DRAM1].va; + emac =3D prueth->emac[port_id - 1]; + switch (port_id) { + case PRUETH_PORT_MII0: + tx_context_ofs_addr =3D TX_CONTEXT_P1_Q1_OFFSET_ADDR; + rx_context_ofs =3D P1_Q1_RX_CONTEXT_OFFSET; + queue_desc_ofs =3D P1_QUEUE_DESC_OFFSET; + + /* for switch PORT MII0 mac addr is in DRAM0. */ + dram_mac =3D prueth->mem[PRUETH_MEM_DRAM0].va; + break; + case PRUETH_PORT_MII1: + tx_context_ofs_addr =3D TX_CONTEXT_P2_Q1_OFFSET_ADDR; + rx_context_ofs =3D P2_Q1_RX_CONTEXT_OFFSET; + queue_desc_ofs =3D P2_QUEUE_DESC_OFFSET; + + /* for switch PORT MII1 mac addr is in DRAM1. */ + dram_mac =3D prueth->mem[PRUETH_MEM_DRAM1].va; + break; + default: + netdev_err(emac->ndev, "invalid port\n"); + return -EINVAL; + } + + /* setup mac address */ + memcpy_toio(dram_mac + PORT_MAC_ADDR, emac->mac_addr, 6); + + /* Remaining switch port configs are in DRAM1 */ + dram_base =3D prueth->mem[PRUETH_MEM_DRAM1].va; + + /* queue information table */ + memcpy_toio(dram_base + tx_context_ofs_addr, + sw_queue_infos[port_id], + sizeof(sw_queue_infos[port_id])); + + memcpy_toio(dram_base + rx_context_ofs, + rx_queue_infos[port_id], + sizeof(rx_queue_infos[port_id])); + + /* buffer descriptor offset table*/ + dram =3D dram_base + QUEUE_DESCRIPTOR_OFFSET_ADDR + + (port_id * NUM_QUEUES * sizeof(u16)); + writew(sw_queue_infos[port_id][PRUETH_QUEUE1].buffer_desc_offset, dram); + writew(sw_queue_infos[port_id][PRUETH_QUEUE2].buffer_desc_offset, + dram + 2); + writew(sw_queue_infos[port_id][PRUETH_QUEUE3].buffer_desc_offset, + dram + 4); + writew(sw_queue_infos[port_id][PRUETH_QUEUE4].buffer_desc_offset, + dram + 6); + + /* buffer offset table */ + dram =3D dram_base + QUEUE_OFFSET_ADDR + + port_id * NUM_QUEUES * sizeof(u16); + writew(sw_queue_infos[port_id][PRUETH_QUEUE1].buffer_offset, dram); + writew(sw_queue_infos[port_id][PRUETH_QUEUE2].buffer_offset, + dram + 2); + writew(sw_queue_infos[port_id][PRUETH_QUEUE3].buffer_offset, + dram + 4); + writew(sw_queue_infos[port_id][PRUETH_QUEUE4].buffer_offset, + dram + 6); + + /* queue size lookup table */ + dram =3D dram_base + QUEUE_SIZE_ADDR + + port_id * NUM_QUEUES * sizeof(u16); + writew(QUEUE_1_SIZE, dram); + writew(QUEUE_2_SIZE, dram + 2); + writew(QUEUE_3_SIZE, dram + 4); + writew(QUEUE_4_SIZE, dram + 6); + + /* queue table */ + memcpy_toio(dram_base + queue_desc_ofs, + &queue_descs[port_id][0], + 4 * sizeof(queue_descs[port_id][0])); + + emac->rx_queue_descs =3D dram1_base + P0_QUEUE_DESC_OFFSET; + emac->tx_queue_descs =3D dram1_base + + rx_queue_infos[port_id][PRUETH_QUEUE1].queue_desc_offset; + + return 0; +} + +int icssm_prueth_sw_emac_config(struct prueth_emac *emac) +{ + struct prueth *prueth =3D emac->prueth; + u32 sharedramaddr, ocmcaddr; + int ret; + + /* PRU needs local shared RAM address for C28 */ + sharedramaddr =3D ICSS_LOCAL_SHARED_RAM; + /* PRU needs real global OCMC address for C30*/ + ocmcaddr =3D (u32)prueth->mem[PRUETH_MEM_OCMC].pa; + + if (prueth->emac_configured & BIT(emac->port_id)) + return 0; + + ret =3D icssm_prueth_sw_port_config(prueth, emac->port_id); + if (ret) + return ret; + + if (!prueth->emac_configured) { + /* Set in constant table C28 of PRUn to ICSS Shared memory */ + pru_rproc_set_ctable(prueth->pru0, PRU_C28, sharedramaddr); + pru_rproc_set_ctable(prueth->pru1, PRU_C28, sharedramaddr); + + /* Set in constant table C30 of PRUn to OCMC memory */ + pru_rproc_set_ctable(prueth->pru0, PRU_C30, ocmcaddr); + pru_rproc_set_ctable(prueth->pru1, PRU_C30, ocmcaddr); + } + return 0; +} + +int icssm_prueth_sw_boot_prus(struct prueth *prueth, struct net_device *nd= ev) +{ + const struct prueth_firmware *pru_firmwares; + const char *fw_name, *fw_name1; + int ret; + + if (prueth->emac_configured) + return 0; + + pru_firmwares =3D &prueth->fw_data->fw_pru[PRUSS_PRU0]; + fw_name =3D pru_firmwares->fw_name[prueth->eth_type]; + pru_firmwares =3D &prueth->fw_data->fw_pru[PRUSS_PRU1]; + fw_name1 =3D pru_firmwares->fw_name[prueth->eth_type]; + + ret =3D rproc_set_firmware(prueth->pru0, fw_name); + if (ret) { + netdev_err(ndev, "failed to set PRU0 firmware %s: %d\n", + fw_name, ret); + return ret; + } + ret =3D rproc_boot(prueth->pru0); + if (ret) { + netdev_err(ndev, "failed to boot PRU0: %d\n", ret); + return ret; + } + + ret =3D rproc_set_firmware(prueth->pru1, fw_name1); + if (ret) { + netdev_err(ndev, "failed to set PRU1 firmware %s: %d\n", + fw_name1, ret); + goto rproc0_shutdown; + } + ret =3D rproc_boot(prueth->pru1); + if (ret) { + netdev_err(ndev, "failed to boot PRU1: %d\n", ret); + goto rproc0_shutdown; + } + + return 0; + +rproc0_shutdown: + rproc_shutdown(prueth->pru0); + return ret; +} + +int icssm_prueth_sw_shutdown_prus(struct prueth_emac *emac, + struct net_device *ndev) +{ + struct prueth *prueth =3D emac->prueth; + + if (prueth->emac_configured) + return 0; + + rproc_shutdown(prueth->pru0); + rproc_shutdown(prueth->pru1); + + return 0; +} diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth_switch.h b/drivers/= net/ethernet/ti/icssm/icssm_prueth_switch.h index d1a092b173a5..fa71b48ea63e 100644 --- a/drivers/net/ethernet/ti/icssm/icssm_prueth_switch.h +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth_switch.h @@ -16,6 +16,8 @@ void icssm_prueth_sw_port_set_stp_state(struct prueth *pr= ueth, u8 icssm_prueth_sw_port_get_stp_state(struct prueth *prueth, enum prueth_port port); =20 +extern const struct prueth_queue_info sw_queue_infos[][4]; + void icssm_prueth_sw_fdb_tbl_init(struct prueth *prueth); int icssm_prueth_sw_init_fdb_table(struct prueth *prueth); void icssm_prueth_sw_free_fdb_table(struct prueth *prueth); @@ -26,4 +28,10 @@ void icssm_prueth_sw_fdb_del(struct prueth_emac *emac, struct switchdev_notifier_fdb_info *fdb); int icssm_prueth_sw_learn_fdb(struct prueth_emac *emac, u8 *src_mac); int icssm_prueth_sw_purge_fdb(struct prueth_emac *emac); +void icssm_prueth_sw_hostconfig(struct prueth *prueth); +int icssm_prueth_sw_emac_config(struct prueth_emac *emac); +int icssm_prueth_sw_boot_prus(struct prueth *prueth, struct net_device *nd= ev); +int icssm_prueth_sw_shutdown_prus(struct prueth_emac *emac, + struct net_device *ndev); + #endif /* __NET_TI_PRUETH_SWITCH_H */ diff --git a/drivers/net/ethernet/ti/icssm/icssm_switch.h b/drivers/net/eth= ernet/ti/icssm/icssm_switch.h index 44b8ae06df9c..6469dda7ad66 100644 --- a/drivers/net/ethernet/ti/icssm/icssm_switch.h +++ b/drivers/net/ethernet/ti/icssm/icssm_switch.h @@ -117,6 +117,15 @@ #define STATISTICS_OFFSET 0x1F00 #define STAT_SIZE 0x98 =20 +/* The following offsets indicate which sections of the memory are used + * for switch internal tasks + */ +#define SWITCH_SPECIFIC_DRAM0_START_SIZE 0x100 +#define SWITCH_SPECIFIC_DRAM0_START_OFFSET 0x1F00 + +#define SWITCH_SPECIFIC_DRAM1_START_SIZE 0x300 +#define SWITCH_SPECIFIC_DRAM1_START_OFFSET 0x1D00 + /* Offset for storing * 1. Storm Prevention Params * 2. PHY Speed Offset @@ -146,6 +155,74 @@ /* 4 bytes ? */ #define STP_INVALID_STATE_OFFSET (STATISTICS_OFFSET + STAT_SIZE + 33) =20 +/* DRAM1 Offsets for Switch */ +/* 4 queue descriptors for port 0 (host receive) */ +#define P0_QUEUE_DESC_OFFSET 0x1E7C +#define P1_QUEUE_DESC_OFFSET 0x1E9C +#define P2_QUEUE_DESC_OFFSET 0x1EBC +/* collision descriptor of port 0 */ +#define P0_COL_QUEUE_DESC_OFFSET 0x1E64 +#define P1_COL_QUEUE_DESC_OFFSET 0x1E6C +#define P2_COL_QUEUE_DESC_OFFSET 0x1E74 +/* Collision Status Register + * P0: bit 0 is pending flag, bit 1..2 indicates which queue, + * P1: bit 8 is pending flag, 9..10 is queue number + * P2: bit 16 is pending flag, 17..18 is queue number, remaining bits a= re 0. + */ +#define COLLISION_STATUS_ADDR 0x1E60 + +#define INTERFACE_MAC_ADDR 0x1E58 +#define P2_MAC_ADDR 0x1E50 +#define P1_MAC_ADDR 0x1E48 + +#define QUEUE_SIZE_ADDR 0x1E30 +#define QUEUE_OFFSET_ADDR 0x1E18 +#define QUEUE_DESCRIPTOR_OFFSET_ADDR 0x1E00 + +#define COL_RX_CONTEXT_P2_OFFSET_ADDR (COL_RX_CONTEXT_P1_OFFSET_ADDR + 12) +#define COL_RX_CONTEXT_P1_OFFSET_ADDR (COL_RX_CONTEXT_P0_OFFSET_ADDR + 12) +#define COL_RX_CONTEXT_P0_OFFSET_ADDR (P2_Q4_RX_CONTEXT_OFFSET + 8) + +/* Port 2 Rx Context */ +#define P2_Q4_RX_CONTEXT_OFFSET (P2_Q3_RX_CONTEXT_OFFSET + 8) +#define P2_Q3_RX_CONTEXT_OFFSET (P2_Q2_RX_CONTEXT_OFFSET + 8) +#define P2_Q2_RX_CONTEXT_OFFSET (P2_Q1_RX_CONTEXT_OFFSET + 8) +#define P2_Q1_RX_CONTEXT_OFFSET RX_CONTEXT_P2_Q1_OFFSET_ADDR +#define RX_CONTEXT_P2_Q1_OFFSET_ADDR (P1_Q4_RX_CONTEXT_OFFSET + 8) + +/* Port 1 Rx Context */ +#define P1_Q4_RX_CONTEXT_OFFSET (P1_Q3_RX_CONTEXT_OFFSET + 8) +#define P1_Q3_RX_CONTEXT_OFFSET (P1_Q2_RX_CONTEXT_OFFSET + 8) +#define P1_Q2_RX_CONTEXT_OFFSET (P1_Q1_RX_CONTEXT_OFFSET + 8) +#define P1_Q1_RX_CONTEXT_OFFSET (RX_CONTEXT_P1_Q1_OFFSET_ADDR) +#define RX_CONTEXT_P1_Q1_OFFSET_ADDR (P0_Q4_RX_CONTEXT_OFFSET + 8) + +/* Host Port Rx Context */ +#define P0_Q4_RX_CONTEXT_OFFSET (P0_Q3_RX_CONTEXT_OFFSET + 8) +#define P0_Q3_RX_CONTEXT_OFFSET (P0_Q2_RX_CONTEXT_OFFSET + 8) +#define P0_Q2_RX_CONTEXT_OFFSET (P0_Q1_RX_CONTEXT_OFFSET + 8) +#define P0_Q1_RX_CONTEXT_OFFSET RX_CONTEXT_P0_Q1_OFFSET_ADDR +#define RX_CONTEXT_P0_Q1_OFFSET_ADDR (COL_TX_CONTEXT_P2_Q1_OFFSET_ADDR + 8) + +/* Port 2 Tx Collision Context */ +#define COL_TX_CONTEXT_P2_Q1_OFFSET_ADDR (COL_TX_CONTEXT_P1_Q1_OFFSET_ADDR= + 8) +/* Port 1 Tx Collision Context */ +#define COL_TX_CONTEXT_P1_Q1_OFFSET_ADDR (P2_Q4_TX_CONTEXT_OFFSET + 8) + +/* Port 2 */ +#define P2_Q4_TX_CONTEXT_OFFSET (P2_Q3_TX_CONTEXT_OFFSET + 8) +#define P2_Q3_TX_CONTEXT_OFFSET (P2_Q2_TX_CONTEXT_OFFSET + 8) +#define P2_Q2_TX_CONTEXT_OFFSET (P2_Q1_TX_CONTEXT_OFFSET + 8) +#define P2_Q1_TX_CONTEXT_OFFSET TX_CONTEXT_P2_Q1_OFFSET_ADDR +#define TX_CONTEXT_P2_Q1_OFFSET_ADDR (P1_Q4_TX_CONTEXT_OFFSET + 8) + +/* Port 1 */ +#define P1_Q4_TX_CONTEXT_OFFSET (P1_Q3_TX_CONTEXT_OFFSET + 8) +#define P1_Q3_TX_CONTEXT_OFFSET (P1_Q2_TX_CONTEXT_OFFSET + 8) +#define P1_Q2_TX_CONTEXT_OFFSET (P1_Q1_TX_CONTEXT_OFFSET + 8) +#define P1_Q1_TX_CONTEXT_OFFSET TX_CONTEXT_P1_Q1_OFFSET_ADDR +#define TX_CONTEXT_P1_Q1_OFFSET_ADDR SWITCH_SPECIFIC_DRAM1_START_OFFSET + /* DRAM Offsets for EMAC * Present on Both DRAM0 and DRAM1 */ --=20 2.43.0