From nobody Sat Apr 18 06:01:14 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5EC1DCCA479 for ; Sat, 16 Jul 2022 17:50:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232046AbiGPRui (ORCPT ); Sat, 16 Jul 2022 13:50:38 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51004 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231968AbiGPRud (ORCPT ); Sat, 16 Jul 2022 13:50:33 -0400 Received: from mail-wr1-x42f.google.com (mail-wr1-x42f.google.com [IPv6:2a00:1450:4864:20::42f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7FD961D0C7; Sat, 16 Jul 2022 10:50:31 -0700 (PDT) Received: by mail-wr1-x42f.google.com with SMTP id f2so10939023wrr.6; Sat, 16 Jul 2022 10:50:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=yFsn95Q1QkjD7u/Gn/6AXxf5eXuxr5kizQJ1Gj9EiVs=; b=IGnCyZfnWmSoZh/oLkNxMASW9EnlPzqo529N4ZSUVkNjUihKpufKXcsX0HIKGhqa5o CcjWgPx3x2ZmX3iBwbMerTnlwdM7tINBJeVsx50Qxms35mqPszkEJ++yFpF9915+B54X bHPzhOuRhoVvn1zHhxDwOoCfkF9pWd84FNIxZ8b0i32qgy48Tn2ST7LJyFyG2djHYPBT BZPttQ2Ai0Sit8Fb0J5DRqT0aS7ScLhfEJiwxs611KIvDIB3qwrjVo9H5yj5/AEqJYPl 9vW/oUGraXuA5uRdQ7RORanN79y9gdSuLJKWRjkoCWivjD/pGXckUan2WuHaAdE6LN1Y yJug== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=yFsn95Q1QkjD7u/Gn/6AXxf5eXuxr5kizQJ1Gj9EiVs=; b=T4wroJH/HXLD9vu2kNd/7wnrCwDWdVN5a8IH/EvRaDjDCOJU4zYzrabOLW3XqOWDBg iMtdSMrMpf+tctfB9xDAs/qVAbHPLRjqd/tcTa3e87V+y6ozAf4RTjUKxN3A98ZyDpen q7R/HRxvsih0Pa2zqHJvoTmSpeCEmclP145kzSUGvvzQYZ8Nwwfi67RZCMUAut87zEVA ITQI2vN8LLzuBNf1QqbSBKdqTx64zLxDXMmDqOFbQqhsLeNZYte99D7o/3a3ZmQ7GJYz 6Oxi8x4Lu5SrM4wY9TfLOjBVtLXF3Xh6tTWnAfBrHhKya3gpMpy6Pa72BchDet70qjnk l+HA== X-Gm-Message-State: AJIora+0+c1NHsiZN3QnraVGOY9iy6O911IuROU2Obchl9bk8cW74VP+ Uf9feAVAVZuTXrMmW7VDzxw= X-Google-Smtp-Source: AGRyM1ssgw+6ldktfBS03azkyjhH8gnzlZc4HqxauKqCeZbup4l00IHOgOFY6AUBWHOTSDC2wm9c7A== X-Received: by 2002:adf:b1d9:0:b0:21d:99c3:f5ca with SMTP id r25-20020adfb1d9000000b0021d99c3f5camr16828828wra.45.1657993829717; Sat, 16 Jul 2022 10:50:29 -0700 (PDT) Received: from localhost.localdomain (93-42-70-190.ip85.fastwebnet.it. [93.42.70.190]) by smtp.googlemail.com with ESMTPSA id s6-20020adfecc6000000b0021d74906683sm6836108wro.28.2022.07.16.10.50.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 16 Jul 2022 10:50:29 -0700 (PDT) From: Christian Marangi To: Andrew Lunn , Vivien Didelot , Florian Fainelli , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Russell King , Greg Kroah-Hartman , Jens Axboe , Christian Marangi , linux-kernel@vger.kernel.org, netdev@vger.kernel.org Subject: [net-next RFC PATCH 1/4] net: dsa: qca8k: drop qca8k_read/write/rmw for regmap variant Date: Sat, 16 Jul 2022 19:49:55 +0200 Message-Id: <20220716174958.22542-2-ansuelsmth@gmail.com> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220716174958.22542-1-ansuelsmth@gmail.com> References: <20220716174958.22542-1-ansuelsmth@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" In preparation for code split, drop the remaining qca8k_read/write/rmw and use regmap helper directly. Signed-off-by: Christian Marangi --- drivers/net/dsa/qca/qca8k.c | 206 +++++++++++++++++------------------- 1 file changed, 95 insertions(+), 111 deletions(-) diff --git a/drivers/net/dsa/qca/qca8k.c b/drivers/net/dsa/qca/qca8k.c index 1cbb05b0323f..2d34e15c2e6f 100644 --- a/drivers/net/dsa/qca/qca8k.c +++ b/drivers/net/dsa/qca/qca8k.c @@ -184,24 +184,6 @@ qca8k_set_page(struct qca8k_priv *priv, u16 page) return 0; } =20 -static int -qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val) -{ - return regmap_read(priv->regmap, reg, val); -} - -static int -qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val) -{ - return regmap_write(priv->regmap, reg, val); -} - -static int -qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) -{ - return regmap_update_bits(priv->regmap, reg, mask, write_val); -} - static void qca8k_rw_reg_ack_handler(struct dsa_switch *ds, struct sk_buff= *skb) { struct qca8k_mgmt_eth_data *mgmt_eth_data; @@ -647,7 +629,7 @@ qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fd= b_cmd cmd, int port) } =20 /* Write the function register triggering the table access */ - ret =3D qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg); + ret =3D regmap_write(priv->regmap, QCA8K_REG_ATU_FUNC, reg); if (ret) return ret; =20 @@ -658,7 +640,7 @@ qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fd= b_cmd cmd, int port) =20 /* Check for table full violation when adding an entry */ if (cmd =3D=3D QCA8K_FDB_LOAD) { - ret =3D qca8k_read(priv, QCA8K_REG_ATU_FUNC, ®); + ret =3D regmap_read(priv->regmap, QCA8K_REG_ATU_FUNC, ®); if (ret < 0) return ret; if (reg & QCA8K_ATU_FUNC_FULL) @@ -803,7 +785,7 @@ qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_v= lan_cmd cmd, u16 vid) reg |=3D FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid); =20 /* Write the function register triggering the table access */ - ret =3D qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg); + ret =3D regmap_write(priv->regmap, QCA8K_REG_VTU_FUNC1, reg); if (ret) return ret; =20 @@ -814,7 +796,7 @@ qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_v= lan_cmd cmd, u16 vid) =20 /* Check for table full violation when adding an entry */ if (cmd =3D=3D QCA8K_VLAN_LOAD) { - ret =3D qca8k_read(priv, QCA8K_REG_VTU_FUNC1, ®); + ret =3D regmap_read(priv->regmap, QCA8K_REG_VTU_FUNC1, ®); if (ret < 0) return ret; if (reg & QCA8K_VTU_FUNC1_FULL) @@ -842,7 +824,7 @@ qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vi= d, bool untagged) if (ret < 0) goto out; =20 - ret =3D qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); + ret =3D regmap_read(priv->regmap, QCA8K_REG_VTU_FUNC0, ®); if (ret < 0) goto out; reg |=3D QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN; @@ -852,7 +834,7 @@ qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vi= d, bool untagged) else reg |=3D QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port); =20 - ret =3D qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); + ret =3D regmap_write(priv->regmap, QCA8K_REG_VTU_FUNC0, reg); if (ret) goto out; ret =3D qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); @@ -875,7 +857,7 @@ qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vi= d) if (ret < 0) goto out; =20 - ret =3D qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); + ret =3D regmap_read(priv->regmap, QCA8K_REG_VTU_FUNC0, ®); if (ret < 0) goto out; reg &=3D ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); @@ -895,7 +877,7 @@ qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vi= d) if (del) { ret =3D qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid); } else { - ret =3D qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); + ret =3D regmap_write(priv->regmap, QCA8K_REG_VTU_FUNC0, reg); if (ret) goto out; ret =3D qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); @@ -928,7 +910,7 @@ qca8k_mib_init(struct qca8k_priv *priv) if (ret) goto exit; =20 - ret =3D qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB); + ret =3D regmap_write(priv->regmap, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_M= IB); =20 exit: mutex_unlock(&priv->reg_mutex); @@ -1434,10 +1416,10 @@ qca8k_setup_mac_pwr_sel(struct qca8k_priv *priv) mask |=3D QCA8K_MAC_PWR_RGMII1_1_8V; =20 if (mask) { - ret =3D qca8k_rmw(priv, QCA8K_REG_MAC_PWR_SEL, - QCA8K_MAC_PWR_RGMII0_1_8V | - QCA8K_MAC_PWR_RGMII1_1_8V, - mask); + ret =3D regmap_update_bits(priv->regmap, QCA8K_REG_MAC_PWR_SEL, + QCA8K_MAC_PWR_RGMII0_1_8V | + QCA8K_MAC_PWR_RGMII1_1_8V, + mask); } =20 return ret; @@ -1478,8 +1460,8 @@ qca8k_setup_of_pws_reg(struct qca8k_priv *priv) if (data->reduced_package) val |=3D QCA8327_PWS_PACKAGE148_EN; =20 - ret =3D qca8k_rmw(priv, QCA8K_REG_PWS, QCA8327_PWS_PACKAGE148_EN, - val); + ret =3D regmap_update_bits(priv->regmap, QCA8K_REG_PWS, + QCA8327_PWS_PACKAGE148_EN, val); if (ret) return ret; } @@ -1496,7 +1478,7 @@ qca8k_setup_of_pws_reg(struct qca8k_priv *priv) val |=3D QCA8K_PWS_LED_OPEN_EN_CSR; } =20 - return qca8k_rmw(priv, QCA8K_REG_PWS, + return regmap_update_bits(priv->regmap, QCA8K_REG_PWS, QCA8K_PWS_LED_OPEN_EN_CSR | QCA8K_PWS_POWER_ON_SEL, val); } @@ -1629,12 +1611,12 @@ qca8k_mac_config_setup_internal_delay(struct qca8k_= priv *priv, int cpu_port_inde } =20 /* Set RGMII delay based on the selected values */ - ret =3D qca8k_rmw(priv, reg, - QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK | - QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK | - QCA8K_PORT_PAD_RGMII_TX_DELAY_EN | - QCA8K_PORT_PAD_RGMII_RX_DELAY_EN, - val); + ret =3D regmap_update_bits(priv->regmap, reg, + QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK | + QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK | + QCA8K_PORT_PAD_RGMII_TX_DELAY_EN | + QCA8K_PORT_PAD_RGMII_RX_DELAY_EN, + val); if (ret) dev_err(priv->dev, "Failed to set internal delay for CPU port%d", cpu_port_index =3D=3D QCA8K_CPU_PORT0 ? 0 : 6); @@ -1723,7 +1705,7 @@ qca8k_phylink_mac_config(struct dsa_switch *ds, int p= ort, unsigned int mode, case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_TXID: case PHY_INTERFACE_MODE_RGMII_RXID: - qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN); + regmap_write(priv->regmap, reg, QCA8K_PORT_PAD_RGMII_EN); =20 /* Configure rgmii delay */ qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg); @@ -1733,13 +1715,13 @@ qca8k_phylink_mac_config(struct dsa_switch *ds, int= port, unsigned int mode, * rather than individual port registers. */ if (priv->switch_id =3D=3D QCA8K_ID_QCA8337) - qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL, - QCA8K_PORT_PAD_RGMII_RX_DELAY_EN); + regmap_write(priv->regmap, QCA8K_REG_PORT5_PAD_CTRL, + QCA8K_PORT_PAD_RGMII_RX_DELAY_EN); break; case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_1000BASEX: /* Enable SGMII on the port */ - qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN); + regmap_write(priv->regmap, reg, QCA8K_PORT_PAD_SGMII_EN); break; default: dev_err(ds->dev, "xMII mode %s not supported for port %d\n", @@ -1832,7 +1814,7 @@ qca8k_phylink_mac_link_up(struct dsa_switch *ds, int = port, unsigned int mode, =20 reg |=3D QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; =20 - qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg); + regmap_write(priv->regmap, QCA8K_REG_PORT_STATUS(port), reg); } =20 static struct qca8k_pcs *pcs_to_qca8k_pcs(struct phylink_pcs *pcs) @@ -1848,7 +1830,7 @@ static void qca8k_pcs_get_state(struct phylink_pcs *p= cs, u32 reg; int ret; =20 - ret =3D qca8k_read(priv, QCA8K_REG_PORT_STATUS(port), ®); + ret =3D regmap_read(priv->regmap, QCA8K_REG_PORT_STATUS(port), ®); if (ret < 0) { state->link =3D false; return; @@ -1908,17 +1890,17 @@ static int qca8k_pcs_config(struct phylink_pcs *pcs= , unsigned int mode, } =20 /* Enable/disable SerDes auto-negotiation as necessary */ - ret =3D qca8k_read(priv, QCA8K_REG_PWS, &val); + ret =3D regmap_read(priv->regmap, QCA8K_REG_PWS, &val); if (ret) return ret; if (phylink_autoneg_inband(mode)) val &=3D ~QCA8K_PWS_SERDES_AEN_DIS; else val |=3D QCA8K_PWS_SERDES_AEN_DIS; - qca8k_write(priv, QCA8K_REG_PWS, val); + regmap_write(priv->regmap, QCA8K_REG_PWS, val); =20 /* Configure the SGMII parameters */ - ret =3D qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val); + ret =3D regmap_read(priv->regmap, QCA8K_REG_SGMII_CTRL, &val); if (ret) return ret; =20 @@ -1940,7 +1922,7 @@ static int qca8k_pcs_config(struct phylink_pcs *pcs, = unsigned int mode, val |=3D QCA8K_SGMII_MODE_CTRL_BASEX; } =20 - qca8k_write(priv, QCA8K_REG_SGMII_CTRL, val); + regmap_write(priv->regmap, QCA8K_REG_SGMII_CTRL, val); =20 /* From original code is reported port instability as SGMII also * require delay set. Apply advised values here or take them from DT. @@ -1964,10 +1946,10 @@ static int qca8k_pcs_config(struct phylink_pcs *pcs= , unsigned int mode, val |=3D QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE; =20 if (val) - ret =3D qca8k_rmw(priv, reg, - QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE | - QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE, - val); + ret =3D regmap_update_bits(priv->regmap, reg, + QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE | + QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE, + val); =20 return 0; } @@ -2122,12 +2104,12 @@ qca8k_get_ethtool_stats(struct dsa_switch *ds, int = port, mib =3D &ar8327_mib[i]; reg =3D QCA8K_PORT_MIB_COUNTER(port) + mib->offset; =20 - ret =3D qca8k_read(priv, reg, &val); + ret =3D regmap_read(priv->regmap, reg, &val); if (ret < 0) continue; =20 if (mib->size =3D=3D 2) { - ret =3D qca8k_read(priv, reg + 4, &hi); + ret =3D regmap_read(priv->regmap, reg + 4, &hi); if (ret < 0) continue; } @@ -2161,7 +2143,7 @@ qca8k_set_mac_eee(struct dsa_switch *ds, int port, st= ruct ethtool_eee *eee) int ret; =20 mutex_lock(&priv->reg_mutex); - ret =3D qca8k_read(priv, QCA8K_REG_EEE_CTRL, ®); + ret =3D regmap_read(priv->regmap, QCA8K_REG_EEE_CTRL, ®); if (ret < 0) goto exit; =20 @@ -2169,7 +2151,7 @@ qca8k_set_mac_eee(struct dsa_switch *ds, int port, st= ruct ethtool_eee *eee) reg |=3D lpi_en; else reg &=3D ~lpi_en; - ret =3D qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg); + ret =3D regmap_write(priv->regmap, QCA8K_REG_EEE_CTRL, reg); =20 exit: mutex_unlock(&priv->reg_mutex); @@ -2208,8 +2190,8 @@ qca8k_port_stp_state_set(struct dsa_switch *ds, int p= ort, u8 state) break; } =20 - qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), - QCA8K_PORT_LOOKUP_STATE_MASK, stp_state); + regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), + QCA8K_PORT_LOOKUP_STATE_MASK, stp_state); } =20 static int qca8k_port_bridge_join(struct dsa_switch *ds, int port, @@ -2242,8 +2224,8 @@ static int qca8k_port_bridge_join(struct dsa_switch *= ds, int port, } =20 /* Add all other ports to this ports portvlan mask */ - ret =3D qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), - QCA8K_PORT_LOOKUP_MEMBER, port_mask); + ret =3D regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), + QCA8K_PORT_LOOKUP_MEMBER, port_mask); =20 return ret; } @@ -2272,8 +2254,8 @@ static void qca8k_port_bridge_leave(struct dsa_switch= *ds, int port, /* Set the cpu port to be the only one in the portvlan mask of * this port */ - qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), - QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port)); + regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), + QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port)); } =20 static void @@ -2357,7 +2339,7 @@ qca8k_port_change_mtu(struct dsa_switch *ds, int port= , int new_mtu) qca8k_port_set_status(priv, 6, 0); =20 /* Include L2 header / FCS length */ - ret =3D qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_= FCS_LEN); + ret =3D regmap_write(priv->regmap, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HL= EN + ETH_FCS_LEN); =20 if (priv->port_enabled_map & BIT(0)) qca8k_port_set_status(priv, 0, 1); @@ -2560,13 +2542,13 @@ qca8k_port_vlan_filtering(struct dsa_switch *ds, in= t port, bool vlan_filtering, int ret; =20 if (vlan_filtering) { - ret =3D qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), - QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, - QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE); + ret =3D regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), + QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, + QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE); } else { - ret =3D qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), - QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, - QCA8K_PORT_LOOKUP_VLAN_MODE_NONE); + ret =3D regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), + QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, + QCA8K_PORT_LOOKUP_VLAN_MODE_NONE); } =20 return ret; @@ -2589,15 +2571,15 @@ qca8k_port_vlan_add(struct dsa_switch *ds, int port, } =20 if (pvid) { - ret =3D qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port), - QCA8K_EGREES_VLAN_PORT_MASK(port), - QCA8K_EGREES_VLAN_PORT(port, vlan->vid)); + ret =3D regmap_update_bits(priv->regmap, QCA8K_EGRESS_VLAN(port), + QCA8K_EGREES_VLAN_PORT_MASK(port), + QCA8K_EGREES_VLAN_PORT(port, vlan->vid)); if (ret) return ret; =20 - ret =3D qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port), - QCA8K_PORT_VLAN_CVID(vlan->vid) | - QCA8K_PORT_VLAN_SVID(vlan->vid)); + ret =3D regmap_write(priv->regmap, QCA8K_REG_PORT_VLAN_CTRL0(port), + QCA8K_PORT_VLAN_CVID(vlan->vid) | + QCA8K_PORT_VLAN_SVID(vlan->vid)); } =20 return ret; @@ -2905,16 +2887,18 @@ qca8k_setup(struct dsa_switch *ds) /* Initial setup of all ports */ for (i =3D 0; i < QCA8K_NUM_PORTS; i++) { /* Disable forwarding by default on all ports */ - ret =3D qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), - QCA8K_PORT_LOOKUP_MEMBER, 0); + ret =3D regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i), + QCA8K_PORT_LOOKUP_MEMBER, 0); if (ret) return ret; =20 /* Enable QCA header mode on all cpu ports */ if (dsa_is_cpu_port(ds, i)) { - ret =3D qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i), - FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) | - FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL)); + ret =3D regmap_write(priv->regmap, QCA8K_REG_PORT_HDR_CTRL(i), + FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, + QCA8K_PORT_HDR_CTRL_ALL) | + FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, + QCA8K_PORT_HDR_CTRL_ALL)); if (ret) { dev_err(priv->dev, "failed enabling QCA header mode"); return ret; @@ -2930,11 +2914,11 @@ qca8k_setup(struct dsa_switch *ds) * Notice that in multi-cpu config only one port should be set * for igmp, unknown, multicast and broadcast packet */ - ret =3D qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1, - FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(cpu_port)) | - FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(cpu_port)) | - FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(cpu_port)) | - FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(cpu_port))); + ret =3D regmap_write(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL1, + FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(cpu_port)) | + FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(cpu_port)) | + FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(cpu_port)) | + FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(cpu_port))); if (ret) return ret; =20 @@ -2944,17 +2928,17 @@ qca8k_setup(struct dsa_switch *ds) for (i =3D 0; i < QCA8K_NUM_PORTS; i++) { /* CPU port gets connected to all user ports of the switch */ if (dsa_is_cpu_port(ds, i)) { - ret =3D qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), - QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); + ret =3D regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i), + QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); if (ret) return ret; } =20 /* Individual user ports get connected to CPU port only */ if (dsa_is_user_port(ds, i)) { - ret =3D qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), - QCA8K_PORT_LOOKUP_MEMBER, - BIT(cpu_port)); + ret =3D regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i), + QCA8K_PORT_LOOKUP_MEMBER, + BIT(cpu_port)); if (ret) return ret; =20 @@ -2967,15 +2951,15 @@ qca8k_setup(struct dsa_switch *ds) /* For port based vlans to work we need to set the * default egress vid */ - ret =3D qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i), - QCA8K_EGREES_VLAN_PORT_MASK(i), - QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF)); + ret =3D regmap_update_bits(priv->regmap, QCA8K_EGRESS_VLAN(i), + QCA8K_EGREES_VLAN_PORT_MASK(i), + QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF)); if (ret) return ret; =20 - ret =3D qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i), - QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) | - QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF)); + ret =3D regmap_write(priv->regmap, QCA8K_REG_PORT_VLAN_CTRL0(i), + QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) | + QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF)); if (ret) return ret; } @@ -3009,18 +2993,18 @@ qca8k_setup(struct dsa_switch *ds) QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) | QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19); } - qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask); + regmap_write(priv->regmap, QCA8K_REG_PORT_HOL_CTRL0(i), mask); =20 mask =3D QCA8K_PORT_HOL_CTRL1_ING(0x6) | QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | QCA8K_PORT_HOL_CTRL1_WRED_EN; - qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i), - QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK | - QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | - QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | - QCA8K_PORT_HOL_CTRL1_WRED_EN, - mask); + regmap_update_bits(priv->regmap, QCA8K_REG_PORT_HOL_CTRL1(i), + QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK | + QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | + QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | + QCA8K_PORT_HOL_CTRL1_WRED_EN, + mask); } } =20 @@ -3028,14 +3012,14 @@ qca8k_setup(struct dsa_switch *ds) if (priv->switch_id =3D=3D QCA8K_ID_QCA8327) { mask =3D QCA8K_GLOBAL_FC_GOL_XON_THRES(288) | QCA8K_GLOBAL_FC_GOL_XOFF_THRES(496); - qca8k_rmw(priv, QCA8K_REG_GLOBAL_FC_THRESH, - QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK | - QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK, - mask); + regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FC_THRESH, + QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK | + QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK, + mask); } =20 /* Setup our port MTUs to match power on defaults */ - ret =3D qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_L= EN); + ret =3D regmap_write(priv->regmap, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + = ETH_FCS_LEN); if (ret) dev_warn(priv->dev, "failed setting MTU settings"); =20 @@ -3103,7 +3087,7 @@ static int qca8k_read_switch_id(struct qca8k_priv *pr= iv) if (!data) return -ENODEV; =20 - ret =3D qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val); + ret =3D regmap_read(priv->regmap, QCA8K_REG_MASK_CTRL, &val); if (ret < 0) return -ENODEV; =20 --=20 2.36.1 From nobody Sat Apr 18 06:01:14 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5AADAC433EF for ; Sat, 16 Jul 2022 17:50:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232145AbiGPRul (ORCPT ); Sat, 16 Jul 2022 13:50:41 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51038 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229473AbiGPRue (ORCPT ); Sat, 16 Jul 2022 13:50:34 -0400 Received: from mail-wm1-x32d.google.com (mail-wm1-x32d.google.com [IPv6:2a00:1450:4864:20::32d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E63CA1D0C6; Sat, 16 Jul 2022 10:50:32 -0700 (PDT) Received: by mail-wm1-x32d.google.com with SMTP id p26-20020a1c545a000000b003a2fb7c1274so2329338wmi.1; Sat, 16 Jul 2022 10:50:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=3JiWc/5hqj/vkIVmgQzXuREwrFKvIetqrvOYslMnRuI=; b=accTHYloOQtzVnbH7VqXpPSEK5zADpmq+RMe1EtOCj+ftfj5Hd42ckOVIULLmM0W4v N1yVLcdu6qBYEiIFIJAfvwHlutD5rtxEsFNreZBP0OCeFSdw1Ia1b9ZH++ePjr0aG4bZ 9iDay5H7c0qitVTipUtMPoF5SmR3SudLS+PBmxUUw4kYvwWDiVuC7HoR/Avrf8xkXDtJ D/sVIMMGdrXYsPDdbVPGSEl5H8hcDJJY2sSuJts228aUwgyyi1hQTVv1zRm0hrnS6kL3 hdPuOX3tB06FujaCkxa01HloIrypPWbqhhcxTh/EPAUyIP7ITMEnbe0UUkNOdwsnM3VW vB2g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=3JiWc/5hqj/vkIVmgQzXuREwrFKvIetqrvOYslMnRuI=; b=SDWG4Amy7bS6cD7ktwb2HzGNp+vHgW/TzBeZuXE2AWWTj/jBopweMKG96u4yeHGRHp q8Y+I4BFflG7R4vZPculls7KySzEKv5UYMbOgW6EEUT6haytKI2k9vYpfmlTCfVbIAEK Y8eF6dJWV4k5SIMMkYbOsOzyo72VYykOcFw51jQyBadQPTaYtgfuzHp0xbfF/zZjB7uh YEuKncOfBFwuqLzE9f74BfH4CY2Gp5No/9MXc8dU5Rw2CFoi5pwn9skxEBElBglKYEiX ui0Tkem6aU0pGSzVDnIVvrOoulfloYXNz1bSS17GHf4OWCZ6mBq1uhzj1u/Z7jgYNhyL Na5A== X-Gm-Message-State: AJIora83Br9lLWIBSxEj5ug3S3rqXrp4zkD3nnY3lO2IZxdR/7X4V83Z m5e2lbKSW24OCmnmZPP3whw= X-Google-Smtp-Source: AGRyM1vMZAocX6pL7g8VZCC5tT9KwvuKYD/DbtS2onTaWF7GLBYYOPFYGVuGzG4P0jAc80oL2MRZwg== X-Received: by 2002:a05:600c:a07:b0:39e:da6e:fc49 with SMTP id z7-20020a05600c0a0700b0039eda6efc49mr24450020wmp.143.1657993830805; Sat, 16 Jul 2022 10:50:30 -0700 (PDT) Received: from localhost.localdomain (93-42-70-190.ip85.fastwebnet.it. [93.42.70.190]) by smtp.googlemail.com with ESMTPSA id s6-20020adfecc6000000b0021d74906683sm6836108wro.28.2022.07.16.10.50.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 16 Jul 2022 10:50:30 -0700 (PDT) From: Christian Marangi To: Andrew Lunn , Vivien Didelot , Florian Fainelli , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Russell King , Greg Kroah-Hartman , Jens Axboe , Christian Marangi , linux-kernel@vger.kernel.org, netdev@vger.kernel.org Subject: [net-next RFC PATCH 2/4] net: dsa: qca8k: convert to regmap read/write API Date: Sat, 16 Jul 2022 19:49:56 +0200 Message-Id: <20220716174958.22542-3-ansuelsmth@gmail.com> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220716174958.22542-1-ansuelsmth@gmail.com> References: <20220716174958.22542-1-ansuelsmth@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Convert qca8k to regmap read/write bulk API. The mgmt eth can write up to 16 bytes of data at times. Currently we use a custom function to do it but regmap now supports declaration of read/write bulk even without a bus. Drop the custom function and rework the regmap function to this new implementation. Signed-off-by: Christian Marangi --- drivers/net/dsa/qca/qca8k.c | 133 ++++++++++++++++++++---------------- drivers/net/dsa/qca/qca8k.h | 2 + 2 files changed, 76 insertions(+), 59 deletions(-) diff --git a/drivers/net/dsa/qca/qca8k.c b/drivers/net/dsa/qca/qca8k.c index 2d34e15c2e6f..fd738d718cd6 100644 --- a/drivers/net/dsa/qca/qca8k.c +++ b/drivers/net/dsa/qca/qca8k.c @@ -394,53 +394,12 @@ qca8k_regmap_update_bits_eth(struct qca8k_priv *priv,= u32 reg, u32 mask, u32 wri } =20 static int -qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len) +qca8k_read_mii(struct qca8k_priv *priv, u32 reg, u32 *val) { - int i, count =3D len / sizeof(u32), ret; - - if (priv->mgmt_master && !qca8k_read_eth(priv, reg, val, len)) - return 0; - - for (i =3D 0; i < count; i++) { - ret =3D regmap_read(priv->regmap, reg + (i * 4), val + i); - if (ret < 0) - return ret; - } - - return 0; -} - -static int -qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len) -{ - int i, count =3D len / sizeof(u32), ret; - u32 tmp; - - if (priv->mgmt_master && !qca8k_write_eth(priv, reg, val, len)) - return 0; - - for (i =3D 0; i < count; i++) { - tmp =3D val[i]; - - ret =3D regmap_write(priv->regmap, reg + (i * 4), tmp); - if (ret < 0) - return ret; - } - - return 0; -} - -static int -qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val) -{ - struct qca8k_priv *priv =3D (struct qca8k_priv *)ctx; struct mii_bus *bus =3D priv->bus; u16 r1, r2, page; int ret; =20 - if (!qca8k_read_eth(priv, reg, val, sizeof(*val))) - return 0; - qca8k_split_addr(reg, &r1, &r2, &page); =20 mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); @@ -457,16 +416,12 @@ qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *= val) } =20 static int -qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val) +qca8k_write_mii(struct qca8k_priv *priv, u32 reg, u32 val) { - struct qca8k_priv *priv =3D (struct qca8k_priv *)ctx; struct mii_bus *bus =3D priv->bus; u16 r1, r2, page; int ret; =20 - if (!qca8k_write_eth(priv, reg, &val, sizeof(val))) - return 0; - qca8k_split_addr(reg, &r1, &r2, &page); =20 mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); @@ -483,17 +438,14 @@ qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t = val) } =20 static int -qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t = write_val) +qca8k_regmap_update_bits_mii(struct qca8k_priv *priv, u32 reg, + u32 mask, u32 write_val) { - struct qca8k_priv *priv =3D (struct qca8k_priv *)ctx; struct mii_bus *bus =3D priv->bus; u16 r1, r2, page; u32 val; int ret; =20 - if (!qca8k_regmap_update_bits_eth(priv, reg, mask, write_val)) - return 0; - qca8k_split_addr(reg, &r1, &r2, &page); =20 mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); @@ -516,6 +468,66 @@ qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint= 32_t mask, uint32_t write_ return ret; } =20 +static int +qca8k_bulk_read(void *ctx, const void *reg_buf, size_t reg_len, + void *val_buf, size_t val_len) +{ + struct qca8k_priv *priv =3D (struct qca8k_priv *)ctx; + int i, count =3D val_len / sizeof(u32), ret; + + if (priv->mgmt_master && !qca8k_read_eth(priv, *(u32 *)reg_buf, val_buf, = val_len)) + return 0; + + for (i =3D 0; i < count; i++) { + ret =3D qca8k_read_mii(priv, *(u32 *)reg_buf + (i * sizeof(u32)), val_bu= f + i); + if (ret < 0) + return ret; + } + + return 0; +} + +static int +qca8k_bulk_gather_write(void *ctx, const void *reg_buf, size_t reg_len, + const void *val_buf, size_t val_len) +{ + struct qca8k_priv *priv =3D (struct qca8k_priv *)ctx; + int i, count =3D val_len / sizeof(u32), ret; + u32 tmp; + + if (priv->mgmt_master && + !qca8k_write_eth(priv, *(u32 *)reg_buf, (u32 *)val_buf, val_len)) + return 0; + + for (i =3D 0; i < count; i++) { + tmp =3D *((u32 *)val_buf + i); + + ret =3D qca8k_write_mii(priv, *(u32 *)reg_buf + (i * sizeof(u32)), tmp); + if (ret < 0) + return ret; + } + + return 0; +} + +static int +qca8k_bulk_write(void *ctx, const void *data, size_t bytes) +{ + return qca8k_bulk_gather_write(ctx, data, sizeof(u32), data + sizeof(u32), + bytes - sizeof(u32)); +} + +static int +qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t = write_val) +{ + struct qca8k_priv *priv =3D (struct qca8k_priv *)ctx; + + if (!qca8k_regmap_update_bits_eth(priv, reg, mask, write_val)) + return 0; + + return qca8k_regmap_update_bits_mii(priv, reg, mask, write_val); +} + static const struct regmap_range qca8k_readable_ranges[] =3D { regmap_reg_range(0x0000, 0x00e4), /* Global control */ regmap_reg_range(0x0100, 0x0168), /* EEE control */ @@ -541,16 +553,18 @@ static const struct regmap_access_table qca8k_readabl= e_table =3D { }; =20 static struct regmap_config qca8k_regmap_config =3D { - .reg_bits =3D 16, + .reg_bits =3D 32, .val_bits =3D 32, .reg_stride =3D 4, .max_register =3D 0x16ac, /* end MIB - Port6 range */ - .reg_read =3D qca8k_regmap_read, - .reg_write =3D qca8k_regmap_write, + .read =3D qca8k_bulk_read, + .write =3D qca8k_bulk_write, .reg_update_bits =3D qca8k_regmap_update_bits, .rd_table =3D &qca8k_readable_table, .disable_locking =3D true, /* Locking is handled by qca8k read/write */ .cache_type =3D REGCACHE_NONE, /* Explicitly disable CACHE */ + .max_raw_read =3D 16, /* mgmt eth can read/write up to 4 bytes at times */ + .max_raw_write =3D 16, }; =20 static int @@ -565,11 +579,12 @@ qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32= mask) static int qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) { - u32 reg[3]; + u32 reg[QCA8K_ATU_TABLE_SIZE]; int ret; =20 /* load the ARL table into an array */ - ret =3D qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); + ret =3D regmap_bulk_read(priv->regmap, QCA8K_REG_ATU_DATA0, reg, + QCA8K_ATU_TABLE_SIZE); if (ret) return ret; =20 @@ -594,7 +609,7 @@ static void qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *= mac, u8 aging) { - u32 reg[3] =3D { 0 }; + u32 reg[QCA8K_ATU_TABLE_SIZE] =3D { 0 }; =20 /* vid - 83:72 */ reg[2] =3D FIELD_PREP(QCA8K_ATU_VID_MASK, vid); @@ -611,7 +626,7 @@ qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 po= rt_mask, const u8 *mac, reg[0] |=3D FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]); =20 /* load the array into the ARL table */ - qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); + regmap_bulk_write(priv->regmap, QCA8K_REG_ATU_DATA0, reg, QCA8K_ATU_TABLE= _SIZE); } =20 static int diff --git a/drivers/net/dsa/qca/qca8k.h b/drivers/net/dsa/qca/qca8k.h index ec58d0e80a70..22ece14e06dc 100644 --- a/drivers/net/dsa/qca/qca8k.h +++ b/drivers/net/dsa/qca/qca8k.h @@ -148,6 +148,8 @@ #define QCA8K_REG_IPV4_PRI_ADDR_MASK 0x474 =20 /* Lookup registers */ +#define QCA8K_ATU_TABLE_SIZE 3 /* 12 bytes wide table / sizeof(u32) */ + #define QCA8K_REG_ATU_DATA0 0x600 #define QCA8K_ATU_ADDR2_MASK GENMASK(31, 24) #define QCA8K_ATU_ADDR3_MASK GENMASK(23, 16) --=20 2.36.1 From nobody Sat Apr 18 06:01:14 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DB577C43334 for ; Sat, 16 Jul 2022 17:50:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232200AbiGPRup (ORCPT ); Sat, 16 Jul 2022 13:50:45 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51052 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231972AbiGPRue (ORCPT ); Sat, 16 Jul 2022 13:50:34 -0400 Received: from mail-wm1-x32c.google.com (mail-wm1-x32c.google.com [IPv6:2a00:1450:4864:20::32c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6CDFD1D0CF; Sat, 16 Jul 2022 10:50:33 -0700 (PDT) Received: by mail-wm1-x32c.google.com with SMTP id l68so4597006wml.3; Sat, 16 Jul 2022 10:50:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=7vXchobVIzGaJEwUA2pO21OnxTUgLKN30nvg+mcRjk0=; b=SFu6tqywaCfksRFRQ5EAGzJTWXnROYEVJipLaKK59mo761gyWyVjQatNxL3Ir3sQxj YEXVWUJo/VY4HTV+0nCjSxIgC7YKhENcqXTW2MRdI6LgJeoBApDdci00HG8QBJe9maCp lo249tMW1Aq2Zv9PALEM42IO08jVUokbhy4jVMcpJeZkDE+HKWIqgn7xtEYPVf7f9YUX DOyFchEvsz1P/J65tCn8HGrDDb/UZgrCDsMjSSK7Dmwz0gjwTJFyzgRSaOUmzOODgRfm t1tWd80V19Alc1M61zAD8HH2lKX1CifR25ObaX/yXYEJE/aShEjkVSy8Oo8mGZG+8gnH 4Jig== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=7vXchobVIzGaJEwUA2pO21OnxTUgLKN30nvg+mcRjk0=; b=xEAHNmWdY0B2/wPwLc2AAD7yKoUrB3CqH3TA1lWefws7abP9FZFfuwp59gKHDP64jP 9PRzzkv1fmUhJG1HrOL8zT1+e8WhHd/rUa+JBa3hvWyfR7HomcfV+lxDHEBGhb3koygQ Akn/G5qiLl8LM7GAbACsniuxaiYkHdIaEG0suv75okIIoVlQg4qjOahqNxisWmZeI+CA dQI9Yh6MWzTz50/L7mIMU4ONN5TiIt5oebtTsynHax54JUnRsy/nuYfCMgw3Tsjipcm3 ZoZAVG2utFZO90rVRTiQ0+3FFKaBwpNKB9ltMUtNyTut9ibTg/zJx8EtuBDHmVmUGjQP 3AJQ== X-Gm-Message-State: AJIora9dOhm44nknZGE3ymZx5I3GCa3FuZeEgCIUX7Wui9PCecXoFFF6 DjC2Yq2rkQ97XHVJ5wm63C8= X-Google-Smtp-Source: AGRyM1vvtjE1Sm1TAeNm1gZFpK46VoPUbQ4q9T3zk5HUcODZdrLGweV8UAd1kBuosyuwK7ErAY/Jrw== X-Received: by 2002:a7b:cb05:0:b0:3a1:8add:aaa5 with SMTP id u5-20020a7bcb05000000b003a18addaaa5mr25287615wmj.13.1657993831865; Sat, 16 Jul 2022 10:50:31 -0700 (PDT) Received: from localhost.localdomain (93-42-70-190.ip85.fastwebnet.it. [93.42.70.190]) by smtp.googlemail.com with ESMTPSA id s6-20020adfecc6000000b0021d74906683sm6836108wro.28.2022.07.16.10.50.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 16 Jul 2022 10:50:31 -0700 (PDT) From: Christian Marangi To: Andrew Lunn , Vivien Didelot , Florian Fainelli , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Russell King , Greg Kroah-Hartman , Jens Axboe , Christian Marangi , linux-kernel@vger.kernel.org, netdev@vger.kernel.org Subject: [net-next RFC PATCH 3/4] net: dsa: qca8k: rework mib autocast handling Date: Sat, 16 Jul 2022 19:49:57 +0200 Message-Id: <20220716174958.22542-4-ansuelsmth@gmail.com> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220716174958.22542-1-ansuelsmth@gmail.com> References: <20220716174958.22542-1-ansuelsmth@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" In preparation for code split, move the autocast mib function used to receive mib data from eth packet in priv struct and use that in get_ethtool_stats instead of referencing the function directly. This is needed as the get_ethtool_stats function will be moved to a common file. Signed-off-by: Christian Marangi --- drivers/net/dsa/qca/qca8k.c | 7 +++++-- drivers/net/dsa/qca/qca8k.h | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/dsa/qca/qca8k.c b/drivers/net/dsa/qca/qca8k.c index fd738d718cd6..e527d15d5065 100644 --- a/drivers/net/dsa/qca/qca8k.c +++ b/drivers/net/dsa/qca/qca8k.c @@ -2109,8 +2109,8 @@ qca8k_get_ethtool_stats(struct dsa_switch *ds, int po= rt, u32 hi =3D 0; int ret; =20 - if (priv->mgmt_master && - qca8k_get_ethtool_stats_eth(ds, port, data) > 0) + if (priv->mgmt_master && priv->autocast_mib && + priv->autocast_mib(ds, port, data) > 0) return; =20 match_data =3D of_device_get_match_data(priv->dev); @@ -3177,6 +3177,9 @@ qca8k_sw_probe(struct mdio_device *mdiodev) mutex_init(&priv->mib_eth_data.mutex); init_completion(&priv->mib_eth_data.rw_done); =20 + /* Assign autocast_mib function as it's supported by this switch */ + priv->autocast_mib =3D &qca8k_get_ethtool_stats_eth; + priv->ds->dev =3D &mdiodev->dev; priv->ds->num_ports =3D QCA8K_NUM_PORTS; priv->ds->priv =3D priv; diff --git a/drivers/net/dsa/qca/qca8k.h b/drivers/net/dsa/qca/qca8k.h index 22ece14e06dc..a306638a7100 100644 --- a/drivers/net/dsa/qca/qca8k.h +++ b/drivers/net/dsa/qca/qca8k.h @@ -403,6 +403,7 @@ struct qca8k_priv { struct qca8k_mdio_cache mdio_cache; struct qca8k_pcs pcs_port_0; struct qca8k_pcs pcs_port_6; + int (*autocast_mib)(struct dsa_switch *ds, int port, u64 *data); }; =20 struct qca8k_mib_desc { --=20 2.36.1 From nobody Sat Apr 18 06:01:14 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 59CA0C43334 for ; Sat, 16 Jul 2022 17:50:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232273AbiGPRux (ORCPT ); Sat, 16 Jul 2022 13:50:53 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51210 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232094AbiGPRuj (ORCPT ); Sat, 16 Jul 2022 13:50:39 -0400 Received: from mail-wr1-x42d.google.com (mail-wr1-x42d.google.com [IPv6:2a00:1450:4864:20::42d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 50D551EC72; Sat, 16 Jul 2022 10:50:35 -0700 (PDT) Received: by mail-wr1-x42d.google.com with SMTP id e15so5708377wro.5; Sat, 16 Jul 2022 10:50:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=7tl82ckDgfUcIaha61NfPIcE18InYXONKJ7q5EvcRGc=; b=AnLzUhtARh9T/HSz7s4xdJZp/7mCc7f4cbW+ZO1eMiUfBjZ9BmHBJGUtaEVqDSyJzB iRo1MYoj3BDC15074BiTFgliP0qBbP6VfxcoP+mj/bVMfqgrxOK4clPzQ6z8/c4hmqtg /AyHvo2FwfWVXBCrcUyAdlAyJxq+j2/n1Gbcyclg/47V0jW8M/xpGt/Ti48oB3jQWUfF mDgzZ7NdIxja5F47rEql22IKegydC3O53hHkRtY1AhI9+HgCii84cAAiUa7DEaY5tsPs dUlP6W5ssz+snDne8bIAkTWvubsKh4d9LKfa7yGvfCY3BwikWhKKuDNhK5ByL+0SRssb l/tg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=7tl82ckDgfUcIaha61NfPIcE18InYXONKJ7q5EvcRGc=; b=DqPm8rtMct4U2jlNgRfuMRU+KcEoLu2BWT/rnlpJUqeQ5qKevVksnuBbse9GEiIBFu 1xQljvU8I5b34sPgo9b8k144D1DmgG4z3KqchITgpkVPIVklVtLprlpWwnWdPl9YdMBp D7lHSv8y3NHDGKK7mXGwLr+ulg3YSPt5vOaSmIrSiSR3aGmQsf2PUMRZGXTA9jD3oY6C 7/QHPcTHJJO9mmJTlO/xLMa3sSjTkvSCA+xs27hDfmDlBHwN7hEF0lq40N/nSJ82HTJY 0+2uzIIr4eDdMN7lTHHDA3UJuiE6SMQWEl8oYE20vBmHTRPTF4B/0oXZVg/vLNP0czro tFVg== X-Gm-Message-State: AJIora8QRe22WbKEymiidYVAbHvkWWNVtaBjq3SKVQxbsEBDCqgMmjig Vjet2HtmkTuRapa+ZBRPBAI= X-Google-Smtp-Source: AGRyM1vZlalV4VCBzwSr3JUn2zbdzX/s/XJruc5pvidcnc1cIeGN1V9kp9kRZqJum4I8gqMf1YsFyw== X-Received: by 2002:a5d:55d0:0:b0:21d:cf5a:7617 with SMTP id i16-20020a5d55d0000000b0021dcf5a7617mr6879026wrw.368.1657993833093; Sat, 16 Jul 2022 10:50:33 -0700 (PDT) Received: from localhost.localdomain (93-42-70-190.ip85.fastwebnet.it. [93.42.70.190]) by smtp.googlemail.com with ESMTPSA id s6-20020adfecc6000000b0021d74906683sm6836108wro.28.2022.07.16.10.50.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 16 Jul 2022 10:50:32 -0700 (PDT) From: Christian Marangi To: Andrew Lunn , Vivien Didelot , Florian Fainelli , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Russell King , Greg Kroah-Hartman , Jens Axboe , Christian Marangi , linux-kernel@vger.kernel.org, netdev@vger.kernel.org Subject: [net-next RFC PATCH 4/4] net: dsa: qca8k: split qca8k in common and 8xxx specific code Date: Sat, 16 Jul 2022 19:49:58 +0200 Message-Id: <20220716174958.22542-5-ansuelsmth@gmail.com> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220716174958.22542-1-ansuelsmth@gmail.com> References: <20220716174958.22542-1-ansuelsmth@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" The qca8k family reg structure is also used in the internal ipq40xx switch. Split qca8k common code from specific code for future implementation of ipq40xx internal switch based on qca8k. While at it also fix minor wrong format for comments and reallign function as we had to drop static declaration. Signed-off-by: Christian Marangi --- drivers/net/dsa/qca/Makefile | 1 + drivers/net/dsa/qca/{qca8k.c =3D> qca8k-8xxx.c} | 1210 +---------------- drivers/net/dsa/qca/qca8k-common.c | 1174 ++++++++++++++++ drivers/net/dsa/qca/qca8k.h | 58 + 4 files changed, 1245 insertions(+), 1198 deletions(-) rename drivers/net/dsa/qca/{qca8k.c =3D> qca8k-8xxx.c} (64%) create mode 100644 drivers/net/dsa/qca/qca8k-common.c diff --git a/drivers/net/dsa/qca/Makefile b/drivers/net/dsa/qca/Makefile index 40bb7c27285b..701f1d199e93 100644 --- a/drivers/net/dsa/qca/Makefile +++ b/drivers/net/dsa/qca/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_NET_DSA_AR9331) +=3D ar9331.o obj-$(CONFIG_NET_DSA_QCA8K) +=3D qca8k.o +qca8k-y +=3D qca8k-common.o qca8k-8xxx.o diff --git a/drivers/net/dsa/qca/qca8k.c b/drivers/net/dsa/qca/qca8k-8xxx.c similarity index 64% rename from drivers/net/dsa/qca/qca8k.c rename to drivers/net/dsa/qca/qca8k-8xxx.c index e527d15d5065..85d138fced66 100644 --- a/drivers/net/dsa/qca/qca8k.c +++ b/drivers/net/dsa/qca/qca8k-8xxx.c @@ -24,57 +24,6 @@ =20 #include "qca8k.h" =20 -#define MIB_DESC(_s, _o, _n) \ - { \ - .size =3D (_s), \ - .offset =3D (_o), \ - .name =3D (_n), \ - } - -static const struct qca8k_mib_desc ar8327_mib[] =3D { - MIB_DESC(1, 0x00, "RxBroad"), - MIB_DESC(1, 0x04, "RxPause"), - MIB_DESC(1, 0x08, "RxMulti"), - MIB_DESC(1, 0x0c, "RxFcsErr"), - MIB_DESC(1, 0x10, "RxAlignErr"), - MIB_DESC(1, 0x14, "RxRunt"), - MIB_DESC(1, 0x18, "RxFragment"), - MIB_DESC(1, 0x1c, "Rx64Byte"), - MIB_DESC(1, 0x20, "Rx128Byte"), - MIB_DESC(1, 0x24, "Rx256Byte"), - MIB_DESC(1, 0x28, "Rx512Byte"), - MIB_DESC(1, 0x2c, "Rx1024Byte"), - MIB_DESC(1, 0x30, "Rx1518Byte"), - MIB_DESC(1, 0x34, "RxMaxByte"), - MIB_DESC(1, 0x38, "RxTooLong"), - MIB_DESC(2, 0x3c, "RxGoodByte"), - MIB_DESC(2, 0x44, "RxBadByte"), - MIB_DESC(1, 0x4c, "RxOverFlow"), - MIB_DESC(1, 0x50, "Filtered"), - MIB_DESC(1, 0x54, "TxBroad"), - MIB_DESC(1, 0x58, "TxPause"), - MIB_DESC(1, 0x5c, "TxMulti"), - MIB_DESC(1, 0x60, "TxUnderRun"), - MIB_DESC(1, 0x64, "Tx64Byte"), - MIB_DESC(1, 0x68, "Tx128Byte"), - MIB_DESC(1, 0x6c, "Tx256Byte"), - MIB_DESC(1, 0x70, "Tx512Byte"), - MIB_DESC(1, 0x74, "Tx1024Byte"), - MIB_DESC(1, 0x78, "Tx1518Byte"), - MIB_DESC(1, 0x7c, "TxMaxByte"), - MIB_DESC(1, 0x80, "TxOverSize"), - MIB_DESC(2, 0x84, "TxByte"), - MIB_DESC(1, 0x8c, "TxCollision"), - MIB_DESC(1, 0x90, "TxAbortCol"), - MIB_DESC(1, 0x94, "TxMultiCol"), - MIB_DESC(1, 0x98, "TxSingleCol"), - MIB_DESC(1, 0x9c, "TxExcDefer"), - MIB_DESC(1, 0xa0, "TxDefer"), - MIB_DESC(1, 0xa4, "TxLateCol"), - MIB_DESC(1, 0xa8, "RXUnicast"), - MIB_DESC(1, 0xac, "TXUnicast"), -}; - static void qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) { @@ -528,30 +477,6 @@ qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint= 32_t mask, uint32_t write_ return qca8k_regmap_update_bits_mii(priv, reg, mask, write_val); } =20 -static const struct regmap_range qca8k_readable_ranges[] =3D { - regmap_reg_range(0x0000, 0x00e4), /* Global control */ - regmap_reg_range(0x0100, 0x0168), /* EEE control */ - regmap_reg_range(0x0200, 0x0270), /* Parser control */ - regmap_reg_range(0x0400, 0x0454), /* ACL */ - regmap_reg_range(0x0600, 0x0718), /* Lookup */ - regmap_reg_range(0x0800, 0x0b70), /* QM */ - regmap_reg_range(0x0c00, 0x0c80), /* PKT */ - regmap_reg_range(0x0e00, 0x0e98), /* L3 */ - regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */ - regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */ - regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */ - regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */ - regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */ - regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */ - regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */ - -}; - -static const struct regmap_access_table qca8k_readable_table =3D { - .yes_ranges =3D qca8k_readable_ranges, - .n_yes_ranges =3D ARRAY_SIZE(qca8k_readable_ranges), -}; - static struct regmap_config qca8k_regmap_config =3D { .reg_bits =3D 32, .val_bits =3D 32, @@ -567,386 +492,6 @@ static struct regmap_config qca8k_regmap_config =3D { .max_raw_write =3D 16, }; =20 -static int -qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) -{ - u32 val; - - return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0, - QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC); -} - -static int -qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) -{ - u32 reg[QCA8K_ATU_TABLE_SIZE]; - int ret; - - /* load the ARL table into an array */ - ret =3D regmap_bulk_read(priv->regmap, QCA8K_REG_ATU_DATA0, reg, - QCA8K_ATU_TABLE_SIZE); - if (ret) - return ret; - - /* vid - 83:72 */ - fdb->vid =3D FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]); - /* aging - 67:64 */ - fdb->aging =3D FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]); - /* portmask - 54:48 */ - fdb->port_mask =3D FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]); - /* mac - 47:0 */ - fdb->mac[0] =3D FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]); - fdb->mac[1] =3D FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]); - fdb->mac[2] =3D FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]); - fdb->mac[3] =3D FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]); - fdb->mac[4] =3D FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]); - fdb->mac[5] =3D FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]); - - return 0; -} - -static void -qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *= mac, - u8 aging) -{ - u32 reg[QCA8K_ATU_TABLE_SIZE] =3D { 0 }; - - /* vid - 83:72 */ - reg[2] =3D FIELD_PREP(QCA8K_ATU_VID_MASK, vid); - /* aging - 67:64 */ - reg[2] |=3D FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging); - /* portmask - 54:48 */ - reg[1] =3D FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask); - /* mac - 47:0 */ - reg[1] |=3D FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]); - reg[1] |=3D FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]); - reg[0] |=3D FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]); - reg[0] |=3D FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]); - reg[0] |=3D FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]); - reg[0] |=3D FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]); - - /* load the array into the ARL table */ - regmap_bulk_write(priv->regmap, QCA8K_REG_ATU_DATA0, reg, QCA8K_ATU_TABLE= _SIZE); -} - -static int -qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port) -{ - u32 reg; - int ret; - - /* Set the command and FDB index */ - reg =3D QCA8K_ATU_FUNC_BUSY; - reg |=3D cmd; - if (port >=3D 0) { - reg |=3D QCA8K_ATU_FUNC_PORT_EN; - reg |=3D FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port); - } - - /* Write the function register triggering the table access */ - ret =3D regmap_write(priv->regmap, QCA8K_REG_ATU_FUNC, reg); - if (ret) - return ret; - - /* wait for completion */ - ret =3D qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY); - if (ret) - return ret; - - /* Check for table full violation when adding an entry */ - if (cmd =3D=3D QCA8K_FDB_LOAD) { - ret =3D regmap_read(priv->regmap, QCA8K_REG_ATU_FUNC, ®); - if (ret < 0) - return ret; - if (reg & QCA8K_ATU_FUNC_FULL) - return -1; - } - - return 0; -} - -static int -qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, int port) -{ - int ret; - - qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging); - ret =3D qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port); - if (ret < 0) - return ret; - - return qca8k_fdb_read(priv, fdb); -} - -static int -qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, - u16 vid, u8 aging) -{ - int ret; - - mutex_lock(&priv->reg_mutex); - qca8k_fdb_write(priv, vid, port_mask, mac, aging); - ret =3D qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); - mutex_unlock(&priv->reg_mutex); - - return ret; -} - -static int -qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, u16 v= id) -{ - int ret; - - mutex_lock(&priv->reg_mutex); - qca8k_fdb_write(priv, vid, port_mask, mac, 0); - ret =3D qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); - mutex_unlock(&priv->reg_mutex); - - return ret; -} - -static void -qca8k_fdb_flush(struct qca8k_priv *priv) -{ - mutex_lock(&priv->reg_mutex); - qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1); - mutex_unlock(&priv->reg_mutex); -} - -static int -qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask, - const u8 *mac, u16 vid) -{ - struct qca8k_fdb fdb =3D { 0 }; - int ret; - - mutex_lock(&priv->reg_mutex); - - qca8k_fdb_write(priv, vid, 0, mac, 0); - ret =3D qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1); - if (ret < 0) - goto exit; - - ret =3D qca8k_fdb_read(priv, &fdb); - if (ret < 0) - goto exit; - - /* Rule exist. Delete first */ - if (!fdb.aging) { - ret =3D qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); - if (ret) - goto exit; - } - - /* Add port to fdb portmask */ - fdb.port_mask |=3D port_mask; - - qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging); - ret =3D qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); - -exit: - mutex_unlock(&priv->reg_mutex); - return ret; -} - -static int -qca8k_fdb_search_and_del(struct qca8k_priv *priv, u8 port_mask, - const u8 *mac, u16 vid) -{ - struct qca8k_fdb fdb =3D { 0 }; - int ret; - - mutex_lock(&priv->reg_mutex); - - qca8k_fdb_write(priv, vid, 0, mac, 0); - ret =3D qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1); - if (ret < 0) - goto exit; - - /* Rule doesn't exist. Why delete? */ - if (!fdb.aging) { - ret =3D -EINVAL; - goto exit; - } - - ret =3D qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); - if (ret) - goto exit; - - /* Only port in the rule is this port. Don't re insert */ - if (fdb.port_mask =3D=3D port_mask) - goto exit; - - /* Remove port from port mask */ - fdb.port_mask &=3D ~port_mask; - - qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging); - ret =3D qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); - -exit: - mutex_unlock(&priv->reg_mutex); - return ret; -} - -static int -qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vi= d) -{ - u32 reg; - int ret; - - /* Set the command and VLAN index */ - reg =3D QCA8K_VTU_FUNC1_BUSY; - reg |=3D cmd; - reg |=3D FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid); - - /* Write the function register triggering the table access */ - ret =3D regmap_write(priv->regmap, QCA8K_REG_VTU_FUNC1, reg); - if (ret) - return ret; - - /* wait for completion */ - ret =3D qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY); - if (ret) - return ret; - - /* Check for table full violation when adding an entry */ - if (cmd =3D=3D QCA8K_VLAN_LOAD) { - ret =3D regmap_read(priv->regmap, QCA8K_REG_VTU_FUNC1, ®); - if (ret < 0) - return ret; - if (reg & QCA8K_VTU_FUNC1_FULL) - return -ENOMEM; - } - - return 0; -} - -static int -qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged) -{ - u32 reg; - int ret; - - /* - We do the right thing with VLAN 0 and treat it as untagged while - preserving the tag on egress. - */ - if (vid =3D=3D 0) - return 0; - - mutex_lock(&priv->reg_mutex); - ret =3D qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); - if (ret < 0) - goto out; - - ret =3D regmap_read(priv->regmap, QCA8K_REG_VTU_FUNC0, ®); - if (ret < 0) - goto out; - reg |=3D QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN; - reg &=3D ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); - if (untagged) - reg |=3D QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port); - else - reg |=3D QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port); - - ret =3D regmap_write(priv->regmap, QCA8K_REG_VTU_FUNC0, reg); - if (ret) - goto out; - ret =3D qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); - -out: - mutex_unlock(&priv->reg_mutex); - - return ret; -} - -static int -qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid) -{ - u32 reg, mask; - int ret, i; - bool del; - - mutex_lock(&priv->reg_mutex); - ret =3D qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); - if (ret < 0) - goto out; - - ret =3D regmap_read(priv->regmap, QCA8K_REG_VTU_FUNC0, ®); - if (ret < 0) - goto out; - reg &=3D ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); - reg |=3D QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port); - - /* Check if we're the last member to be removed */ - del =3D true; - for (i =3D 0; i < QCA8K_NUM_PORTS; i++) { - mask =3D QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i); - - if ((reg & mask) !=3D mask) { - del =3D false; - break; - } - } - - if (del) { - ret =3D qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid); - } else { - ret =3D regmap_write(priv->regmap, QCA8K_REG_VTU_FUNC0, reg); - if (ret) - goto out; - ret =3D qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); - } - -out: - mutex_unlock(&priv->reg_mutex); - - return ret; -} - -static int -qca8k_mib_init(struct qca8k_priv *priv) -{ - int ret; - - mutex_lock(&priv->reg_mutex); - ret =3D regmap_update_bits(priv->regmap, QCA8K_REG_MIB, - QCA8K_MIB_FUNC | QCA8K_MIB_BUSY, - FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_FLUSH) | - QCA8K_MIB_BUSY); - if (ret) - goto exit; - - ret =3D qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); - if (ret) - goto exit; - - ret =3D regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); - if (ret) - goto exit; - - ret =3D regmap_write(priv->regmap, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_M= IB); - -exit: - mutex_unlock(&priv->reg_mutex); - return ret; -} - -static void -qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) -{ - u32 mask =3D QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; - - /* Port 0 and 6 have no internal PHY */ - if (port > 0 && port < 6) - mask |=3D QCA8K_PORT_STATUS_LINK_AUTO; - - if (enable) - regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); - else - regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); -} - static int qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data, struct sk_buff *read_skb, u32 *val) @@ -1990,23 +1535,6 @@ static void qca8k_setup_pcs(struct qca8k_priv *priv,= struct qca8k_pcs *qpcs, qpcs->port =3D port; } =20 -static void -qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t = *data) -{ - const struct qca8k_match_data *match_data; - struct qca8k_priv *priv =3D ds->priv; - int i; - - if (stringset !=3D ETH_SS_STATS) - return; - - match_data =3D of_device_get_match_data(priv->dev); - - for (i =3D 0; i < match_data->mib_count; i++) - strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name, - ETH_GSTRING_LEN); -} - static void qca8k_mib_autocast_handler(struct dsa_switch *ds, struct sk_bu= ff *skb) { const struct qca8k_match_data *match_data; @@ -2098,537 +1626,21 @@ qca8k_get_ethtool_stats_eth(struct dsa_switch *ds,= int port, u64 *data) return ret; } =20 -static void -qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, - uint64_t *data) +static u32 qca8k_get_phy_flags(struct dsa_switch *ds, int port) { - struct qca8k_priv *priv =3D (struct qca8k_priv *)ds->priv; - const struct qca8k_match_data *match_data; - const struct qca8k_mib_desc *mib; - u32 reg, i, val; - u32 hi =3D 0; - int ret; + struct qca8k_priv *priv =3D ds->priv; =20 - if (priv->mgmt_master && priv->autocast_mib && - priv->autocast_mib(ds, port, data) > 0) - return; + /* Communicate to the phy internal driver the switch revision. + * Based on the switch revision different values needs to be + * set to the dbg and mmd reg on the phy. + * The first 2 bit are used to communicate the switch revision + * to the phy driver. + */ + if (port > 0 && port < 6) + return priv->switch_revision; =20 - match_data =3D of_device_get_match_data(priv->dev); - - for (i =3D 0; i < match_data->mib_count; i++) { - mib =3D &ar8327_mib[i]; - reg =3D QCA8K_PORT_MIB_COUNTER(port) + mib->offset; - - ret =3D regmap_read(priv->regmap, reg, &val); - if (ret < 0) - continue; - - if (mib->size =3D=3D 2) { - ret =3D regmap_read(priv->regmap, reg + 4, &hi); - if (ret < 0) - continue; - } - - data[i] =3D val; - if (mib->size =3D=3D 2) - data[i] |=3D (u64)hi << 32; - } -} - -static int -qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset) -{ - const struct qca8k_match_data *match_data; - struct qca8k_priv *priv =3D ds->priv; - - if (sset !=3D ETH_SS_STATS) - return 0; - - match_data =3D of_device_get_match_data(priv->dev); - - return match_data->mib_count; -} - -static int -qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee) -{ - struct qca8k_priv *priv =3D (struct qca8k_priv *)ds->priv; - u32 lpi_en =3D QCA8K_REG_EEE_CTRL_LPI_EN(port); - u32 reg; - int ret; - - mutex_lock(&priv->reg_mutex); - ret =3D regmap_read(priv->regmap, QCA8K_REG_EEE_CTRL, ®); - if (ret < 0) - goto exit; - - if (eee->eee_enabled) - reg |=3D lpi_en; - else - reg &=3D ~lpi_en; - ret =3D regmap_write(priv->regmap, QCA8K_REG_EEE_CTRL, reg); - -exit: - mutex_unlock(&priv->reg_mutex); - return ret; -} - -static int -qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) -{ - /* Nothing to do on the port's MAC */ - return 0; -} - -static void -qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) -{ - struct qca8k_priv *priv =3D (struct qca8k_priv *)ds->priv; - u32 stp_state; - - switch (state) { - case BR_STATE_DISABLED: - stp_state =3D QCA8K_PORT_LOOKUP_STATE_DISABLED; - break; - case BR_STATE_BLOCKING: - stp_state =3D QCA8K_PORT_LOOKUP_STATE_BLOCKING; - break; - case BR_STATE_LISTENING: - stp_state =3D QCA8K_PORT_LOOKUP_STATE_LISTENING; - break; - case BR_STATE_LEARNING: - stp_state =3D QCA8K_PORT_LOOKUP_STATE_LEARNING; - break; - case BR_STATE_FORWARDING: - default: - stp_state =3D QCA8K_PORT_LOOKUP_STATE_FORWARD; - break; - } - - regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), - QCA8K_PORT_LOOKUP_STATE_MASK, stp_state); -} - -static int qca8k_port_bridge_join(struct dsa_switch *ds, int port, - struct dsa_bridge bridge, - bool *tx_fwd_offload, - struct netlink_ext_ack *extack) -{ - struct qca8k_priv *priv =3D (struct qca8k_priv *)ds->priv; - int port_mask, cpu_port; - int i, ret; - - cpu_port =3D dsa_to_port(ds, port)->cpu_dp->index; - port_mask =3D BIT(cpu_port); - - for (i =3D 0; i < QCA8K_NUM_PORTS; i++) { - if (dsa_is_cpu_port(ds, i)) - continue; - if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) - continue; - /* Add this port to the portvlan mask of the other ports - * in the bridge - */ - ret =3D regmap_set_bits(priv->regmap, - QCA8K_PORT_LOOKUP_CTRL(i), - BIT(port)); - if (ret) - return ret; - if (i !=3D port) - port_mask |=3D BIT(i); - } - - /* Add all other ports to this ports portvlan mask */ - ret =3D regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), - QCA8K_PORT_LOOKUP_MEMBER, port_mask); - - return ret; -} - -static void qca8k_port_bridge_leave(struct dsa_switch *ds, int port, - struct dsa_bridge bridge) -{ - struct qca8k_priv *priv =3D (struct qca8k_priv *)ds->priv; - int cpu_port, i; - - cpu_port =3D dsa_to_port(ds, port)->cpu_dp->index; - - for (i =3D 0; i < QCA8K_NUM_PORTS; i++) { - if (dsa_is_cpu_port(ds, i)) - continue; - if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) - continue; - /* Remove this port to the portvlan mask of the other ports - * in the bridge - */ - regmap_clear_bits(priv->regmap, - QCA8K_PORT_LOOKUP_CTRL(i), - BIT(port)); - } - - /* Set the cpu port to be the only one in the portvlan mask of - * this port - */ - regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), - QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port)); -} - -static void -qca8k_port_fast_age(struct dsa_switch *ds, int port) -{ - struct qca8k_priv *priv =3D ds->priv; - - mutex_lock(&priv->reg_mutex); - qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port); - mutex_unlock(&priv->reg_mutex); -} - -static int -qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) -{ - struct qca8k_priv *priv =3D ds->priv; - unsigned int secs =3D msecs / 1000; - u32 val; - - /* AGE_TIME reg is set in 7s step */ - val =3D secs / 7; - - /* Handle case with 0 as val to NOT disable - * learning - */ - if (!val) - val =3D 1; - - return regmap_update_bits(priv->regmap, QCA8K_REG_ATU_CTRL, QCA8K_ATU_AGE= _TIME_MASK, - QCA8K_ATU_AGE_TIME(val)); -} - -static int -qca8k_port_enable(struct dsa_switch *ds, int port, - struct phy_device *phy) -{ - struct qca8k_priv *priv =3D (struct qca8k_priv *)ds->priv; - - qca8k_port_set_status(priv, port, 1); - priv->port_enabled_map |=3D BIT(port); - - if (dsa_is_user_port(ds, port)) - phy_support_asym_pause(phy); - - return 0; -} - -static void -qca8k_port_disable(struct dsa_switch *ds, int port) -{ - struct qca8k_priv *priv =3D (struct qca8k_priv *)ds->priv; - - qca8k_port_set_status(priv, port, 0); - priv->port_enabled_map &=3D ~BIT(port); -} - -static int -qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) -{ - struct qca8k_priv *priv =3D ds->priv; - int ret; - - /* We have only have a general MTU setting. - * DSA always set the CPU port's MTU to the largest MTU of the slave - * ports. - * Setting MTU just for the CPU port is sufficient to correctly set a - * value for every port. - */ - if (!dsa_is_cpu_port(ds, port)) - return 0; - - /* To change the MAX_FRAME_SIZE the cpu ports must be off or - * the switch panics. - * Turn off both cpu ports before applying the new value to prevent - * this. - */ - if (priv->port_enabled_map & BIT(0)) - qca8k_port_set_status(priv, 0, 0); - - if (priv->port_enabled_map & BIT(6)) - qca8k_port_set_status(priv, 6, 0); - - /* Include L2 header / FCS length */ - ret =3D regmap_write(priv->regmap, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HL= EN + ETH_FCS_LEN); - - if (priv->port_enabled_map & BIT(0)) - qca8k_port_set_status(priv, 0, 1); - - if (priv->port_enabled_map & BIT(6)) - qca8k_port_set_status(priv, 6, 1); - - return ret; -} - -static int -qca8k_port_max_mtu(struct dsa_switch *ds, int port) -{ - return QCA8K_MAX_MTU; -} - -static int -qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, - u16 port_mask, u16 vid) -{ - /* Set the vid to the port vlan id if no vid is set */ - if (!vid) - vid =3D QCA8K_PORT_VID_DEF; - - return qca8k_fdb_add(priv, addr, port_mask, vid, - QCA8K_ATU_STATUS_STATIC); -} - -static int -qca8k_port_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid, - struct dsa_db db) -{ - struct qca8k_priv *priv =3D (struct qca8k_priv *)ds->priv; - u16 port_mask =3D BIT(port); - - return qca8k_port_fdb_insert(priv, addr, port_mask, vid); -} - -static int -qca8k_port_fdb_del(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid, - struct dsa_db db) -{ - struct qca8k_priv *priv =3D (struct qca8k_priv *)ds->priv; - u16 port_mask =3D BIT(port); - - if (!vid) - vid =3D QCA8K_PORT_VID_DEF; - - return qca8k_fdb_del(priv, addr, port_mask, vid); -} - -static int -qca8k_port_fdb_dump(struct dsa_switch *ds, int port, - dsa_fdb_dump_cb_t *cb, void *data) -{ - struct qca8k_priv *priv =3D (struct qca8k_priv *)ds->priv; - struct qca8k_fdb _fdb =3D { 0 }; - int cnt =3D QCA8K_NUM_FDB_RECORDS; - bool is_static; - int ret =3D 0; - - mutex_lock(&priv->reg_mutex); - while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) { - if (!_fdb.aging) - break; - is_static =3D (_fdb.aging =3D=3D QCA8K_ATU_STATUS_STATIC); - ret =3D cb(_fdb.mac, _fdb.vid, is_static, data); - if (ret) - break; - } - mutex_unlock(&priv->reg_mutex); - - return 0; -} - -static int -qca8k_port_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb, - struct dsa_db db) -{ - struct qca8k_priv *priv =3D ds->priv; - const u8 *addr =3D mdb->addr; - u16 vid =3D mdb->vid; - - return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid); -} - -static int -qca8k_port_mdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb, - struct dsa_db db) -{ - struct qca8k_priv *priv =3D ds->priv; - const u8 *addr =3D mdb->addr; - u16 vid =3D mdb->vid; - - return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid); -} - -static int -qca8k_port_mirror_add(struct dsa_switch *ds, int port, - struct dsa_mall_mirror_tc_entry *mirror, - bool ingress, struct netlink_ext_ack *extack) -{ - struct qca8k_priv *priv =3D ds->priv; - int monitor_port, ret; - u32 reg, val; - - /* Check for existent entry */ - if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port)) - return -EEXIST; - - ret =3D regmap_read(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, &val); - if (ret) - return ret; - - /* QCA83xx can have only one port set to mirror mode. - * Check that the correct port is requested and return error otherwise. - * When no mirror port is set, the values is set to 0xF - */ - monitor_port =3D FIELD_GET(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); - if (monitor_port !=3D 0xF && monitor_port !=3D mirror->to_local_port) - return -EEXIST; - - /* Set the monitor port */ - val =3D FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, - mirror->to_local_port); - ret =3D regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, - QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); - if (ret) - return ret; - - if (ingress) { - reg =3D QCA8K_PORT_LOOKUP_CTRL(port); - val =3D QCA8K_PORT_LOOKUP_ING_MIRROR_EN; - } else { - reg =3D QCA8K_REG_PORT_HOL_CTRL1(port); - val =3D QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN; - } - - ret =3D regmap_update_bits(priv->regmap, reg, val, val); - if (ret) - return ret; - - /* Track mirror port for tx and rx to decide when the - * mirror port has to be disabled. - */ - if (ingress) - priv->mirror_rx |=3D BIT(port); - else - priv->mirror_tx |=3D BIT(port); - - return 0; -} - -static void -qca8k_port_mirror_del(struct dsa_switch *ds, int port, - struct dsa_mall_mirror_tc_entry *mirror) -{ - struct qca8k_priv *priv =3D ds->priv; - u32 reg, val; - int ret; - - if (mirror->ingress) { - reg =3D QCA8K_PORT_LOOKUP_CTRL(port); - val =3D QCA8K_PORT_LOOKUP_ING_MIRROR_EN; - } else { - reg =3D QCA8K_REG_PORT_HOL_CTRL1(port); - val =3D QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN; - } - - ret =3D regmap_clear_bits(priv->regmap, reg, val); - if (ret) - goto err; - - if (mirror->ingress) - priv->mirror_rx &=3D ~BIT(port); - else - priv->mirror_tx &=3D ~BIT(port); - - /* No port set to send packet to mirror port. Disable mirror port */ - if (!priv->mirror_rx && !priv->mirror_tx) { - val =3D FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, 0xF); - ret =3D regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, - QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); - if (ret) - goto err; - } -err: - dev_err(priv->dev, "Failed to del mirror port from %d", port); -} - -static int -qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filte= ring, - struct netlink_ext_ack *extack) -{ - struct qca8k_priv *priv =3D ds->priv; - int ret; - - if (vlan_filtering) { - ret =3D regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), - QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, - QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE); - } else { - ret =3D regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), - QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, - QCA8K_PORT_LOOKUP_VLAN_MODE_NONE); - } - - return ret; -} - -static int -qca8k_port_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan, - struct netlink_ext_ack *extack) -{ - bool untagged =3D vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; - bool pvid =3D vlan->flags & BRIDGE_VLAN_INFO_PVID; - struct qca8k_priv *priv =3D ds->priv; - int ret; - - ret =3D qca8k_vlan_add(priv, port, vlan->vid, untagged); - if (ret) { - dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret); - return ret; - } - - if (pvid) { - ret =3D regmap_update_bits(priv->regmap, QCA8K_EGRESS_VLAN(port), - QCA8K_EGREES_VLAN_PORT_MASK(port), - QCA8K_EGREES_VLAN_PORT(port, vlan->vid)); - if (ret) - return ret; - - ret =3D regmap_write(priv->regmap, QCA8K_REG_PORT_VLAN_CTRL0(port), - QCA8K_PORT_VLAN_CVID(vlan->vid) | - QCA8K_PORT_VLAN_SVID(vlan->vid)); - } - - return ret; -} - -static int -qca8k_port_vlan_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) -{ - struct qca8k_priv *priv =3D ds->priv; - int ret; - - ret =3D qca8k_vlan_del(priv, port, vlan->vid); - if (ret) - dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret); - - return ret; -} - -static u32 qca8k_get_phy_flags(struct dsa_switch *ds, int port) -{ - struct qca8k_priv *priv =3D ds->priv; - - /* Communicate to the phy internal driver the switch revision. - * Based on the switch revision different values needs to be - * set to the dbg and mmd reg on the phy. - * The first 2 bit are used to communicate the switch revision - * to the phy driver. - */ - if (port > 0 && port < 6) - return priv->switch_revision; - - return 0; -} + return 0; +} =20 static enum dsa_tag_protocol qca8k_get_tag_protocol(struct dsa_switch *ds, int port, @@ -2637,174 +1649,6 @@ qca8k_get_tag_protocol(struct dsa_switch *ds, int p= ort, return DSA_TAG_PROTO_QCA; } =20 -static bool -qca8k_lag_can_offload(struct dsa_switch *ds, struct dsa_lag lag, - struct netdev_lag_upper_info *info) -{ - struct dsa_port *dp; - int members =3D 0; - - if (!lag.id) - return false; - - dsa_lag_foreach_port(dp, ds->dst, &lag) - /* Includes the port joining the LAG */ - members++; - - if (members > QCA8K_NUM_PORTS_FOR_LAG) - return false; - - if (info->tx_type !=3D NETDEV_LAG_TX_TYPE_HASH) - return false; - - if (info->hash_type !=3D NETDEV_LAG_HASH_L2 && - info->hash_type !=3D NETDEV_LAG_HASH_L23) - return false; - - return true; -} - -static int -qca8k_lag_setup_hash(struct dsa_switch *ds, struct dsa_lag lag, - struct netdev_lag_upper_info *info) -{ - struct net_device *lag_dev =3D lag.dev; - struct qca8k_priv *priv =3D ds->priv; - bool unique_lag =3D true; - unsigned int i; - u32 hash =3D 0; - - switch (info->hash_type) { - case NETDEV_LAG_HASH_L23: - hash |=3D QCA8K_TRUNK_HASH_SIP_EN; - hash |=3D QCA8K_TRUNK_HASH_DIP_EN; - fallthrough; - case NETDEV_LAG_HASH_L2: - hash |=3D QCA8K_TRUNK_HASH_SA_EN; - hash |=3D QCA8K_TRUNK_HASH_DA_EN; - break; - default: /* We should NEVER reach this */ - return -EOPNOTSUPP; - } - - /* Check if we are the unique configured LAG */ - dsa_lags_foreach_id(i, ds->dst) - if (i !=3D lag.id && dsa_lag_by_id(ds->dst, i)) { - unique_lag =3D false; - break; - } - - /* Hash Mode is global. Make sure the same Hash Mode - * is set to all the 4 possible lag. - * If we are the unique LAG we can set whatever hash - * mode we want. - * To change hash mode it's needed to remove all LAG - * and change the mode with the latest. - */ - if (unique_lag) { - priv->lag_hash_mode =3D hash; - } else if (priv->lag_hash_mode !=3D hash) { - netdev_err(lag_dev, "Error: Mismatched Hash Mode across different lag is= not supported\n"); - return -EOPNOTSUPP; - } - - return regmap_update_bits(priv->regmap, QCA8K_TRUNK_HASH_EN_CTRL, - QCA8K_TRUNK_HASH_MASK, hash); -} - -static int -qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port, - struct dsa_lag lag, bool delete) -{ - struct qca8k_priv *priv =3D ds->priv; - int ret, id, i; - u32 val; - - /* DSA LAG IDs are one-based, hardware is zero-based */ - id =3D lag.id - 1; - - /* Read current port member */ - ret =3D regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val); - if (ret) - return ret; - - /* Shift val to the correct trunk */ - val >>=3D QCA8K_REG_GOL_TRUNK_SHIFT(id); - val &=3D QCA8K_REG_GOL_TRUNK_MEMBER_MASK; - if (delete) - val &=3D ~BIT(port); - else - val |=3D BIT(port); - - /* Update port member. With empty portmap disable trunk */ - ret =3D regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, - QCA8K_REG_GOL_TRUNK_MEMBER(id) | - QCA8K_REG_GOL_TRUNK_EN(id), - !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) | - val << QCA8K_REG_GOL_TRUNK_SHIFT(id)); - - /* Search empty member if adding or port on deleting */ - for (i =3D 0; i < QCA8K_NUM_PORTS_FOR_LAG; i++) { - ret =3D regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val); - if (ret) - return ret; - - val >>=3D QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i); - val &=3D QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK; - - if (delete) { - /* If port flagged to be disabled assume this member is - * empty - */ - if (val !=3D QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK) - continue; - - val &=3D QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK; - if (val !=3D port) - continue; - } else { - /* If port flagged to be enabled assume this member is - * already set - */ - if (val =3D=3D QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK) - continue; - } - - /* We have found the member to add/remove */ - break; - } - - /* Set port in the correct port mask or disable port if in delete mode */ - return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), - QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) | - QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i), - !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) | - port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i)); -} - -static int -qca8k_port_lag_join(struct dsa_switch *ds, int port, struct dsa_lag lag, - struct netdev_lag_upper_info *info) -{ - int ret; - - if (!qca8k_lag_can_offload(ds, lag, info)) - return -EOPNOTSUPP; - - ret =3D qca8k_lag_setup_hash(ds, lag, info); - if (ret) - return ret; - - return qca8k_lag_refresh_portmap(ds, port, lag, false); -} - -static int -qca8k_port_lag_leave(struct dsa_switch *ds, int port, - struct dsa_lag lag) -{ - return qca8k_lag_refresh_portmap(ds, port, lag, true); -} - static void qca8k_master_change(struct dsa_switch *ds, const struct net_device *master, bool operational) @@ -3090,36 +1934,6 @@ static const struct dsa_switch_ops qca8k_switch_ops = =3D { .connect_tag_protocol =3D qca8k_connect_tag_protocol, }; =20 -static int qca8k_read_switch_id(struct qca8k_priv *priv) -{ - const struct qca8k_match_data *data; - u32 val; - u8 id; - int ret; - - /* get the switches ID from the compatible */ - data =3D of_device_get_match_data(priv->dev); - if (!data) - return -ENODEV; - - ret =3D regmap_read(priv->regmap, QCA8K_REG_MASK_CTRL, &val); - if (ret < 0) - return -ENODEV; - - id =3D QCA8K_MASK_CTRL_DEVICE_ID(val); - if (id !=3D data->id) { - dev_err(priv->dev, "Switch id detected %x but expected %x", id, data->id= ); - return -ENODEV; - } - - priv->switch_id =3D id; - - /* Save revision to communicate to the internal PHY driver */ - priv->switch_revision =3D QCA8K_MASK_CTRL_REV_ID(val); - - return 0; -} - static int qca8k_sw_probe(struct mdio_device *mdiodev) { diff --git a/drivers/net/dsa/qca/qca8k-common.c b/drivers/net/dsa/qca/qca8k= -common.c new file mode 100644 index 000000000000..b8a18c5e7664 --- /dev/null +++ b/drivers/net/dsa/qca/qca8k-common.c @@ -0,0 +1,1174 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2009 Felix Fietkau + * Copyright (C) 2011-2012 Gabor Juhos + * Copyright (c) 2015, 2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2016 John Crispin + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "qca8k.h" + +#define MIB_DESC(_s, _o, _n) \ + { \ + .size =3D (_s), \ + .offset =3D (_o), \ + .name =3D (_n), \ + } + +const struct qca8k_mib_desc ar8327_mib[] =3D { + MIB_DESC(1, 0x00, "RxBroad"), + MIB_DESC(1, 0x04, "RxPause"), + MIB_DESC(1, 0x08, "RxMulti"), + MIB_DESC(1, 0x0c, "RxFcsErr"), + MIB_DESC(1, 0x10, "RxAlignErr"), + MIB_DESC(1, 0x14, "RxRunt"), + MIB_DESC(1, 0x18, "RxFragment"), + MIB_DESC(1, 0x1c, "Rx64Byte"), + MIB_DESC(1, 0x20, "Rx128Byte"), + MIB_DESC(1, 0x24, "Rx256Byte"), + MIB_DESC(1, 0x28, "Rx512Byte"), + MIB_DESC(1, 0x2c, "Rx1024Byte"), + MIB_DESC(1, 0x30, "Rx1518Byte"), + MIB_DESC(1, 0x34, "RxMaxByte"), + MIB_DESC(1, 0x38, "RxTooLong"), + MIB_DESC(2, 0x3c, "RxGoodByte"), + MIB_DESC(2, 0x44, "RxBadByte"), + MIB_DESC(1, 0x4c, "RxOverFlow"), + MIB_DESC(1, 0x50, "Filtered"), + MIB_DESC(1, 0x54, "TxBroad"), + MIB_DESC(1, 0x58, "TxPause"), + MIB_DESC(1, 0x5c, "TxMulti"), + MIB_DESC(1, 0x60, "TxUnderRun"), + MIB_DESC(1, 0x64, "Tx64Byte"), + MIB_DESC(1, 0x68, "Tx128Byte"), + MIB_DESC(1, 0x6c, "Tx256Byte"), + MIB_DESC(1, 0x70, "Tx512Byte"), + MIB_DESC(1, 0x74, "Tx1024Byte"), + MIB_DESC(1, 0x78, "Tx1518Byte"), + MIB_DESC(1, 0x7c, "TxMaxByte"), + MIB_DESC(1, 0x80, "TxOverSize"), + MIB_DESC(2, 0x84, "TxByte"), + MIB_DESC(1, 0x8c, "TxCollision"), + MIB_DESC(1, 0x90, "TxAbortCol"), + MIB_DESC(1, 0x94, "TxMultiCol"), + MIB_DESC(1, 0x98, "TxSingleCol"), + MIB_DESC(1, 0x9c, "TxExcDefer"), + MIB_DESC(1, 0xa0, "TxDefer"), + MIB_DESC(1, 0xa4, "TxLateCol"), + MIB_DESC(1, 0xa8, "RXUnicast"), + MIB_DESC(1, 0xac, "TXUnicast"), +}; + +static const struct regmap_range qca8k_readable_ranges[] =3D { + regmap_reg_range(0x0000, 0x00e4), /* Global control */ + regmap_reg_range(0x0100, 0x0168), /* EEE control */ + regmap_reg_range(0x0200, 0x0270), /* Parser control */ + regmap_reg_range(0x0400, 0x0454), /* ACL */ + regmap_reg_range(0x0600, 0x0718), /* Lookup */ + regmap_reg_range(0x0800, 0x0b70), /* QM */ + regmap_reg_range(0x0c00, 0x0c80), /* PKT */ + regmap_reg_range(0x0e00, 0x0e98), /* L3 */ + regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */ + regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */ + regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */ + regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */ + regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */ + regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */ + regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */ + +}; + +const struct regmap_access_table qca8k_readable_table =3D { + .yes_ranges =3D qca8k_readable_ranges, + .n_yes_ranges =3D ARRAY_SIZE(qca8k_readable_ranges), +}; + +static int +qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) +{ + u32 val; + + return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0, + QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC); +} + +static int +qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) +{ + u32 reg[QCA8K_ATU_TABLE_SIZE]; + int ret; + + /* load the ARL table into an array */ + ret =3D regmap_bulk_read(priv->regmap, QCA8K_REG_ATU_DATA0, reg, + QCA8K_ATU_TABLE_SIZE); + if (ret) + return ret; + + /* vid - 83:72 */ + fdb->vid =3D FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]); + /* aging - 67:64 */ + fdb->aging =3D FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]); + /* portmask - 54:48 */ + fdb->port_mask =3D FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]); + /* mac - 47:0 */ + fdb->mac[0] =3D FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]); + fdb->mac[1] =3D FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]); + fdb->mac[2] =3D FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]); + fdb->mac[3] =3D FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]); + fdb->mac[4] =3D FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]); + fdb->mac[5] =3D FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]); + + return 0; +} + +static void +qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *= mac, + u8 aging) +{ + u32 reg[QCA8K_ATU_TABLE_SIZE] =3D { 0 }; + + /* vid - 83:72 */ + reg[2] =3D FIELD_PREP(QCA8K_ATU_VID_MASK, vid); + /* aging - 67:64 */ + reg[2] |=3D FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging); + /* portmask - 54:48 */ + reg[1] =3D FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask); + /* mac - 47:0 */ + reg[1] |=3D FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]); + reg[1] |=3D FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]); + reg[0] |=3D FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]); + reg[0] |=3D FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]); + reg[0] |=3D FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]); + reg[0] |=3D FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]); + + /* load the array into the ARL table */ + regmap_bulk_write(priv->regmap, QCA8K_REG_ATU_DATA0, reg, QCA8K_ATU_TABLE= _SIZE); +} + +static int +qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port) +{ + u32 reg; + int ret; + + /* Set the command and FDB index */ + reg =3D QCA8K_ATU_FUNC_BUSY; + reg |=3D cmd; + if (port >=3D 0) { + reg |=3D QCA8K_ATU_FUNC_PORT_EN; + reg |=3D FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port); + } + + /* Write the function register triggering the table access */ + ret =3D regmap_write(priv->regmap, QCA8K_REG_ATU_FUNC, reg); + if (ret) + return ret; + + /* wait for completion */ + ret =3D qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY); + if (ret) + return ret; + + /* Check for table full violation when adding an entry */ + if (cmd =3D=3D QCA8K_FDB_LOAD) { + ret =3D regmap_read(priv->regmap, QCA8K_REG_ATU_FUNC, ®); + if (ret < 0) + return ret; + if (reg & QCA8K_ATU_FUNC_FULL) + return -1; + } + + return 0; +} + +static int +qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, int port) +{ + int ret; + + qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging); + ret =3D qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port); + if (ret < 0) + return ret; + + return qca8k_fdb_read(priv, fdb); +} + +static int +qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, + u16 vid, u8 aging) +{ + int ret; + + mutex_lock(&priv->reg_mutex); + qca8k_fdb_write(priv, vid, port_mask, mac, aging); + ret =3D qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); + mutex_unlock(&priv->reg_mutex); + + return ret; +} + +static int +qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, u16 v= id) +{ + int ret; + + mutex_lock(&priv->reg_mutex); + qca8k_fdb_write(priv, vid, port_mask, mac, 0); + ret =3D qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); + mutex_unlock(&priv->reg_mutex); + + return ret; +} + +void qca8k_fdb_flush(struct qca8k_priv *priv) +{ + mutex_lock(&priv->reg_mutex); + qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1); + mutex_unlock(&priv->reg_mutex); +} + +static int +qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask, + const u8 *mac, u16 vid) +{ + struct qca8k_fdb fdb =3D { 0 }; + int ret; + + mutex_lock(&priv->reg_mutex); + + qca8k_fdb_write(priv, vid, 0, mac, 0); + ret =3D qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1); + if (ret < 0) + goto exit; + + ret =3D qca8k_fdb_read(priv, &fdb); + if (ret < 0) + goto exit; + + /* Rule exist. Delete first */ + if (!fdb.aging) { + ret =3D qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); + if (ret) + goto exit; + } + + /* Add port to fdb portmask */ + fdb.port_mask |=3D port_mask; + + qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging); + ret =3D qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); + +exit: + mutex_unlock(&priv->reg_mutex); + return ret; +} + +static int +qca8k_fdb_search_and_del(struct qca8k_priv *priv, u8 port_mask, + const u8 *mac, u16 vid) +{ + struct qca8k_fdb fdb =3D { 0 }; + int ret; + + mutex_lock(&priv->reg_mutex); + + qca8k_fdb_write(priv, vid, 0, mac, 0); + ret =3D qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1); + if (ret < 0) + goto exit; + + /* Rule doesn't exist. Why delete? */ + if (!fdb.aging) { + ret =3D -EINVAL; + goto exit; + } + + ret =3D qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); + if (ret) + goto exit; + + /* Only port in the rule is this port. Don't re insert */ + if (fdb.port_mask =3D=3D port_mask) + goto exit; + + /* Remove port from port mask */ + fdb.port_mask &=3D ~port_mask; + + qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging); + ret =3D qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); + +exit: + mutex_unlock(&priv->reg_mutex); + return ret; +} + +static int +qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vi= d) +{ + u32 reg; + int ret; + + /* Set the command and VLAN index */ + reg =3D QCA8K_VTU_FUNC1_BUSY; + reg |=3D cmd; + reg |=3D FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid); + + /* Write the function register triggering the table access */ + ret =3D regmap_write(priv->regmap, QCA8K_REG_VTU_FUNC1, reg); + if (ret) + return ret; + + /* wait for completion */ + ret =3D qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY); + if (ret) + return ret; + + /* Check for table full violation when adding an entry */ + if (cmd =3D=3D QCA8K_VLAN_LOAD) { + ret =3D regmap_read(priv->regmap, QCA8K_REG_VTU_FUNC1, ®); + if (ret < 0) + return ret; + if (reg & QCA8K_VTU_FUNC1_FULL) + return -ENOMEM; + } + + return 0; +} + +static int +qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged) +{ + u32 reg; + int ret; + + /* We do the right thing with VLAN 0 and treat it as untagged while + * preserving the tag on egress. + */ + if (vid =3D=3D 0) + return 0; + + mutex_lock(&priv->reg_mutex); + ret =3D qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); + if (ret < 0) + goto out; + + ret =3D regmap_read(priv->regmap, QCA8K_REG_VTU_FUNC0, ®); + if (ret < 0) + goto out; + reg |=3D QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN; + reg &=3D ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); + if (untagged) + reg |=3D QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port); + else + reg |=3D QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port); + + ret =3D regmap_write(priv->regmap, QCA8K_REG_VTU_FUNC0, reg); + if (ret) + goto out; + ret =3D qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); + +out: + mutex_unlock(&priv->reg_mutex); + + return ret; +} + +static int +qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid) +{ + u32 reg, mask; + int ret, i; + bool del; + + mutex_lock(&priv->reg_mutex); + ret =3D qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); + if (ret < 0) + goto out; + + ret =3D regmap_read(priv->regmap, QCA8K_REG_VTU_FUNC0, ®); + if (ret < 0) + goto out; + reg &=3D ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); + reg |=3D QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port); + + /* Check if we're the last member to be removed */ + del =3D true; + for (i =3D 0; i < QCA8K_NUM_PORTS; i++) { + mask =3D QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i); + + if ((reg & mask) !=3D mask) { + del =3D false; + break; + } + } + + if (del) { + ret =3D qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid); + } else { + ret =3D regmap_write(priv->regmap, QCA8K_REG_VTU_FUNC0, reg); + if (ret) + goto out; + ret =3D qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); + } + +out: + mutex_unlock(&priv->reg_mutex); + + return ret; +} + +int qca8k_mib_init(struct qca8k_priv *priv) +{ + int ret; + + mutex_lock(&priv->reg_mutex); + ret =3D regmap_update_bits(priv->regmap, QCA8K_REG_MIB, + QCA8K_MIB_FUNC | QCA8K_MIB_BUSY, + FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_FLUSH) | + QCA8K_MIB_BUSY); + if (ret) + goto exit; + + ret =3D qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); + if (ret) + goto exit; + + ret =3D regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); + if (ret) + goto exit; + + ret =3D regmap_write(priv->regmap, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_M= IB); + +exit: + mutex_unlock(&priv->reg_mutex); + return ret; +} + +void qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) +{ + u32 mask =3D QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; + + /* Port 0 and 6 have no internal PHY */ + if (port > 0 && port < 6) + mask |=3D QCA8K_PORT_STATUS_LINK_AUTO; + + if (enable) + regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); + else + regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); +} + +void qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uin= t8_t *data) +{ + const struct qca8k_match_data *match_data; + struct qca8k_priv *priv =3D ds->priv; + int i; + + if (stringset !=3D ETH_SS_STATS) + return; + + match_data =3D of_device_get_match_data(priv->dev); + + for (i =3D 0; i < match_data->mib_count; i++) + strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name, + ETH_GSTRING_LEN); +} + +void qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, + uint64_t *data) +{ + struct qca8k_priv *priv =3D (struct qca8k_priv *)ds->priv; + const struct qca8k_match_data *match_data; + const struct qca8k_mib_desc *mib; + u32 reg, i, val; + u32 hi =3D 0; + int ret; + + if (priv->mgmt_master && priv->autocast_mib && + priv->autocast_mib(ds, port, data) > 0) + return; + + match_data =3D of_device_get_match_data(priv->dev); + + for (i =3D 0; i < match_data->mib_count; i++) { + mib =3D &ar8327_mib[i]; + reg =3D QCA8K_PORT_MIB_COUNTER(port) + mib->offset; + + ret =3D regmap_read(priv->regmap, reg, &val); + if (ret < 0) + continue; + + if (mib->size =3D=3D 2) { + ret =3D regmap_read(priv->regmap, reg + 4, &hi); + if (ret < 0) + continue; + } + + data[i] =3D val; + if (mib->size =3D=3D 2) + data[i] |=3D (u64)hi << 32; + } +} + +int qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset) +{ + const struct qca8k_match_data *match_data; + struct qca8k_priv *priv =3D ds->priv; + + if (sset !=3D ETH_SS_STATS) + return 0; + + match_data =3D of_device_get_match_data(priv->dev); + + return match_data->mib_count; +} + +int qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee = *eee) +{ + struct qca8k_priv *priv =3D (struct qca8k_priv *)ds->priv; + u32 lpi_en =3D QCA8K_REG_EEE_CTRL_LPI_EN(port); + u32 reg; + int ret; + + mutex_lock(&priv->reg_mutex); + ret =3D regmap_read(priv->regmap, QCA8K_REG_EEE_CTRL, ®); + if (ret < 0) + goto exit; + + if (eee->eee_enabled) + reg |=3D lpi_en; + else + reg &=3D ~lpi_en; + ret =3D regmap_write(priv->regmap, QCA8K_REG_EEE_CTRL, reg); + +exit: + mutex_unlock(&priv->reg_mutex); + return ret; +} + +int qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee = *e) +{ + /* Nothing to do on the port's MAC */ + return 0; +} + +void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) +{ + struct qca8k_priv *priv =3D (struct qca8k_priv *)ds->priv; + u32 stp_state; + + switch (state) { + case BR_STATE_DISABLED: + stp_state =3D QCA8K_PORT_LOOKUP_STATE_DISABLED; + break; + case BR_STATE_BLOCKING: + stp_state =3D QCA8K_PORT_LOOKUP_STATE_BLOCKING; + break; + case BR_STATE_LISTENING: + stp_state =3D QCA8K_PORT_LOOKUP_STATE_LISTENING; + break; + case BR_STATE_LEARNING: + stp_state =3D QCA8K_PORT_LOOKUP_STATE_LEARNING; + break; + case BR_STATE_FORWARDING: + default: + stp_state =3D QCA8K_PORT_LOOKUP_STATE_FORWARD; + break; + } + + regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), + QCA8K_PORT_LOOKUP_STATE_MASK, stp_state); +} + +int qca8k_port_bridge_join(struct dsa_switch *ds, int port, + struct dsa_bridge bridge, + bool *tx_fwd_offload, + struct netlink_ext_ack *extack) +{ + struct qca8k_priv *priv =3D (struct qca8k_priv *)ds->priv; + int port_mask, cpu_port; + int i, ret; + + cpu_port =3D dsa_to_port(ds, port)->cpu_dp->index; + port_mask =3D BIT(cpu_port); + + for (i =3D 0; i < QCA8K_NUM_PORTS; i++) { + if (dsa_is_cpu_port(ds, i)) + continue; + if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) + continue; + /* Add this port to the portvlan mask of the other ports + * in the bridge + */ + ret =3D regmap_set_bits(priv->regmap, + QCA8K_PORT_LOOKUP_CTRL(i), + BIT(port)); + if (ret) + return ret; + if (i !=3D port) + port_mask |=3D BIT(i); + } + + /* Add all other ports to this ports portvlan mask */ + ret =3D regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), + QCA8K_PORT_LOOKUP_MEMBER, port_mask); + + return ret; +} + +void qca8k_port_bridge_leave(struct dsa_switch *ds, int port, + struct dsa_bridge bridge) +{ + struct qca8k_priv *priv =3D (struct qca8k_priv *)ds->priv; + int cpu_port, i; + + cpu_port =3D dsa_to_port(ds, port)->cpu_dp->index; + + for (i =3D 0; i < QCA8K_NUM_PORTS; i++) { + if (dsa_is_cpu_port(ds, i)) + continue; + if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) + continue; + /* Remove this port to the portvlan mask of the other ports + * in the bridge + */ + regmap_clear_bits(priv->regmap, + QCA8K_PORT_LOOKUP_CTRL(i), + BIT(port)); + } + + /* Set the cpu port to be the only one in the portvlan mask of + * this port + */ + regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), + QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port)); +} + +void qca8k_port_fast_age(struct dsa_switch *ds, int port) +{ + struct qca8k_priv *priv =3D ds->priv; + + mutex_lock(&priv->reg_mutex); + qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port); + mutex_unlock(&priv->reg_mutex); +} + +int qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) +{ + struct qca8k_priv *priv =3D ds->priv; + unsigned int secs =3D msecs / 1000; + u32 val; + + /* AGE_TIME reg is set in 7s step */ + val =3D secs / 7; + + /* Handle case with 0 as val to NOT disable + * learning + */ + if (!val) + val =3D 1; + + return regmap_update_bits(priv->regmap, QCA8K_REG_ATU_CTRL, QCA8K_ATU_AGE= _TIME_MASK, + QCA8K_ATU_AGE_TIME(val)); +} + +int qca8k_port_enable(struct dsa_switch *ds, int port, + struct phy_device *phy) +{ + struct qca8k_priv *priv =3D (struct qca8k_priv *)ds->priv; + + qca8k_port_set_status(priv, port, 1); + priv->port_enabled_map |=3D BIT(port); + + if (dsa_is_user_port(ds, port)) + phy_support_asym_pause(phy); + + return 0; +} + +void qca8k_port_disable(struct dsa_switch *ds, int port) +{ + struct qca8k_priv *priv =3D (struct qca8k_priv *)ds->priv; + + qca8k_port_set_status(priv, port, 0); + priv->port_enabled_map &=3D ~BIT(port); +} + +int qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) +{ + struct qca8k_priv *priv =3D ds->priv; + int ret; + + /* We have only have a general MTU setting. + * DSA always set the CPU port's MTU to the largest MTU of the slave + * ports. + * Setting MTU just for the CPU port is sufficient to correctly set a + * value for every port. + */ + if (!dsa_is_cpu_port(ds, port)) + return 0; + + /* To change the MAX_FRAME_SIZE the cpu ports must be off or + * the switch panics. + * Turn off both cpu ports before applying the new value to prevent + * this. + */ + if (priv->port_enabled_map & BIT(0)) + qca8k_port_set_status(priv, 0, 0); + + if (priv->port_enabled_map & BIT(6)) + qca8k_port_set_status(priv, 6, 0); + + /* Include L2 header / FCS length */ + ret =3D regmap_write(priv->regmap, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HL= EN + ETH_FCS_LEN); + + if (priv->port_enabled_map & BIT(0)) + qca8k_port_set_status(priv, 0, 1); + + if (priv->port_enabled_map & BIT(6)) + qca8k_port_set_status(priv, 6, 1); + + return ret; +} + +int qca8k_port_max_mtu(struct dsa_switch *ds, int port) +{ + return QCA8K_MAX_MTU; +} + +int qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, + u16 port_mask, u16 vid) +{ + /* Set the vid to the port vlan id if no vid is set */ + if (!vid) + vid =3D QCA8K_PORT_VID_DEF; + + return qca8k_fdb_add(priv, addr, port_mask, vid, + QCA8K_ATU_STATUS_STATIC); +} + +int qca8k_port_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db) +{ + struct qca8k_priv *priv =3D (struct qca8k_priv *)ds->priv; + u16 port_mask =3D BIT(port); + + return qca8k_port_fdb_insert(priv, addr, port_mask, vid); +} + +int qca8k_port_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db) +{ + struct qca8k_priv *priv =3D (struct qca8k_priv *)ds->priv; + u16 port_mask =3D BIT(port); + + if (!vid) + vid =3D QCA8K_PORT_VID_DEF; + + return qca8k_fdb_del(priv, addr, port_mask, vid); +} + +int qca8k_port_fdb_dump(struct dsa_switch *ds, int port, + dsa_fdb_dump_cb_t *cb, void *data) +{ + struct qca8k_priv *priv =3D (struct qca8k_priv *)ds->priv; + struct qca8k_fdb _fdb =3D { 0 }; + int cnt =3D QCA8K_NUM_FDB_RECORDS; + bool is_static; + int ret =3D 0; + + mutex_lock(&priv->reg_mutex); + while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) { + if (!_fdb.aging) + break; + is_static =3D (_fdb.aging =3D=3D QCA8K_ATU_STATUS_STATIC); + ret =3D cb(_fdb.mac, _fdb.vid, is_static, data); + if (ret) + break; + } + mutex_unlock(&priv->reg_mutex); + + return 0; +} + +int qca8k_port_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) +{ + struct qca8k_priv *priv =3D ds->priv; + const u8 *addr =3D mdb->addr; + u16 vid =3D mdb->vid; + + return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid); +} + +int qca8k_port_mdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) +{ + struct qca8k_priv *priv =3D ds->priv; + const u8 *addr =3D mdb->addr; + u16 vid =3D mdb->vid; + + return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid); +} + +int qca8k_port_mirror_add(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror, + bool ingress, struct netlink_ext_ack *extack) +{ + struct qca8k_priv *priv =3D ds->priv; + int monitor_port, ret; + u32 reg, val; + + /* Check for existent entry */ + if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port)) + return -EEXIST; + + ret =3D regmap_read(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, &val); + if (ret) + return ret; + + /* QCA83xx can have only one port set to mirror mode. + * Check that the correct port is requested and return error otherwise. + * When no mirror port is set, the values is set to 0xF + */ + monitor_port =3D FIELD_GET(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); + if (monitor_port !=3D 0xF && monitor_port !=3D mirror->to_local_port) + return -EEXIST; + + /* Set the monitor port */ + val =3D FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, + mirror->to_local_port); + ret =3D regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, + QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); + if (ret) + return ret; + + if (ingress) { + reg =3D QCA8K_PORT_LOOKUP_CTRL(port); + val =3D QCA8K_PORT_LOOKUP_ING_MIRROR_EN; + } else { + reg =3D QCA8K_REG_PORT_HOL_CTRL1(port); + val =3D QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN; + } + + ret =3D regmap_update_bits(priv->regmap, reg, val, val); + if (ret) + return ret; + + /* Track mirror port for tx and rx to decide when the + * mirror port has to be disabled. + */ + if (ingress) + priv->mirror_rx |=3D BIT(port); + else + priv->mirror_tx |=3D BIT(port); + + return 0; +} + +void qca8k_port_mirror_del(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror) +{ + struct qca8k_priv *priv =3D ds->priv; + u32 reg, val; + int ret; + + if (mirror->ingress) { + reg =3D QCA8K_PORT_LOOKUP_CTRL(port); + val =3D QCA8K_PORT_LOOKUP_ING_MIRROR_EN; + } else { + reg =3D QCA8K_REG_PORT_HOL_CTRL1(port); + val =3D QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN; + } + + ret =3D regmap_clear_bits(priv->regmap, reg, val); + if (ret) + goto err; + + if (mirror->ingress) + priv->mirror_rx &=3D ~BIT(port); + else + priv->mirror_tx &=3D ~BIT(port); + + /* No port set to send packet to mirror port. Disable mirror port */ + if (!priv->mirror_rx && !priv->mirror_tx) { + val =3D FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, 0xF); + ret =3D regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, + QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); + if (ret) + goto err; + } +err: + dev_err(priv->dev, "Failed to del mirror port from %d", port); +} + +int qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_f= iltering, + struct netlink_ext_ack *extack) +{ + struct qca8k_priv *priv =3D ds->priv; + int ret; + + if (vlan_filtering) { + ret =3D regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), + QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, + QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE); + } else { + ret =3D regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), + QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, + QCA8K_PORT_LOOKUP_VLAN_MODE_NONE); + } + + return ret; +} + +int qca8k_port_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack) +{ + bool untagged =3D vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; + bool pvid =3D vlan->flags & BRIDGE_VLAN_INFO_PVID; + struct qca8k_priv *priv =3D ds->priv; + int ret; + + ret =3D qca8k_vlan_add(priv, port, vlan->vid, untagged); + if (ret) { + dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret); + return ret; + } + + if (pvid) { + ret =3D regmap_update_bits(priv->regmap, QCA8K_EGRESS_VLAN(port), + QCA8K_EGREES_VLAN_PORT_MASK(port), + QCA8K_EGREES_VLAN_PORT(port, vlan->vid)); + if (ret) + return ret; + + ret =3D regmap_write(priv->regmap, QCA8K_REG_PORT_VLAN_CTRL0(port), + QCA8K_PORT_VLAN_CVID(vlan->vid) | + QCA8K_PORT_VLAN_SVID(vlan->vid)); + } + + return ret; +} + +int qca8k_port_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + struct qca8k_priv *priv =3D ds->priv; + int ret; + + ret =3D qca8k_vlan_del(priv, port, vlan->vid); + if (ret) + dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret); + + return ret; +} + +static bool +qca8k_lag_can_offload(struct dsa_switch *ds, struct dsa_lag lag, + struct netdev_lag_upper_info *info) +{ + struct dsa_port *dp; + int members =3D 0; + + if (!lag.id) + return false; + + dsa_lag_foreach_port(dp, ds->dst, &lag) + /* Includes the port joining the LAG */ + members++; + + if (members > QCA8K_NUM_PORTS_FOR_LAG) + return false; + + if (info->tx_type !=3D NETDEV_LAG_TX_TYPE_HASH) + return false; + + if (info->hash_type !=3D NETDEV_LAG_HASH_L2 && + info->hash_type !=3D NETDEV_LAG_HASH_L23) + return false; + + return true; +} + +static int +qca8k_lag_setup_hash(struct dsa_switch *ds, struct dsa_lag lag, + struct netdev_lag_upper_info *info) +{ + struct net_device *lag_dev =3D lag.dev; + struct qca8k_priv *priv =3D ds->priv; + bool unique_lag =3D true; + unsigned int i; + u32 hash =3D 0; + + switch (info->hash_type) { + case NETDEV_LAG_HASH_L23: + hash |=3D QCA8K_TRUNK_HASH_SIP_EN; + hash |=3D QCA8K_TRUNK_HASH_DIP_EN; + fallthrough; + case NETDEV_LAG_HASH_L2: + hash |=3D QCA8K_TRUNK_HASH_SA_EN; + hash |=3D QCA8K_TRUNK_HASH_DA_EN; + break; + default: /* We should NEVER reach this */ + return -EOPNOTSUPP; + } + + /* Check if we are the unique configured LAG */ + dsa_lags_foreach_id(i, ds->dst) + if (i !=3D lag.id && dsa_lag_by_id(ds->dst, i)) { + unique_lag =3D false; + break; + } + + /* Hash Mode is global. Make sure the same Hash Mode + * is set to all the 4 possible lag. + * If we are the unique LAG we can set whatever hash + * mode we want. + * To change hash mode it's needed to remove all LAG + * and change the mode with the latest. + */ + if (unique_lag) { + priv->lag_hash_mode =3D hash; + } else if (priv->lag_hash_mode !=3D hash) { + netdev_err(lag_dev, "Error: Mismatched Hash Mode across different lag is= not supported\n"); + return -EOPNOTSUPP; + } + + return regmap_update_bits(priv->regmap, QCA8K_TRUNK_HASH_EN_CTRL, + QCA8K_TRUNK_HASH_MASK, hash); +} + +static int +qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port, + struct dsa_lag lag, bool delete) +{ + struct qca8k_priv *priv =3D ds->priv; + int ret, id, i; + u32 val; + + /* DSA LAG IDs are one-based, hardware is zero-based */ + id =3D lag.id - 1; + + /* Read current port member */ + ret =3D regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val); + if (ret) + return ret; + + /* Shift val to the correct trunk */ + val >>=3D QCA8K_REG_GOL_TRUNK_SHIFT(id); + val &=3D QCA8K_REG_GOL_TRUNK_MEMBER_MASK; + if (delete) + val &=3D ~BIT(port); + else + val |=3D BIT(port); + + /* Update port member. With empty portmap disable trunk */ + ret =3D regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, + QCA8K_REG_GOL_TRUNK_MEMBER(id) | + QCA8K_REG_GOL_TRUNK_EN(id), + !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) | + val << QCA8K_REG_GOL_TRUNK_SHIFT(id)); + + /* Search empty member if adding or port on deleting */ + for (i =3D 0; i < QCA8K_NUM_PORTS_FOR_LAG; i++) { + ret =3D regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val); + if (ret) + return ret; + + val >>=3D QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i); + val &=3D QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK; + + if (delete) { + /* If port flagged to be disabled assume this member is + * empty + */ + if (val !=3D QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK) + continue; + + val &=3D QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK; + if (val !=3D port) + continue; + } else { + /* If port flagged to be enabled assume this member is + * already set + */ + if (val =3D=3D QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK) + continue; + } + + /* We have found the member to add/remove */ + break; + } + + /* Set port in the correct port mask or disable port if in delete mode */ + return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), + QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) | + QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i), + !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) | + port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i)); +} + +int qca8k_port_lag_join(struct dsa_switch *ds, int port, struct dsa_lag la= g, + struct netdev_lag_upper_info *info) +{ + int ret; + + if (!qca8k_lag_can_offload(ds, lag, info)) + return -EOPNOTSUPP; + + ret =3D qca8k_lag_setup_hash(ds, lag, info); + if (ret) + return ret; + + return qca8k_lag_refresh_portmap(ds, port, lag, false); +} + +int qca8k_port_lag_leave(struct dsa_switch *ds, int port, + struct dsa_lag lag) +{ + return qca8k_lag_refresh_portmap(ds, port, lag, true); +} + +int qca8k_read_switch_id(struct qca8k_priv *priv) +{ + const struct qca8k_match_data *data; + u32 val; + u8 id; + int ret; + + /* get the switches ID from the compatible */ + data =3D of_device_get_match_data(priv->dev); + if (!data) + return -ENODEV; + + ret =3D regmap_read(priv->regmap, QCA8K_REG_MASK_CTRL, &val); + if (ret < 0) + return -ENODEV; + + id =3D QCA8K_MASK_CTRL_DEVICE_ID(val); + if (id !=3D data->id) { + dev_err(priv->dev, "Switch id detected %x but expected %x", id, data->id= ); + return -ENODEV; + } + + priv->switch_id =3D id; + + /* Save revision to communicate to the internal PHY driver */ + priv->switch_revision =3D QCA8K_MASK_CTRL_REV_ID(val); + + return 0; +} diff --git a/drivers/net/dsa/qca/qca8k.h b/drivers/net/dsa/qca/qca8k.h index a306638a7100..96e726e65185 100644 --- a/drivers/net/dsa/qca/qca8k.h +++ b/drivers/net/dsa/qca/qca8k.h @@ -419,4 +419,62 @@ struct qca8k_fdb { u8 mac[6]; }; =20 +/* Common setup function */ +extern const struct qca8k_mib_desc ar8327_mib[]; +extern const struct regmap_access_table qca8k_readable_table; + +int qca8k_read_switch_id(struct qca8k_priv *priv); +int qca8k_mib_init(struct qca8k_priv *priv); +void qca8k_fdb_flush(struct qca8k_priv *priv); +void qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable); + +/* Common ops function */ +void qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uin= t8_t *data); +void qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *da= ta); +int qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset); +int qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee = *eee); +int qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee = *e); +void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state); +int qca8k_port_bridge_join(struct dsa_switch *ds, int port, + struct dsa_bridge bridge, + bool *tx_fwd_offload, + struct netlink_ext_ack *extack); +void qca8k_port_bridge_leave(struct dsa_switch *ds, int port, + struct dsa_bridge bridge); +void qca8k_port_fast_age(struct dsa_switch *ds, int port); +int qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs); +int qca8k_port_enable(struct dsa_switch *ds, int port, struct phy_device *= phy); +void qca8k_port_disable(struct dsa_switch *ds, int port); +int qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu); +int qca8k_port_max_mtu(struct dsa_switch *ds, int port); +int qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, u16 por= t_mask, u16 vid); +int qca8k_port_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db); +int qca8k_port_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db); +int qca8k_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t= *cb, void *data); +int qca8k_port_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db); +int qca8k_port_mdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db); +int qca8k_port_mirror_add(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror, + bool ingress, struct netlink_ext_ack *extack); +void qca8k_port_mirror_del(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror); +int qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_f= iltering, + struct netlink_ext_ack *extack); +int qca8k_port_vlan_add(struct dsa_switch *ds, int port, const struct swit= chdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack); +int qca8k_port_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan); +int qca8k_port_lag_join(struct dsa_switch *ds, int port, struct dsa_lag la= g, + struct netdev_lag_upper_info *info); +int qca8k_port_lag_leave(struct dsa_switch *ds, int port, + struct dsa_lag lag); + #endif /* __QCA8K_H */ --=20 2.36.1