Commit 95cd03f3 authored by Paolo Abeni's avatar Paolo Abeni

Merge branch 'introduce-switch-mode-support-for-icssg-driver'

MD Danish Anwar says:

====================
Introduce switch mode support for ICSSG driver

This series adds support for switch-mode for ICSSG driver. This series
also introduces helper APIs to configure firmware maintained FDB
(Forwarding Database) and VLAN tables. These APIs are later used by ICSSG
driver in switch mode.

Now the driver will boot by default in dual EMAC mode. When first ICSSG
interface is added to bridge driver will still be in EMAC mode. As soon as
second ICSSG interface is added to same bridge, switch-mode will be
enabled and switch firmwares will be loaded to PRU cores. The driver will
remain in dual EMAC mode if ICSSG interfaces are added to two different
bridges or if two different interfaces (One ICSSG, one other) is added to
the same bridge. We'll only enable is_switch_mode flag when two ICSSG
interfaces are added to same bridge.

We start in dual MAC mode. Let's say lan0 and lan1 are ICSSG interfaces

ip link add name br0 type bridge
ip link set lan0 master br0

At this point, we get a CHANGEUPPER event. Only one port is a member of
the bridge, so we will still be in dual MAC mode.

ip link set lan1 master br0

We get a second CHANGEUPPER event, the second interface lan1 is also ICSSG
interface so we will set the is_switch_mode flag and when interfaces are
brought up again, ICSSG switch firmwares will be loaded to PRU Cores.

There are some other cases to consider as well.

ip link add name br0 type bridge
ip link add name br1 type bridge

ip link set lan0 master br0
ip link set ppp0 master br0

Here we are adding lan0 (ICSSG) and ppp0 (non ICSSG) to same bridge, as
they both are not ICSSG, we will still be running in dual EMAC mode.

ip link set lan1 master br1
ip link set vpn0 master br1

Here we are adding lan1 (ICSSG) and vpn0 (non ICSSG) to same bridge, as
they both are not ICSSG, we will still be running in dual EMAC mode.

This is v6 of the series.

Changes from v5 to v6:
*) Removed __packed from structures in icssg_config.h file.
*) Added RB tags of Andrew Lunn <andrew@lunn.ch> to patch 2/3 and patch
   3/3 of this series.

Changes from v4 to v5:
*) Rebased on 6.10-rc1.
*) Dropped the RFC tag.

Changes from v3 to v4:
*) Added RFC tag as net-next is closed now.
*) Modified the driver to remove the need of bringing interfaces up / down
   for enabling / disabling switch mode. Now switch mode can be enabled
   without bringig interfaces up / down as requested by Andrew Lunn
   <andrew@lunn.ch>
*) Modified commit message of patch 3/3.

Changes from v2 to v3:
*) Dropped RFC tag.
*) Used ether_addr_copy() instead of manually copying mac address using
   for loop in patch 1/3 as suggested by Andrew Lunn <andrew@lunn.ch>
*) Added helper API icssg_fdb_setup() in patch 1/3 to reduce code
   duplication as suggested by Andrew Lunn <andrew@lunn.ch>
*) In prueth_switchdev_stp_state_set() removed BR_STATE_LEARNING as
   learning without forwarding is not supported by ICSSG firmware.
*) Used ether_addr_equal() wherever possible in patch 2/3 as suggested
   by Andrew Lunn <andrew@lunn.ch>
*) Fixed typo "nit: s/prueth_switchdevice_nb/prueth_switchdev_nb/" in
   patch 2/3 as suggested by Simon Horman <horms@kernel.org>
*) Squashed "#include "icssg_mii_rt.h" to patch 2/3 from patch 3/3 as
   suggested by Simon Horman <horms@kernel.org>
*) Rebased on latest net-next/main.

Changes from v1 to v2:
*) Removed TAPRIO support patch from this series.
*) Stopped using devlink for enabling switch-mode as suggested by Andrew L
*) Added read_poll_timeout() in patch 1 / 3 as suggested by Andrew L.

