From patchwork Wed May 1 20:45:02 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hauke Mehrtens X-Patchwork-Id: 1093827 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=hauke-m.de Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 44vVln4bxDz9sDn for ; Thu, 2 May 2019 06:45:41 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726242AbfEAUpk (ORCPT ); Wed, 1 May 2019 16:45:40 -0400 Received: from mx1.mailbox.org ([80.241.60.212]:62946 "EHLO mx1.mailbox.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726088AbfEAUpk (ORCPT ); Wed, 1 May 2019 16:45:40 -0400 Received: from smtp1.mailbox.org (smtp1.mailbox.org [IPv6:2001:67c:2050:105:465:1:1:0]) (using TLSv1.2 with cipher ECDHE-RSA-CHACHA20-POLY1305 (256/256 bits)) (No client certificate requested) by mx1.mailbox.org (Postfix) with ESMTPS id 143584C15C; Wed, 1 May 2019 22:45:39 +0200 (CEST) X-Virus-Scanned: amavisd-new at heinlein-support.de Received: from smtp1.mailbox.org ([80.241.60.240]) by hefe.heinlein-support.de (hefe.heinlein-support.de [91.198.250.172]) (amavisd-new, port 10030) with ESMTP id YSb1038aVTzZ; Wed, 1 May 2019 22:45:32 +0200 (CEST) From: Hauke Mehrtens To: davem@davemloft.net Cc: andrew@lunn.ch, vivien.didelot@gmail.com, f.fainelli@gmail.com, netdev@vger.kernel.org, Hauke Mehrtens Subject: [PATCH 1/5] net: dsa: lantiq: Allow special tags only on CPU port Date: Wed, 1 May 2019 22:45:02 +0200 Message-Id: <20190501204506.21579-2-hauke@hauke-m.de> In-Reply-To: <20190501204506.21579-1-hauke@hauke-m.de> References: <20190501204506.21579-1-hauke@hauke-m.de> MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Allow the special tag in ingress only on the CPU port and not on all ports. A packet with a special tag could circumvent the hardware forwarding and should only be allowed on the CPU port where Linux controls the port. Signed-off-by: Hauke Mehrtens --- drivers/net/dsa/lantiq_gswip.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c index d8328866908c..0a2259cb09df 100644 --- a/drivers/net/dsa/lantiq_gswip.c +++ b/drivers/net/dsa/lantiq_gswip.c @@ -461,8 +461,6 @@ static int gswip_port_enable(struct dsa_switch *ds, int port, GSWIP_FDMA_PCTRLp(port)); gswip_switch_mask(priv, 0, GSWIP_SDMA_PCTRL_EN, GSWIP_SDMA_PCTRLp(port)); - gswip_switch_mask(priv, 0, GSWIP_PCE_PCTRL_0_INGRESS, - GSWIP_PCE_PCTRL_0p(port)); if (!dsa_is_cpu_port(ds, port)) { u32 macconf = GSWIP_MDIO_PHY_LINK_AUTO | @@ -578,6 +576,10 @@ static int gswip_setup(struct dsa_switch *ds) gswip_switch_mask(priv, 0, GSWIP_FDMA_PCTRL_STEN, GSWIP_FDMA_PCTRLp(cpu_port)); + /* accept special tag in ingress direction */ + gswip_switch_mask(priv, 0, GSWIP_PCE_PCTRL_0_INGRESS, + GSWIP_PCE_PCTRL_0p(cpu_port)); + gswip_switch_mask(priv, 0, GSWIP_MAC_CTRL_2_MLEN, GSWIP_MAC_CTRL_2p(cpu_port)); gswip_switch_w(priv, VLAN_ETH_FRAME_LEN + 8, GSWIP_MAC_FLEN); From patchwork Wed May 1 20:45:03 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hauke Mehrtens X-Patchwork-Id: 1093831 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=hauke-m.de Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 44vVmC13rLz9sBr for ; Thu, 2 May 2019 06:46:03 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726341AbfEAUqC (ORCPT ); Wed, 1 May 2019 16:46:02 -0400 Received: from mx2.mailbox.org ([80.241.60.215]:25544 "EHLO mx2.mailbox.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726302AbfEAUqB (ORCPT ); Wed, 1 May 2019 16:46:01 -0400 Received: from smtp1.mailbox.org (smtp1.mailbox.org [80.241.60.240]) (using TLSv1.2 with cipher ECDHE-RSA-CHACHA20-POLY1305 (256/256 bits)) (No client certificate requested) by mx2.mailbox.org (Postfix) with ESMTPS id F2E83A0034; Wed, 1 May 2019 22:45:58 +0200 (CEST) X-Virus-Scanned: amavisd-new at heinlein-support.de Received: from smtp1.mailbox.org ([80.241.60.240]) by spamfilter02.heinlein-hosting.de (spamfilter02.heinlein-hosting.de [80.241.56.116]) (amavisd-new, port 10030) with ESMTP id YqcQUQG5ZSgB; Wed, 1 May 2019 22:45:32 +0200 (CEST) From: Hauke Mehrtens To: davem@davemloft.net Cc: andrew@lunn.ch, vivien.didelot@gmail.com, f.fainelli@gmail.com, netdev@vger.kernel.org, Hauke Mehrtens Subject: [PATCH 2/5] net: dsa: lantiq: Add VLAN unaware bridge offloading Date: Wed, 1 May 2019 22:45:03 +0200 Message-Id: <20190501204506.21579-3-hauke@hauke-m.de> In-Reply-To: <20190501204506.21579-1-hauke@hauke-m.de> References: <20190501204506.21579-1-hauke@hauke-m.de> MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org This allows to offload bridges with DSA to the switch hardware and do the packet forwarding in hardware. This implements generic functions to access the switch hardware tables, which are used to control many features of the switch. This patch activates the MAC learning by removing the MAC address table lock, to prevent uncontrolled forwarding of packets between all the LAN ports, they are added into individual bridge tables entries with individual flow ids and the switch will do the MAC learning for each port separately before they are added to a real bridge. Each bridge consist of an entry in the active VLAN table and the VLAN mapping table, table entries with the same index are matching. In the VLAN unaware mode we configure everything with VLAN ID 0, but we use different flow IDs, the switch should handle all VLANs as normal payload and ignore them. When the hardware looks for the port of the destination MAC address it only takes the entries which have the same flow ID of the ingress packet. Signed-off-by: Hauke Mehrtens --- drivers/net/dsa/lantiq_gswip.c | 476 ++++++++++++++++++++++++++++++++- 1 file changed, 469 insertions(+), 7 deletions(-) diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c index 0a2259cb09df..8ab29db36bc2 100644 --- a/drivers/net/dsa/lantiq_gswip.c +++ b/drivers/net/dsa/lantiq_gswip.c @@ -4,7 +4,25 @@ * * Copyright (C) 2010 Lantiq Deutschland * Copyright (C) 2012 John Crispin - * Copyright (C) 2017 - 2018 Hauke Mehrtens + * Copyright (C) 2017 - 2019 Hauke Mehrtens + * + * The VLAN and bridge model the GSWIP hardware uses does not directly + * matches the model DSA uses. + * + * The hardware has 64 possible table entries for bridges with one VLAN + * ID, one flow id and a list of ports for each bridge. All entries which + * match the same flow ID are combined in the mac learning table, they + * act as one global bridge. + * The hardware does not support VLAN filter on the port, but on the + * bridge, this driver converts the DSA model to the hardware. + * + * The CPU gets all the exception frames which do not match any forwarding + * rule and the CPU port is also added to all bridges. This makes it possible + * to handle all the special cases easily in software. + * At the initialization the driver allocates one bridge table entry for + * each switch port which is used when the port is used without an + * explicit bridge. This prevents the frames from being forwarded + * between all LAN ports by default. */ #include @@ -148,19 +166,29 @@ #define GSWIP_PCE_PMAP2 0x454 /* Default Multicast port map */ #define GSWIP_PCE_PMAP3 0x455 /* Default Unknown Unicast port map */ #define GSWIP_PCE_GCTRL_0 0x456 +#define GSWIP_PCE_GCTRL_0_MTFL BIT(0) /* MAC Table Flushing */ #define GSWIP_PCE_GCTRL_0_MC_VALID BIT(3) #define GSWIP_PCE_GCTRL_0_VLAN BIT(14) /* VLAN aware Switching */ #define GSWIP_PCE_GCTRL_1 0x457 #define GSWIP_PCE_GCTRL_1_MAC_GLOCK BIT(2) /* MAC Address table lock */ #define GSWIP_PCE_GCTRL_1_MAC_GLOCK_MOD BIT(3) /* Mac address table lock forwarding mode */ #define GSWIP_PCE_PCTRL_0p(p) (0x480 + ((p) * 0xA)) -#define GSWIP_PCE_PCTRL_0_INGRESS BIT(11) +#define GSWIP_PCE_PCTRL_0_TVM BIT(5) /* Transparent VLAN mode */ +#define GSWIP_PCE_PCTRL_0_VREP BIT(6) /* VLAN Replace Mode */ +#define GSWIP_PCE_PCTRL_0_INGRESS BIT(11) /* Accept special tag in ingress */ #define GSWIP_PCE_PCTRL_0_PSTATE_LISTEN 0x0 #define GSWIP_PCE_PCTRL_0_PSTATE_RX 0x1 #define GSWIP_PCE_PCTRL_0_PSTATE_TX 0x2 #define GSWIP_PCE_PCTRL_0_PSTATE_LEARNING 0x3 #define GSWIP_PCE_PCTRL_0_PSTATE_FORWARDING 0x7 #define GSWIP_PCE_PCTRL_0_PSTATE_MASK GENMASK(2, 0) +#define GSWIP_PCE_VCTRL(p) (0x485 + ((p) * 0xA)) +#define GSWIP_PCE_VCTRL_UVR BIT(0) /* Unknown VLAN Rule */ +#define GSWIP_PCE_VCTRL_VIMR BIT(3) /* VLAN Ingress Member violation rule */ +#define GSWIP_PCE_VCTRL_VEMR BIT(4) /* VLAN Egress Member violation rule */ +#define GSWIP_PCE_VCTRL_VSR BIT(5) /* VLAN Security */ +#define GSWIP_PCE_VCTRL_VID0 BIT(6) /* Priority Tagged Rule */ +#define GSWIP_PCE_DEFPVID(p) (0x486 + ((p) * 0xA)) #define GSWIP_MAC_FLEN 0x8C5 #define GSWIP_MAC_CTRL_2p(p) (0x905 + ((p) * 0xC)) @@ -183,6 +211,10 @@ #define GSWIP_SDMA_PCTRL_FCEN BIT(1) /* Flow Control Enable */ #define GSWIP_SDMA_PCTRL_PAUFWD BIT(1) /* Pause Frame Forwarding */ +#define GSWIP_TABLE_ACTIVE_VLAN 0x01 +#define GSWIP_TABLE_VLAN_MAPPING 0x02 +#define GSWIP_TABLE_MAC_BRIDGE 0x0b + #define XRX200_GPHY_FW_ALIGN (16 * 1024) struct gswip_hw_info { @@ -202,6 +234,12 @@ struct gswip_gphy_fw { char *fw_name; }; +struct gswip_vlan { + struct net_device *bridge; + u16 vid; + u8 fid; +}; + struct gswip_priv { __iomem void *gswip; __iomem void *mdio; @@ -211,10 +249,23 @@ struct gswip_priv { struct dsa_switch *ds; struct device *dev; struct regmap *rcu_regmap; + struct gswip_vlan vlans[64]; int num_gphy_fw; struct gswip_gphy_fw *gphy_fw; }; +struct gswip_pce_table_entry { + u16 index; // PCE_TBL_ADDR.ADDR = pData->table_index + u16 table; // PCE_TBL_CTRL.ADDR = pData->table + u16 key[8]; + u16 val[5]; + u16 mask; + u8 gmap; + bool type; + bool valid; + bool key_mode; +}; + struct gswip_rmon_cnt_desc { unsigned int size; unsigned int offset; @@ -447,10 +498,153 @@ static int gswip_mdio(struct gswip_priv *priv, struct device_node *mdio_np) return of_mdiobus_register(ds->slave_mii_bus, mdio_np); } +static int gswip_pce_table_entry_read(struct gswip_priv *priv, + struct gswip_pce_table_entry *tbl) +{ + int i; + int err; + u16 crtl; + u16 addr_mode = tbl->key_mode ? GSWIP_PCE_TBL_CTRL_OPMOD_KSRD : + GSWIP_PCE_TBL_CTRL_OPMOD_ADRD; + + err = gswip_switch_r_timeout(priv, GSWIP_PCE_TBL_CTRL, + GSWIP_PCE_TBL_CTRL_BAS); + if (err) + return err; + + gswip_switch_w(priv, tbl->index, GSWIP_PCE_TBL_ADDR); + gswip_switch_mask(priv, GSWIP_PCE_TBL_CTRL_ADDR_MASK | + GSWIP_PCE_TBL_CTRL_OPMOD_MASK, + tbl->table | addr_mode | GSWIP_PCE_TBL_CTRL_BAS, + GSWIP_PCE_TBL_CTRL); + + err = gswip_switch_r_timeout(priv, GSWIP_PCE_TBL_CTRL, + GSWIP_PCE_TBL_CTRL_BAS); + if (err) + return err; + + for (i = 0; i < ARRAY_SIZE(tbl->key); i++) + tbl->key[i] = gswip_switch_r(priv, GSWIP_PCE_TBL_KEY(i)); + + for (i = 0; i < ARRAY_SIZE(tbl->val); i++) + tbl->val[i] = gswip_switch_r(priv, GSWIP_PCE_TBL_VAL(i)); + + tbl->mask = gswip_switch_r(priv, GSWIP_PCE_TBL_MASK); + + crtl = gswip_switch_r(priv, GSWIP_PCE_TBL_CTRL); + + tbl->type = !!(crtl & GSWIP_PCE_TBL_CTRL_TYPE); + tbl->valid = !!(crtl & GSWIP_PCE_TBL_CTRL_VLD); + tbl->gmap = (crtl & GSWIP_PCE_TBL_CTRL_GMAP_MASK) >> 7; + + return 0; +} + +static int gswip_pce_table_entry_write(struct gswip_priv *priv, + struct gswip_pce_table_entry *tbl) +{ + int i; + int err; + u16 crtl; + u16 addr_mode = tbl->key_mode ? GSWIP_PCE_TBL_CTRL_OPMOD_KSWR : + GSWIP_PCE_TBL_CTRL_OPMOD_ADWR; + + err = gswip_switch_r_timeout(priv, GSWIP_PCE_TBL_CTRL, + GSWIP_PCE_TBL_CTRL_BAS); + if (err) + return err; + + gswip_switch_w(priv, tbl->index, GSWIP_PCE_TBL_ADDR); + gswip_switch_mask(priv, GSWIP_PCE_TBL_CTRL_ADDR_MASK | + GSWIP_PCE_TBL_CTRL_OPMOD_MASK, + tbl->table | addr_mode, + GSWIP_PCE_TBL_CTRL); + + for (i = 0; i < ARRAY_SIZE(tbl->key); i++) + gswip_switch_w(priv, tbl->key[i], GSWIP_PCE_TBL_KEY(i)); + + for (i = 0; i < ARRAY_SIZE(tbl->val); i++) + gswip_switch_w(priv, tbl->val[i], GSWIP_PCE_TBL_VAL(i)); + + gswip_switch_mask(priv, GSWIP_PCE_TBL_CTRL_ADDR_MASK | + GSWIP_PCE_TBL_CTRL_OPMOD_MASK, + tbl->table | addr_mode, + GSWIP_PCE_TBL_CTRL); + + gswip_switch_w(priv, tbl->mask, GSWIP_PCE_TBL_MASK); + + crtl = gswip_switch_r(priv, GSWIP_PCE_TBL_CTRL); + crtl &= ~(GSWIP_PCE_TBL_CTRL_TYPE | GSWIP_PCE_TBL_CTRL_VLD | + GSWIP_PCE_TBL_CTRL_GMAP_MASK); + if (tbl->type) + crtl |= GSWIP_PCE_TBL_CTRL_TYPE; + if (tbl->valid) + crtl |= GSWIP_PCE_TBL_CTRL_VLD; + crtl |= (tbl->gmap << 7) & GSWIP_PCE_TBL_CTRL_GMAP_MASK; + crtl |= GSWIP_PCE_TBL_CTRL_BAS; + gswip_switch_w(priv, crtl, GSWIP_PCE_TBL_CTRL); + + return gswip_switch_r_timeout(priv, GSWIP_PCE_TBL_CTRL, + GSWIP_PCE_TBL_CTRL_BAS); +} + +/* Add the LAN port into a bridge with the CPU port by + * default. This prevents automatic forwarding of + * packages between the LAN ports when no explicit + * bridge is configured. + */ +static int gswip_add_signle_port_br(struct gswip_priv *priv, int port, bool add) +{ + struct gswip_pce_table_entry vlan_active = {0,}; + struct gswip_pce_table_entry vlan_mapping = {0,}; + unsigned int cpu_port = priv->hw_info->cpu_port; + unsigned int max_ports = priv->hw_info->max_ports; + int err; + + if (port >= max_ports) { + dev_err(priv->dev, "single port for %i supported\n", port); + return -EIO; + } + + vlan_active.index = port + 1; + vlan_active.table = GSWIP_TABLE_ACTIVE_VLAN; + vlan_active.key[0] = 0; /* vid */ + vlan_active.val[0] = port + 1 /* fid */; + vlan_active.valid = add; + err = gswip_pce_table_entry_write(priv, &vlan_active); + if (err) { + dev_err(priv->dev, "failed to write active VLAN: %d\n", err); + return err; + } + + if (!add) + return 0; + + vlan_mapping.index = port + 1; + vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING; + vlan_mapping.val[0] = 0 /* vid */; + vlan_mapping.val[1] = BIT(port) | BIT(cpu_port); + vlan_mapping.val[2] = 0; + err = gswip_pce_table_entry_write(priv, &vlan_mapping); + if (err) { + dev_err(priv->dev, "failed to write VLAN mapping: %d\n", err); + return err; + } + + return 0; +} + static int gswip_port_enable(struct dsa_switch *ds, int port, struct phy_device *phydev) { struct gswip_priv *priv = ds->priv; + int err; + + if (!dsa_is_cpu_port(ds, port)) { + err = gswip_add_signle_port_br(priv, port, true); + if (err) + return err; + } /* RMON Counter Enable for port */ gswip_switch_w(priv, GSWIP_BM_PCFG_CNTEN, GSWIP_BM_PCFGp(port)); @@ -533,6 +727,34 @@ static int gswip_pce_load_microcode(struct gswip_priv *priv) return 0; } +static int gswip_port_vlan_filtering(struct dsa_switch *ds, int port, + bool vlan_filtering) +{ + struct gswip_priv *priv = ds->priv; + + if (vlan_filtering) { + /* Use port based VLAN tag */ + gswip_switch_mask(priv, + GSWIP_PCE_VCTRL_VSR, + GSWIP_PCE_VCTRL_UVR | GSWIP_PCE_VCTRL_VIMR | + GSWIP_PCE_VCTRL_VEMR, + GSWIP_PCE_VCTRL(port)); + gswip_switch_mask(priv, GSWIP_PCE_PCTRL_0_TVM, 0, + GSWIP_PCE_PCTRL_0p(port)); + } else { + /* Use port based VLAN tag */ + gswip_switch_mask(priv, + GSWIP_PCE_VCTRL_UVR | GSWIP_PCE_VCTRL_VIMR | + GSWIP_PCE_VCTRL_VEMR, + GSWIP_PCE_VCTRL_VSR, + GSWIP_PCE_VCTRL(port)); + gswip_switch_mask(priv, 0, GSWIP_PCE_PCTRL_0_TVM, + GSWIP_PCE_PCTRL_0p(port)); + } + + return 0; +} + static int gswip_setup(struct dsa_switch *ds) { struct gswip_priv *priv = ds->priv; @@ -545,8 +767,10 @@ static int gswip_setup(struct dsa_switch *ds) gswip_switch_w(priv, 0, GSWIP_SWRES); /* disable port fetch/store dma on all ports */ - for (i = 0; i < priv->hw_info->max_ports; i++) + for (i = 0; i < priv->hw_info->max_ports; i++) { gswip_port_disable(ds, i); + gswip_port_vlan_filtering(ds, i, false); + } /* enable Switch */ gswip_mdio_mask(priv, 0, GSWIP_MDIO_GLOB_ENABLE, GSWIP_MDIO_GLOB); @@ -589,10 +813,15 @@ static int gswip_setup(struct dsa_switch *ds) /* VLAN aware Switching */ gswip_switch_mask(priv, 0, GSWIP_PCE_GCTRL_0_VLAN, GSWIP_PCE_GCTRL_0); - /* Mac Address Table Lock */ - gswip_switch_mask(priv, 0, GSWIP_PCE_GCTRL_1_MAC_GLOCK | - GSWIP_PCE_GCTRL_1_MAC_GLOCK_MOD, - GSWIP_PCE_GCTRL_1); + /* Flush MAC Table */ + gswip_switch_mask(priv, 0, GSWIP_PCE_GCTRL_0_MTFL, GSWIP_PCE_GCTRL_0); + + err = gswip_switch_r_timeout(priv, GSWIP_PCE_GCTRL_0, + GSWIP_PCE_GCTRL_0_MTFL); + if (err) { + dev_err(priv->dev, "MAC flushing didn't finish\n"); + return err; + } gswip_port_enable(ds, cpu_port, NULL); return 0; @@ -604,6 +833,236 @@ static enum dsa_tag_protocol gswip_get_tag_protocol(struct dsa_switch *ds, return DSA_TAG_PROTO_GSWIP; } +static int gswip_vlan_active_create(struct gswip_priv *priv, + struct net_device *bridge, + int fid, u16 vid) +{ + struct gswip_pce_table_entry vlan_active = {0,}; + unsigned int max_ports = priv->hw_info->max_ports; + int idx = -1; + int err; + int i; + + /* Look for a free slot */ + for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) { + if (!priv->vlans[i].bridge) { + idx = i; + break; + } + } + + if (idx == -1) + return -ENOSPC; + + if (fid == -1) + fid = idx; + + vlan_active.index = idx; + vlan_active.table = GSWIP_TABLE_ACTIVE_VLAN; + vlan_active.key[0] = vid; + vlan_active.val[0] = fid; + vlan_active.valid = true; + + err = gswip_pce_table_entry_write(priv, &vlan_active); + if (err) { + dev_err(priv->dev, "failed to write active VLAN: %d\n", err); + return err; + } + + priv->vlans[idx].bridge = bridge; + priv->vlans[idx].vid = vid; + priv->vlans[idx].fid = fid; + + return idx; +} + +static int gswip_vlan_active_remove(struct gswip_priv *priv, int idx) +{ + struct gswip_pce_table_entry vlan_active = {0,}; + int err; + + vlan_active.index = idx; + vlan_active.table = GSWIP_TABLE_ACTIVE_VLAN; + vlan_active.valid = false; + err = gswip_pce_table_entry_write(priv, &vlan_active); + if (err) + dev_err(priv->dev, "failed to delete active VLAN: %d\n", err); + priv->vlans[idx].bridge = NULL; + + return err; +} + +static int gswip_vlan_add_unaware(struct gswip_priv *priv, + struct net_device *bridge, int port) +{ + struct gswip_pce_table_entry vlan_mapping = {0,}; + unsigned int max_ports = priv->hw_info->max_ports; + unsigned int cpu_port = priv->hw_info->cpu_port; + bool active_vlan_created = false; + int idx = -1; + int i; + int err; + + /* Check if there is already a page for this bridge */ + for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) { + if (priv->vlans[i].bridge == bridge) { + idx = i; + break; + } + } + + /* If this bridge is not programmed yet, add a Active VLAN table + * entry in a free slot and prepare the VLAN mapping table entry. + */ + if (idx == -1) { + idx = gswip_vlan_active_create(priv, bridge, -1, 0); + if (idx < 0) + return idx; + active_vlan_created = true; + + vlan_mapping.index = idx; + vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING; + /* VLAN ID byte, maps to the VLAN ID of vlan active table */ + vlan_mapping.val[0] = 0; + } else { + /* Read the existing VLAN mapping entry from the switch */ + vlan_mapping.index = idx; + vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING; + err = gswip_pce_table_entry_read(priv, &vlan_mapping); + if (err) { + dev_err(priv->dev, "failed to read VLAN mapping: %d\n", + err); + return err; + } + } + + /* Update the VLAN mapping entry and write it to the switch */ + vlan_mapping.val[1] |= BIT(cpu_port); + vlan_mapping.val[1] |= BIT(port); + err = gswip_pce_table_entry_write(priv, &vlan_mapping); + if (err) { + dev_err(priv->dev, "failed to write VLAN mapping: %d\n", err); + /* In case an Active VLAN was creaetd delete it again */ + if (active_vlan_created) + gswip_vlan_active_remove(priv, idx); + return err; + } + + gswip_switch_w(priv, 0, GSWIP_PCE_DEFPVID(port)); + return 0; +} + +static int gswip_vlan_remove(struct gswip_priv *priv, + struct net_device *bridge, int port, + u16 vid, bool pvid, bool vlan_aware) +{ + struct gswip_pce_table_entry vlan_mapping = {0,}; + unsigned int max_ports = priv->hw_info->max_ports; + unsigned int cpu_port = priv->hw_info->cpu_port; + int idx = -1; + int i; + int err; + + /* Check if there is already a page for this bridge */ + for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) { + if (priv->vlans[i].bridge == bridge && + (!vlan_aware || priv->vlans[i].vid == vid)) { + idx = i; + break; + } + } + + if (idx == -1) { + dev_err(priv->dev, "bridge to leave does not exists\n"); + return -ENOENT; + } + + vlan_mapping.index = idx; + vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING; + err = gswip_pce_table_entry_read(priv, &vlan_mapping); + if (err) { + dev_err(priv->dev, "failed to read VLAN mapping: %d\n", err); + return err; + } + + vlan_mapping.val[1] &= ~BIT(port); + vlan_mapping.val[2] &= ~BIT(port); + err = gswip_pce_table_entry_write(priv, &vlan_mapping); + if (err) { + dev_err(priv->dev, "failed to write VLAN mapping: %d\n", err); + return err; + } + + /* In case all ports are removed from the bridge, remove the VLAN */ + if ((vlan_mapping.val[1] & ~BIT(cpu_port)) == 0) { + err = gswip_vlan_active_remove(priv, idx); + if (err) { + dev_err(priv->dev, "failed to write active VLAN: %d\n", + err); + return err; + } + } + + /* GSWIP 2.2 (GRX300) and later program here the VID directly. */ + if (pvid) + gswip_switch_w(priv, 0, GSWIP_PCE_DEFPVID(port)); + + return 0; +} + +static int gswip_port_bridge_join(struct dsa_switch *ds, int port, + struct net_device *bridge) +{ + struct gswip_priv *priv = ds->priv; + int err; + + err = gswip_vlan_add_unaware(priv, bridge, port); + if (err) + return err; + return gswip_add_signle_port_br(priv, port, false); +} + +static void gswip_port_bridge_leave(struct dsa_switch *ds, int port, + struct net_device *bridge) +{ + struct gswip_priv *priv = ds->priv; + + gswip_add_signle_port_br(priv, port, true); + + gswip_vlan_remove(priv, bridge, port, 0, true, false); +} + +static void gswip_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) +{ + struct gswip_priv *priv = ds->priv; + u32 stp_state; + + switch (state) { + case BR_STATE_DISABLED: + gswip_switch_mask(priv, GSWIP_SDMA_PCTRL_EN, 0, + GSWIP_SDMA_PCTRLp(port)); + return; + case BR_STATE_BLOCKING: + case BR_STATE_LISTENING: + stp_state = GSWIP_PCE_PCTRL_0_PSTATE_LISTEN; + break; + case BR_STATE_LEARNING: + stp_state = GSWIP_PCE_PCTRL_0_PSTATE_LEARNING; + break; + case BR_STATE_FORWARDING: + stp_state = GSWIP_PCE_PCTRL_0_PSTATE_FORWARDING; + break; + default: + dev_err(priv->dev, "invalid STP state: %d\n", state); + return; + } + + gswip_switch_mask(priv, 0, GSWIP_SDMA_PCTRL_EN, + GSWIP_SDMA_PCTRLp(port)); + gswip_switch_mask(priv, GSWIP_PCE_PCTRL_0_PSTATE_MASK, stp_state, + GSWIP_PCE_PCTRL_0p(port)); +} + static void gswip_phylink_validate(struct dsa_switch *ds, int port, unsigned long *supported, struct phylink_link_state *state) @@ -811,6 +1270,9 @@ static const struct dsa_switch_ops gswip_switch_ops = { .setup = gswip_setup, .port_enable = gswip_port_enable, .port_disable = gswip_port_disable, + .port_bridge_join = gswip_port_bridge_join, + .port_bridge_leave = gswip_port_bridge_leave, + .port_stp_state_set = gswip_port_stp_state_set, .phylink_validate = gswip_phylink_validate, .phylink_mac_config = gswip_phylink_mac_config, .phylink_mac_link_down = gswip_phylink_mac_link_down, From patchwork Wed May 1 20:45:04 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hauke Mehrtens X-Patchwork-Id: 1093829 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=hauke-m.de Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 44vVlz1jBPz9sDn for ; Thu, 2 May 2019 06:45:51 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726322AbfEAUpq (ORCPT ); Wed, 1 May 2019 16:45:46 -0400 Received: from mx1.mailbox.org ([80.241.60.212]:63122 "EHLO mx1.mailbox.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726088AbfEAUpq (ORCPT ); Wed, 1 May 2019 16:45:46 -0400 Received: from smtp1.mailbox.org (smtp1.mailbox.org [IPv6:2001:67c:2050:105:465:1:1:0]) (using TLSv1.2 with cipher ECDHE-RSA-CHACHA20-POLY1305 (256/256 bits)) (No client certificate requested) by mx1.mailbox.org (Postfix) with ESMTPS id 0EB124CB10; Wed, 1 May 2019 22:45:44 +0200 (CEST) X-Virus-Scanned: amavisd-new at heinlein-support.de Received: from smtp1.mailbox.org ([80.241.60.240]) by spamfilter02.heinlein-hosting.de (spamfilter02.heinlein-hosting.de [80.241.56.116]) (amavisd-new, port 10030) with ESMTP id aPFJXt9jLFk9; Wed, 1 May 2019 22:45:32 +0200 (CEST) From: Hauke Mehrtens To: davem@davemloft.net Cc: andrew@lunn.ch, vivien.didelot@gmail.com, f.fainelli@gmail.com, netdev@vger.kernel.org, Hauke Mehrtens Subject: [PATCH 3/5] net: dsa: lantiq: Add VLAN aware bridge offloading Date: Wed, 1 May 2019 22:45:04 +0200 Message-Id: <20190501204506.21579-4-hauke@hauke-m.de> In-Reply-To: <20190501204506.21579-1-hauke@hauke-m.de> References: <20190501204506.21579-1-hauke@hauke-m.de> MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org The VLAN aware bridge offloading is similar to the VLAN unaware offloading, this makes it possible to offload the VLAN bridge functionalities. The hardware supports up to 64 VLAN bridge entries, we already use one entry for each LAN port to prevent forwarding of packets between the ports when the ports are not in a bridge, so in the end we have 57 possible VLANs. The VLAN filtering is currently only active when the ports are in a bridge, VLAN filtering for ports not in a bridge is not implemented. It is currently not possible to change between VLAN filtering and not filtering while the port is already in a bridge, this would make the driver more complicated. The VLANs are only defined on bridge entries, so we will not add anything into the hardware when the port joins a bridge if it is doing VLAN filtering, but only when an allowed VLAN is added. Signed-off-by: Hauke Mehrtens --- drivers/net/dsa/lantiq_gswip.c | 201 ++++++++++++++++++++++++++++++++- 1 file changed, 197 insertions(+), 4 deletions(-) diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c index 8ab29db36bc2..23686b8399ea 100644 --- a/drivers/net/dsa/lantiq_gswip.c +++ b/drivers/net/dsa/lantiq_gswip.c @@ -252,6 +252,7 @@ struct gswip_priv { struct gswip_vlan vlans[64]; int num_gphy_fw; struct gswip_gphy_fw *gphy_fw; + u32 port_vlan_filter; }; struct gswip_pce_table_entry { @@ -731,6 +732,11 @@ static int gswip_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering) { struct gswip_priv *priv = ds->priv; + struct net_device *bridge = dsa_to_port(ds, port)->bridge_dev; + + /* Do not allow chaning the VLAN filtering options while in bridge */ + if (!!(priv->port_vlan_filter & BIT(port)) != vlan_filtering && bridge) + return -EIO; if (vlan_filtering) { /* Use port based VLAN tag */ @@ -952,6 +958,82 @@ static int gswip_vlan_add_unaware(struct gswip_priv *priv, return 0; } +static int gswip_vlan_add_aware(struct gswip_priv *priv, + struct net_device *bridge, int port, + u16 vid, bool untagged, + bool pvid) +{ + struct gswip_pce_table_entry vlan_mapping = {0,}; + unsigned int max_ports = priv->hw_info->max_ports; + unsigned int cpu_port = priv->hw_info->cpu_port; + bool active_vlan_created = false; + int idx = -1; + int fid = -1; + int i; + int err; + + /* Check if there is already a page for this bridge */ + for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) { + if (priv->vlans[i].bridge == bridge) { + if (fid != -1 && fid != priv->vlans[i].fid) + dev_err(priv->dev, "one bridge with multiple flow ids\n"); + fid = priv->vlans[i].fid; + if (priv->vlans[i].vid == vid) { + idx = i; + break; + } + } + } + + /* If this bridge is not programmed yet, add a Active VLAN table + * entry in a free slot and prepare the VLAN mapping table entry. + */ + if (idx == -1) { + idx = gswip_vlan_active_create(priv, bridge, fid, vid); + if (idx < 0) + return idx; + active_vlan_created = true; + + vlan_mapping.index = idx; + vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING; + /* VLAN ID byte, maps to the VLAN ID of vlan active table */ + vlan_mapping.val[0] = vid; + } else { + /* Read the existing VLAN mapping entry from the switch */ + vlan_mapping.index = idx; + vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING; + err = gswip_pce_table_entry_read(priv, &vlan_mapping); + if (err) { + dev_err(priv->dev, "failed to read VLAN mapping: %d\n", + err); + return err; + } + } + + vlan_mapping.val[0] = vid; + /* Update the VLAN mapping entry and write it to the switch */ + vlan_mapping.val[1] |= BIT(cpu_port); + vlan_mapping.val[2] |= BIT(cpu_port); + vlan_mapping.val[1] |= BIT(port); + if (untagged) + vlan_mapping.val[2] &= ~BIT(port); + else + vlan_mapping.val[2] |= BIT(port); + err = gswip_pce_table_entry_write(priv, &vlan_mapping); + if (err) { + dev_err(priv->dev, "failed to write VLAN mapping: %d\n", err); + /* In case an Active VLAN was creaetd delete it again */ + if (active_vlan_created) + gswip_vlan_active_remove(priv, idx); + return err; + } + + if (pvid) + gswip_switch_w(priv, idx, GSWIP_PCE_DEFPVID(port)); + + return 0; +} + static int gswip_vlan_remove(struct gswip_priv *priv, struct net_device *bridge, int port, u16 vid, bool pvid, bool vlan_aware) @@ -1016,9 +1098,17 @@ static int gswip_port_bridge_join(struct dsa_switch *ds, int port, struct gswip_priv *priv = ds->priv; int err; - err = gswip_vlan_add_unaware(priv, bridge, port); - if (err) - return err; + /* When the bridge uses VLAN filtering we have to configure VLAN + * specific bridges. No bridge is configured here. + */ + if (!br_vlan_enabled(bridge)) { + err = gswip_vlan_add_unaware(priv, bridge, port); + if (err) + return err; + priv->port_vlan_filter &= ~BIT(port); + } else { + priv->port_vlan_filter |= BIT(port); + } return gswip_add_signle_port_br(priv, port, false); } @@ -1029,7 +1119,106 @@ static void gswip_port_bridge_leave(struct dsa_switch *ds, int port, gswip_add_signle_port_br(priv, port, true); - gswip_vlan_remove(priv, bridge, port, 0, true, false); + /* When the bridge uses VLAN filtering we have to configure VLAN + * specific bridges. No bridge is configured here. + */ + if (!br_vlan_enabled(bridge)) + gswip_vlan_remove(priv, bridge, port, 0, true, false); +} + +static int gswip_port_vlan_prepare(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + struct gswip_priv *priv = ds->priv; + struct net_device *bridge = dsa_to_port(ds, port)->bridge_dev; + unsigned int max_ports = priv->hw_info->max_ports; + u16 vid; + int i; + int pos = max_ports; + + /* We only support VLAN filtering on bridges */ + if (!dsa_is_cpu_port(ds, port) && !bridge) + return -EOPNOTSUPP; + + for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { + int idx = -1; + + /* Check if there is already a page for this VLAN */ + for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) { + if (priv->vlans[i].bridge == bridge && + priv->vlans[i].vid == vid) { + idx = i; + break; + } + } + + /* If this VLAN is not programmed yet, we have to reserve + * one entry in the VLAN table. Make sure we start at the + * next position round. + */ + if (idx == -1) { + /* Look for a free slot */ + for (; pos < ARRAY_SIZE(priv->vlans); pos++) { + if (!priv->vlans[pos].bridge) { + idx = pos; + pos++; + break; + } + } + + if (idx == -1) + return -ENOSPC; + } + } + + return 0; +} + +static void gswip_port_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + struct gswip_priv *priv = ds->priv; + struct net_device *bridge = dsa_to_port(ds, port)->bridge_dev; + bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; + bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; + u16 vid; + + /* We have to receive all packets on the CPU port and should not + * do any VLAN filtering here. This is also called with bridge + * NULL and then we do not know for which bridge to configure + * this. + */ + if (dsa_is_cpu_port(ds, port)) + return; + + for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) + gswip_vlan_add_aware(priv, bridge, port, vid, untagged, pvid); +} + +static int gswip_port_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + struct gswip_priv *priv = ds->priv; + struct net_device *bridge = dsa_to_port(ds, port)->bridge_dev; + bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; + u16 vid; + int err; + + /* We have to receive all packets on the CPU port and should not + * do any VLAN filtering here. This is also called with bridge + * NULL and then we do not know for which bridge to configure + * this. + */ + if (dsa_is_cpu_port(ds, port)) + return 0; + + for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { + err = gswip_vlan_remove(priv, bridge, port, vid, pvid, true); + if (err) + return err; + } + + return 0; } static void gswip_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) @@ -1272,6 +1461,10 @@ static const struct dsa_switch_ops gswip_switch_ops = { .port_disable = gswip_port_disable, .port_bridge_join = gswip_port_bridge_join, .port_bridge_leave = gswip_port_bridge_leave, + .port_vlan_filtering = gswip_port_vlan_filtering, + .port_vlan_prepare = gswip_port_vlan_prepare, + .port_vlan_add = gswip_port_vlan_add, + .port_vlan_del = gswip_port_vlan_del, .port_stp_state_set = gswip_port_stp_state_set, .phylink_validate = gswip_phylink_validate, .phylink_mac_config = gswip_phylink_mac_config, From patchwork Wed May 1 20:45:05 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hauke Mehrtens X-Patchwork-Id: 1093830 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=hauke-m.de Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 44vVm12kRhz9sBr for ; Thu, 2 May 2019 06:45:53 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726328AbfEAUpr (ORCPT ); Wed, 1 May 2019 16:45:47 -0400 Received: from mx2.mailbox.org ([80.241.60.215]:25086 "EHLO mx2.mailbox.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726302AbfEAUpr (ORCPT ); Wed, 1 May 2019 16:45:47 -0400 Received: from smtp1.mailbox.org (smtp1.mailbox.org [IPv6:2001:67c:2050:105:465:1:1:0]) (using TLSv1.2 with cipher ECDHE-RSA-CHACHA20-POLY1305 (256/256 bits)) (No client certificate requested) by mx2.mailbox.org (Postfix) with ESMTPS id 89F71A0203; Wed, 1 May 2019 22:45:44 +0200 (CEST) X-Virus-Scanned: amavisd-new at heinlein-support.de Received: from smtp1.mailbox.org ([80.241.60.240]) by spamfilter06.heinlein-hosting.de (spamfilter06.heinlein-hosting.de [80.241.56.125]) (amavisd-new, port 10030) with ESMTP id iJ7PULiraicw; Wed, 1 May 2019 22:45:33 +0200 (CEST) From: Hauke Mehrtens To: davem@davemloft.net Cc: andrew@lunn.ch, vivien.didelot@gmail.com, f.fainelli@gmail.com, netdev@vger.kernel.org, Hauke Mehrtens Subject: [PATCH 4/5] net: dsa: lantiq: Add fast age function Date: Wed, 1 May 2019 22:45:05 +0200 Message-Id: <20190501204506.21579-5-hauke@hauke-m.de> In-Reply-To: <20190501204506.21579-1-hauke@hauke-m.de> References: <20190501204506.21579-1-hauke@hauke-m.de> MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Fast aging per port is not supported directly by the hardware, it is only possible to configure a global aging time. Do the fast aging by iterating over the MAC forwarding table and remove all dynamic entries for a given port. Signed-off-by: Hauke Mehrtens --- drivers/net/dsa/lantiq_gswip.c | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c index 23686b8399ea..ef8e2a827e16 100644 --- a/drivers/net/dsa/lantiq_gswip.c +++ b/drivers/net/dsa/lantiq_gswip.c @@ -1221,6 +1221,43 @@ static int gswip_port_vlan_del(struct dsa_switch *ds, int port, return 0; } +static void gswip_port_fast_age(struct dsa_switch *ds, int port) +{ + struct gswip_priv *priv = ds->priv; + struct gswip_pce_table_entry mac_bridge = {0,}; + int i; + int err; + + for (i = 0; i < 2048; i++) { + mac_bridge.table = GSWIP_TABLE_MAC_BRIDGE; + mac_bridge.index = i; + + err = gswip_pce_table_entry_read(priv, &mac_bridge); + if (err) { + dev_err(priv->dev, "failed to read mac brigde: %d\n", + err); + return; + } + + if (!mac_bridge.valid) + continue; + + if (mac_bridge.val[1] & 0x01) + continue; + + if (((mac_bridge.val[0] & GENMASK(7, 4)) >> 4) != port) + continue; + + mac_bridge.valid = false; + err = gswip_pce_table_entry_write(priv, &mac_bridge); + if (err) { + dev_err(priv->dev, "failed to write mac brigde: %d\n", + err); + return; + } + } +} + static void gswip_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) { struct gswip_priv *priv = ds->priv; @@ -1461,6 +1498,7 @@ static const struct dsa_switch_ops gswip_switch_ops = { .port_disable = gswip_port_disable, .port_bridge_join = gswip_port_bridge_join, .port_bridge_leave = gswip_port_bridge_leave, + .port_fast_age = gswip_port_fast_age, .port_vlan_filtering = gswip_port_vlan_filtering, .port_vlan_prepare = gswip_port_vlan_prepare, .port_vlan_add = gswip_port_vlan_add, From patchwork Wed May 1 20:45:06 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hauke Mehrtens X-Patchwork-Id: 1093828 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=hauke-m.de Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 44vVls2Tn5z9sBr for ; Thu, 2 May 2019 06:45:45 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726268AbfEAUpo (ORCPT ); Wed, 1 May 2019 16:45:44 -0400 Received: from mx1.mailbox.org ([80.241.60.212]:62982 "EHLO mx1.mailbox.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726088AbfEAUpl (ORCPT ); Wed, 1 May 2019 16:45:41 -0400 Received: from smtp1.mailbox.org (smtp1.mailbox.org [80.241.60.240]) (using TLSv1.2 with cipher ECDHE-RSA-CHACHA20-POLY1305 (256/256 bits)) (No client certificate requested) by mx1.mailbox.org (Postfix) with ESMTPS id D839F4CDA0; Wed, 1 May 2019 22:45:39 +0200 (CEST) X-Virus-Scanned: amavisd-new at heinlein-support.de Received: from smtp1.mailbox.org ([80.241.60.240]) by spamfilter05.heinlein-hosting.de (spamfilter05.heinlein-hosting.de [80.241.56.123]) (amavisd-new, port 10030) with ESMTP id g3aqUjmZi80a; Wed, 1 May 2019 22:45:33 +0200 (CEST) From: Hauke Mehrtens To: davem@davemloft.net Cc: andrew@lunn.ch, vivien.didelot@gmail.com, f.fainelli@gmail.com, netdev@vger.kernel.org, Hauke Mehrtens Subject: [PATCH 5/5] net: dsa: lantiq: Add Forwarding Database access Date: Wed, 1 May 2019 22:45:06 +0200 Message-Id: <20190501204506.21579-6-hauke@hauke-m.de> In-Reply-To: <20190501204506.21579-1-hauke@hauke-m.de> References: <20190501204506.21579-1-hauke@hauke-m.de> MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org This adds functions to add and remove static entries to and from the forwarding database and dump the full forwarding database. Signed-off-by: Hauke Mehrtens --- drivers/net/dsa/lantiq_gswip.c | 98 ++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c index ef8e2a827e16..83371394d6ed 100644 --- a/drivers/net/dsa/lantiq_gswip.c +++ b/drivers/net/dsa/lantiq_gswip.c @@ -1289,6 +1289,101 @@ static void gswip_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) GSWIP_PCE_PCTRL_0p(port)); } +static int gswip_port_fdb(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, bool add) +{ + struct gswip_priv *priv = ds->priv; + struct net_device *bridge = dsa_to_port(ds, port)->bridge_dev; + struct gswip_pce_table_entry mac_bridge = {0,}; + unsigned int cpu_port = priv->hw_info->cpu_port; + int fid = -1; + int i; + int err; + + if (!bridge) + return -EINVAL; + + for (i = cpu_port; i < ARRAY_SIZE(priv->vlans); i++) { + if (priv->vlans[i].bridge == bridge) { + fid = priv->vlans[i].fid; + break; + } + } + + if (fid == -1) { + dev_err(priv->dev, "Port not part of a bridge\n"); + return -EINVAL; + } + + mac_bridge.table = 0x0b; + mac_bridge.key_mode = true; + mac_bridge.key[0] = addr[5] | (addr[4] << 8); + mac_bridge.key[1] = addr[3] | (addr[2] << 8); + mac_bridge.key[2] = addr[1] | (addr[0] << 8); + mac_bridge.key[3] = fid; + mac_bridge.val[0] = add ? BIT(port) : 0; /* port map */ + mac_bridge.val[1] = 0x01; /* static entry */ + mac_bridge.valid = add; + + err = gswip_pce_table_entry_write(priv, &mac_bridge); + if (err) + dev_err(priv->dev, "failed to write mac brigde: %d\n", err); + + return err; +} + +static int gswip_port_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) +{ + return gswip_port_fdb(ds, port, addr, vid, true); +} + +static int gswip_port_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) +{ + return gswip_port_fdb(ds, port, addr, vid, false); +} + +static int gswip_port_fdb_dump(struct dsa_switch *ds, int port, + dsa_fdb_dump_cb_t *cb, void *data) +{ + struct gswip_priv *priv = ds->priv; + struct gswip_pce_table_entry mac_bridge = {0,}; + unsigned char addr[6]; + int i; + int err; + + for (i = 0; i < 2048; i++) { + mac_bridge.table = 0x0b; + mac_bridge.index = i; + + err = gswip_pce_table_entry_read(priv, &mac_bridge); + if (err) { + dev_err(priv->dev, "failed to write mac brigde: %d\n", + err); + return err; + } + + if (!mac_bridge.valid) + continue; + + addr[5] = mac_bridge.key[0] & 0xff; + addr[4] = (mac_bridge.key[0] >> 8) & 0xff; + addr[3] = mac_bridge.key[1] & 0xff; + addr[2] = (mac_bridge.key[1] >> 8) & 0xff; + addr[1] = mac_bridge.key[2] & 0xff; + addr[0] = (mac_bridge.key[2] >> 8) & 0xff; + if (mac_bridge.val[1] & 0x01) { + if (mac_bridge.val[0] & BIT(port)) + cb(addr, 0, true, data); + } else { + if (((mac_bridge.val[0] & GENMASK(7, 4)) >> 4) == port) + cb(addr, 0, false, data); + } + } + return 0; +} + static void gswip_phylink_validate(struct dsa_switch *ds, int port, unsigned long *supported, struct phylink_link_state *state) @@ -1504,6 +1599,9 @@ static const struct dsa_switch_ops gswip_switch_ops = { .port_vlan_add = gswip_port_vlan_add, .port_vlan_del = gswip_port_vlan_del, .port_stp_state_set = gswip_port_stp_state_set, + .port_fdb_add = gswip_port_fdb_add, + .port_fdb_del = gswip_port_fdb_del, + .port_fdb_dump = gswip_port_fdb_dump, .phylink_validate = gswip_phylink_validate, .phylink_mac_config = gswip_phylink_mac_config, .phylink_mac_link_down = gswip_phylink_mac_link_down,