v1 https://lore.kernel.org/all/20230830110847.1219515-4-danishanwar@ti.com/
v2 https://lore.kernel.org/all/20240118071005.1514498-1-danishanwar@ti.com/
v3 https://lore.kernel.org/all/20240327114054.1907278-1-danishanwar@ti.com/
v4 https://lore.kernel.org/all/20240515060320.2783244-1-danishanwar@ti.com/
v5 https://lore.kernel.org/all/20240527052738.152821-1-danishanwar@ti.com/

Thanks and Regards,
Md Danish Anwar
====================

Link: https://lore.kernel.org/r/20240528113734.379422-1-danishanwar@ti.comSigned-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parents c53a46b1 abd5576b
......@@ -204,6 +204,7 @@ config TI_ICSSG_PRUETH_SR1
select TI_ICSS_IEP
select TI_K3_CPPI_DESC_POOL
depends on PRU_REMOTEPROC
depends on NET_SWITCHDEV
depends on ARCH_K3 && OF && TI_K3_UDMA_GLUE_LAYER
help
Support dual Gigabit Ethernet ports over the ICSSG PRU Subsystem.
......
......@@ -39,7 +39,8 @@ icssg-prueth-y := icssg/icssg_prueth.o \
icssg/icssg_config.o \
icssg/icssg_mii_cfg.o \
icssg/icssg_stats.o \
icssg/icssg_ethtool.o
icssg/icssg_ethtool.o \
icssg/icssg_switchdev.o
obj-$(CONFIG_TI_ICSSG_PRUETH_SR1) += icssg-prueth-sr1.o
icssg-prueth-sr1-y := icssg/icssg_prueth_sr1.o \
icssg/icssg_common.o \
......
......@@ -455,7 +455,7 @@ void icssg_ft1_set_mac_addr(struct regmap *miig_rt, int slice, u8 *mac_addr)
{
const u8 mask_addr[] = { 0, 0, 0, 0, 0, 0, };
rx_class_ft1_set_start_len(miig_rt, slice, 0, 6);
rx_class_ft1_set_start_len(miig_rt, slice, 6, 6);
rx_class_ft1_set_da(miig_rt, slice, 0, mac_addr);
rx_class_ft1_set_da_mask(miig_rt, slice, 0, mask_addr);
rx_class_ft1_cfg_set_type(miig_rt, slice, 0, FT1_CFG_TYPE_EQ);
......
......@@ -581,6 +581,8 @@ static int emac_rx_packet(struct prueth_emac *emac, u32 flow_id)
} else {
/* send the filled skb up the n/w stack */
skb_put(skb, pkt_len);
if (emac->prueth->is_switch_mode)
skb->offload_fwd_mark = emac->offload_fwd_mark;
skb->protocol = eth_type_trans(skb, ndev);
napi_gro_receive(&emac->napi_rx, skb);
ndev->stats.rx_bytes += pkt_len;
......
......@@ -35,6 +35,15 @@ struct icssg_flow_cfg {
(2 * (PRUETH_EMAC_BUF_POOL_SIZE * PRUETH_NUM_BUF_POOLS + \
PRUETH_EMAC_RX_CTX_BUF_SIZE * 2))
#define PRUETH_SW_BUF_POOL_SIZE_HOST SZ_4K
#define PRUETH_SW_NUM_BUF_POOLS_HOST 8
#define PRUETH_SW_NUM_BUF_POOLS_PER_PRU 4
#define MSMC_RAM_SIZE_SWITCH_MODE \
(MSMC_RAM_SIZE + \
(2 * PRUETH_SW_BUF_POOL_SIZE_HOST * PRUETH_SW_NUM_BUF_POOLS_HOST))
#define PRUETH_SWITCH_FDB_MASK ((SIZE_OF_FDB / NUMBER_OF_FDB_BUCKET_ENTRIES) - 1)
struct icssg_rxq_ctx {
__le32 start[3];
__le32 end;
......@@ -202,6 +211,23 @@ struct icssg_setclock_desc {
#define ICSSG_TS_PUSH_SLICE0 40
#define ICSSG_TS_PUSH_SLICE1 41
struct mgmt_cmd {
u8 param;
u8 seqnum;
u8 type;
u8 header;
u32 cmd_args[3];
};
struct mgmt_cmd_rsp {
u32 reserved;
u8 status;
u8 seqnum;
u8 type;
u8 header;
u32 cmd_args[3];
};
/* FDB FID_C2 flag definitions */
/* Indicates host port membership.*/
#define ICSSG_FDB_ENTRY_P0_MEMBERSHIP BIT(0)
......
......@@ -27,13 +27,19 @@
#include <linux/remoteproc/pruss.h>
#include <linux/regmap.h>
#include <linux/remoteproc.h>
#include <net/switchdev.h>
#include "icssg_prueth.h"
#include "icssg_mii_rt.h"
#include "icssg_switchdev.h"
#include "../k3-cppi-desc-pool.h"
#define PRUETH_MODULE_DESCRIPTION "PRUSS ICSSG Ethernet driver"
#define DEFAULT_VID 1
#define DEFAULT_PORT_MASK 1
#define DEFAULT_UNTAG_MASK 1
/* CTRLMMR_ICSSG_RGMII_CTRL register bits */
#define ICSSG_CTRL_RGMII_ID_MODE BIT(24)
......@@ -112,6 +118,19 @@ static irqreturn_t prueth_tx_ts_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
static struct icssg_firmwares icssg_switch_firmwares[] = {
{
.pru = "ti-pruss/am65x-sr2-pru0-prusw-fw.elf",
.rtu = "ti-pruss/am65x-sr2-rtu0-prusw-fw.elf",
.txpru = "ti-pruss/am65x-sr2-txpru0-prusw-fw.elf",
},
{
.pru = "ti-pruss/am65x-sr2-pru1-prusw-fw.elf",
.rtu = "ti-pruss/am65x-sr2-rtu1-prusw-fw.elf",
.txpru = "ti-pruss/am65x-sr2-txpru1-prusw-fw.elf",
}
};
static struct icssg_firmwares icssg_emac_firmwares[] = {
{
.pru = "ti-pruss/am65x-sr2-pru0-prueth-fw.elf",
......@@ -131,7 +150,10 @@ static int prueth_emac_start(struct prueth *prueth, struct prueth_emac *emac)
struct device *dev = prueth->dev;
int slice, ret;
firmwares = icssg_emac_firmwares;
if (prueth->is_switch_mode)
firmwares = icssg_switch_firmwares;
else
firmwares = icssg_emac_firmwares;
slice = prueth_emac_slice(emac);
if (slice < 0) {
......@@ -445,9 +467,8 @@ static int emac_ndo_open(struct net_device *ndev)
ether_addr_copy(emac->mac_addr, ndev->dev_addr);
icssg_class_set_mac_addr(prueth->miig_rt, slice, emac->mac_addr);
icssg_ft1_set_mac_addr(prueth->miig_rt, slice, emac->mac_addr);
icssg_class_default(prueth->miig_rt, slice, 0, false);
icssg_ft1_set_mac_addr(prueth->miig_rt, slice, emac->mac_addr);
/* Notify the stack of the actual queue counts. */
ret = netif_set_real_num_tx_queues(ndev, num_data_chn);
......@@ -833,6 +854,214 @@ static int prueth_netdev_init(struct prueth *prueth,
return ret;
}
bool prueth_dev_check(const struct net_device *ndev)
{
if (ndev->netdev_ops == &emac_netdev_ops && netif_running(ndev)) {
struct prueth_emac *emac = netdev_priv(ndev);
return emac->prueth->is_switch_mode;
}
return false;
}
static void prueth_offload_fwd_mark_update(struct prueth *prueth)
{
int set_val = 0;
int i;
if (prueth->br_members == (BIT(PRUETH_PORT_MII0) | BIT(PRUETH_PORT_MII1)))
set_val = 1;
dev_dbg(prueth->dev, "set offload_fwd_mark %d\n", set_val);
for (i = PRUETH_MAC0; i < PRUETH_NUM_MACS; i++) {
struct prueth_emac *emac = prueth->emac[i];
if (!emac || !emac->ndev)
continue;
emac->offload_fwd_mark = set_val;
}
}
static void prueth_emac_restart(struct prueth *prueth)
{
struct prueth_emac *emac0 = prueth->emac[PRUETH_MAC0];
struct prueth_emac *emac1 = prueth->emac[PRUETH_MAC1];
/* Detach the net_device for both PRUeth ports*/
if (netif_running(emac0->ndev))
netif_device_detach(emac0->ndev);
if (netif_running(emac1->ndev))
netif_device_detach(emac1->ndev);
/* Disable both PRUeth ports */
emac_set_port_state(emac0, ICSSG_EMAC_PORT_DISABLE);
emac_set_port_state(emac1, ICSSG_EMAC_PORT_DISABLE);
/* Stop both pru cores for both PRUeth ports*/
prueth_emac_stop(emac0);
prueth->emacs_initialized--;
prueth_emac_stop(emac1);
prueth->emacs_initialized--;
/* Start both pru cores for both PRUeth ports */
prueth_emac_start(prueth, emac0);
prueth->emacs_initialized++;
prueth_emac_start(prueth, emac1);
prueth->emacs_initialized++;
/* Enable forwarding for both PRUeth ports */
emac_set_port_state(emac0, ICSSG_EMAC_PORT_FORWARD);
emac_set_port_state(emac1, ICSSG_EMAC_PORT_FORWARD);
/* Attache net_device for both PRUeth ports */
netif_device_attach(emac0->ndev);
netif_device_attach(emac1->ndev);
}
static void icssg_enable_switch_mode(struct prueth *prueth)
{
struct prueth_emac *emac;
int mac;
prueth_emac_restart(prueth);
for (mac = PRUETH_MAC0; mac < PRUETH_NUM_MACS; mac++) {
emac = prueth->emac[mac];
if (netif_running(emac->ndev)) {
icssg_fdb_add_del(emac, eth_stp_addr, prueth->default_vlan,
ICSSG_FDB_ENTRY_P0_MEMBERSHIP |
ICSSG_FDB_ENTRY_P1_MEMBERSHIP |
ICSSG_FDB_ENTRY_P2_MEMBERSHIP |
ICSSG_FDB_ENTRY_BLOCK,
true);
icssg_vtbl_modify(emac, emac->port_vlan | DEFAULT_VID,
BIT(emac->port_id) | DEFAULT_PORT_MASK,
BIT(emac->port_id) | DEFAULT_UNTAG_MASK,
true);
icssg_set_pvid(prueth, emac->port_vlan, emac->port_id);
emac_set_port_state(emac, ICSSG_EMAC_PORT_VLAN_AWARE_ENABLE);
}
}
}
static int prueth_netdevice_port_link(struct net_device *ndev,
struct net_device *br_ndev,
struct netlink_ext_ack *extack)
{
struct prueth_emac *emac = netdev_priv(ndev);
struct prueth *prueth = emac->prueth;
int err;
if (!prueth->br_members) {
prueth->hw_bridge_dev = br_ndev;
} else {
/* This is adding the port to a second bridge, this is
* unsupported
*/
if (prueth->hw_bridge_dev != br_ndev)
return -EOPNOTSUPP;
}
err = switchdev_bridge_port_offload(br_ndev, ndev, emac,
&prueth->prueth_switchdev_nb,
&prueth->prueth_switchdev_bl_nb,
false, extack);
if (err)
return err;
prueth->br_members |= BIT(emac->port_id);
if (!prueth->is_switch_mode) {
if (prueth->br_members & BIT(PRUETH_PORT_MII0) &&
prueth->br_members & BIT(PRUETH_PORT_MII1)) {
prueth->is_switch_mode = true;
prueth->default_vlan = 1;
emac->port_vlan = prueth->default_vlan;
icssg_enable_switch_mode(prueth);
}
}
prueth_offload_fwd_mark_update(prueth);
return NOTIFY_DONE;
}
static void prueth_netdevice_port_unlink(struct net_device *ndev)
{
struct prueth_emac *emac = netdev_priv(ndev);
struct prueth *prueth = emac->prueth;
prueth->br_members &= ~BIT(emac->port_id);
if (prueth->is_switch_mode) {
prueth->is_switch_mode = false;
emac->port_vlan = 0;
prueth_emac_restart(prueth);
}
prueth_offload_fwd_mark_update(prueth);
if (!prueth->br_members)
prueth->hw_bridge_dev = NULL;
}
/* netdev notifier */
static int prueth_netdevice_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr);
struct net_device *ndev = netdev_notifier_info_to_dev(ptr);
struct netdev_notifier_changeupper_info *info;
int ret = NOTIFY_DONE;
if (ndev->netdev_ops != &emac_netdev_ops)
return NOTIFY_DONE;
switch (event) {
case NETDEV_CHANGEUPPER:
info = ptr;
if (netif_is_bridge_master(info->upper_dev)) {
if (info->linking)
ret = prueth_netdevice_port_link(ndev, info->upper_dev, extack);
else
prueth_netdevice_port_unlink(ndev);
}
break;
default:
return NOTIFY_DONE;
}
return notifier_from_errno(ret);
}
static int prueth_register_notifiers(struct prueth *prueth)
{
int ret = 0;
prueth->prueth_netdevice_nb.notifier_call = &prueth_netdevice_event;
ret = register_netdevice_notifier(&prueth->prueth_netdevice_nb);
if (ret) {
dev_err(prueth->dev, "can't register netdevice notifier\n");
return ret;
}
ret = prueth_switchdev_register_notifiers(prueth);
if (ret)
unregister_netdevice_notifier(&prueth->prueth_netdevice_nb);
return ret;
}
static void prueth_unregister_notifiers(struct prueth *prueth)
{
prueth_switchdev_unregister_notifiers(prueth);
unregister_netdevice_notifier(&prueth->prueth_netdevice_nb);
}
static int prueth_probe(struct platform_device *pdev)
{
struct device_node *eth_node, *eth_ports_node;
......@@ -960,6 +1189,9 @@ static int prueth_probe(struct platform_device *pdev)
}
msmc_ram_size = MSMC_RAM_SIZE;
prueth->is_switchmode_supported = prueth->pdata.switch_mode;
if (prueth->is_switchmode_supported)
msmc_ram_size = MSMC_RAM_SIZE_SWITCH_MODE;
/* NOTE: FW bug needs buffer base to be 64KB aligned */
prueth->msmcram.va =
......@@ -1065,6 +1297,14 @@ static int prueth_probe(struct platform_device *pdev)
phy_attached_info(prueth->emac[PRUETH_MAC1]->ndev->phydev);
}
if (prueth->is_switchmode_supported) {
ret = prueth_register_notifiers(prueth);
if (ret)
goto netdev_unregister;
sprintf(prueth->switch_id, "%s", dev_name(dev));
}
dev_info(dev, "TI PRU ethernet driver initialized: %s EMAC mode\n",
(!eth0_node || !eth1_node) ? "single" : "dual");
......@@ -1134,6 +1374,8 @@ static void prueth_remove(struct platform_device *pdev)
struct device_node *eth_node;
int i;
prueth_unregister_notifiers(prueth);
for (i = 0; i < PRUETH_NUM_MACS; i++) {
if (!prueth->registered_netdevs[i])
continue;
......@@ -1175,10 +1417,12 @@ static void prueth_remove(struct platform_device *pdev)
static const struct prueth_pdata am654_icssg_pdata = {
.fdqring_mode = K3_RINGACC_RING_MODE_MESSAGE,
.quirk_10m_link_issue = 1,
.switch_mode = 1,
};
static const struct prueth_pdata am64x_icssg_pdata = {
.fdqring_mode = K3_RINGACC_RING_MODE_RING,
.switch_mode = 1,
};
static const struct of_device_id prueth_dt_match[] = {
......
......@@ -186,6 +186,9 @@ struct prueth_emac {
struct pruss_mem_region dram;
bool offload_fwd_mark;
int port_vlan;
struct delayed_work stats_work;
u64 stats[ICSSG_NUM_STATS];
......@@ -198,10 +201,12 @@ struct prueth_emac {
* struct prueth_pdata - PRUeth platform data
* @fdqring_mode: Free desc queue mode
* @quirk_10m_link_issue: 10M link detect errata
* @switch_mode: switch firmware support
*/
struct prueth_pdata {
enum k3_ring_mode fdqring_mode;
u32 quirk_10m_link_issue:1;
u32 switch_mode:1;
};
struct icssg_firmwares {
......@@ -232,6 +237,16 @@ struct icssg_firmwares {
* @emacs_initialized: num of EMACs/ext ports that are up/running
* @iep0: pointer to IEP0 device
* @iep1: pointer to IEP1 device
* @vlan_tbl: VLAN-FID table pointer
* @hw_bridge_dev: pointer to HW bridge net device
* @br_members: bitmask of bridge member ports
* @prueth_netdevice_nb: netdevice notifier block
* @prueth_switchdev_nb: switchdev notifier block
* @prueth_switchdev_bl_nb: switchdev blocking notifier block
* @is_switch_mode: flag to indicate if device is in Switch mode
* @is_switchmode_supported: indicates platform support for switch mode
* @switch_id: ID for mapping switch ports to bridge
* @default_vlan: Default VLAN for host
*/
struct prueth {
struct device *dev;
......@@ -256,6 +271,17 @@ struct prueth {
int emacs_initialized;
struct icss_iep *iep0;
struct icss_iep *iep1;
struct prueth_vlan_tbl *vlan_tbl;
struct net_device *hw_bridge_dev;
u8 br_members;
struct notifier_block prueth_netdevice_nb;
struct notifier_block prueth_switchdev_nb;
struct notifier_block prueth_switchdev_bl_nb;
bool is_switch_mode;
bool is_switchmode_supported;
unsigned char switch_id[MAX_PHYS_ITEM_ID_LEN];
int default_vlan;
};
struct emac_tx_ts_response {
......@@ -313,6 +339,16 @@ int icssg_queue_pop(struct prueth *prueth, u8 queue);
void icssg_queue_push(struct prueth *prueth, int queue, u16 addr);
u32 icssg_queue_level(struct prueth *prueth, int queue);
int icssg_send_fdb_msg(struct prueth_emac *emac, struct mgmt_cmd *cmd,
struct mgmt_cmd_rsp *rsp);
int icssg_fdb_add_del(struct prueth_emac *emac, const unsigned char *addr,
u8 vid, u8 fid_c2, bool add);
int icssg_fdb_lookup(struct prueth_emac *emac, const unsigned char *addr,
u8 vid);
void icssg_vtbl_modify(struct prueth_emac *emac, u8 vid, u8 port_mask,
u8 untag_mask, bool add);
u16 icssg_get_pvid(struct prueth_emac *emac);
void icssg_set_pvid(struct prueth *prueth, u8 vid, u8 port);
#define prueth_napi_to_tx_chn(pnapi) \
container_of(pnapi, struct prueth_tx_chn, napi_tx)
......
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/
*/
#ifndef __NET_TI_ICSSG_SWITCHDEV_H
#define __NET_TI_ICSSG_SWITCHDEV_H
#include "icssg_prueth.h"
int prueth_switchdev_register_notifiers(struct prueth *prueth);
void prueth_switchdev_unregister_notifiers(struct prueth *prueth);
bool prueth_dev_check(const struct net_device *ndev);
#endif /* __NET_TI_ICSSG_SWITCHDEV_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment