From nobody Sun May 24 22:35:50 2026 Received: from pidgin.makrotopia.org (pidgin.makrotopia.org [185.142.180.65]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A5BF0375AC4; Thu, 21 May 2026 02:40:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.142.180.65 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779331233; cv=none; b=iG9gJ0mKF6mm5HOTs/010qewJZ6mfj0wSPyvilo+B5jXjPvB/AnUIgUlxtC6zCGRtOrWPVbWREMiQObtUmWI+xhDIdfJQGryxwLlHdRnOZHUQGmavLVcIgPa0VgTKinB7WFzpbkl29gqo1/D6HC4qFsYVWxjnkkhoEsDPhWiejs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779331233; c=relaxed/simple; bh=JS6YJFhMRLBVyhGHHkpEiJdsMIKUWJEUOjH/ygM6dFY=; h=Date:From:To:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=ZrnzcLx4W265hPi1teFoBwrnfkX6a/D/jzN0Wslqo2/YVvpVBVKnPmR30dbX4EMvoCfCB6CsP/NEt5btkCh5kW70gVCwBF4Q9SGdhIirM0vApA34FeitwDjVy6W9SIPvjJndrApQtZOwsfa84/5Z4nR9CQx/pMU7rJta1i/9QQM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=makrotopia.org; spf=pass smtp.mailfrom=makrotopia.org; arc=none smtp.client-ip=185.142.180.65 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=makrotopia.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=makrotopia.org Received: from local by pidgin.makrotopia.org with esmtpsa (TLS1.3:TLS_AES_256_GCM_SHA384:256) (Exim 4.99) (envelope-from ) id 1wPtKl-0000000034L-3iIm; Thu, 21 May 2026 02:40:27 +0000 Date: Thu, 21 May 2026 03:40:25 +0100 From: Daniel Golle To: Daniel Golle , Andrew Lunn , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Russell King , linux-kernel@vger.kernel.org, netdev@vger.kernel.org Subject: [PATCH v2 1/4] net: dsa: mxl862xx: store firmware version for feature gating Message-ID: <98e1645882734e0c64fc5755b00c4f659b355bff.1779330653.git.daniel@makrotopia.org> References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Query the firmware version at init (already done in wait_ready), cache it in priv->fw_version, and provide MXL862XX_FW_VER_MIN() for version-gated code paths throughout the driver. The union mxl862xx_fw_version lays out major/minor/revision so that the u32 raw field compares with natural version ordering on both big- and little-endian machines. Signed-off-by: Daniel Golle --- v2: no changes drivers/net/dsa/mxl862xx/mxl862xx.c | 3 +++ drivers/net/dsa/mxl862xx/mxl862xx.h | 36 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/drivers/net/dsa/mxl862xx/mxl862xx.c b/drivers/net/dsa/mxl862xx= /mxl862xx.c index b60482d93a85..2f22adedfbf6 100644 --- a/drivers/net/dsa/mxl862xx/mxl862xx.c +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c @@ -257,6 +257,9 @@ static int mxl862xx_wait_ready(struct dsa_switch *ds) ver.iv_major, ver.iv_minor, le16_to_cpu(ver.iv_revision), le32_to_cpu(ver.iv_build_num)); + priv->fw_version.major =3D ver.iv_major; + priv->fw_version.minor =3D ver.iv_minor; + priv->fw_version.revision =3D le16_to_cpu(ver.iv_revision); return 0; =20 not_ready_yet: diff --git a/drivers/net/dsa/mxl862xx/mxl862xx.h b/drivers/net/dsa/mxl862xx= /mxl862xx.h index 80053ab40e4c..79fd32c4db4e 100644 --- a/drivers/net/dsa/mxl862xx/mxl862xx.h +++ b/drivers/net/dsa/mxl862xx/mxl862xx.h @@ -3,6 +3,7 @@ #ifndef __MXL862XX_H #define __MXL862XX_H =20 +#include #include #include #include @@ -241,6 +242,38 @@ struct mxl862xx_port { spinlock_t stats_lock; /* protects stats accumulators */ }; =20 +/** + * union mxl862xx_fw_version - firmware version for comparison and display + * @major: firmware major version + * @minor: firmware minor version + * @revision: firmware revision number + * @raw: combined u32 for direct >=3D comparison (major most significant) + * + * The struct layout places major in the most-significant byte of the + * u32 on both big- and little-endian machines, so raw values compare + * with the natural major > minor > revision ordering. + */ +union mxl862xx_fw_version { + struct { +#if defined(__BIG_ENDIAN) + u8 major; + u8 minor; + u16 revision; +#elif defined(__LITTLE_ENDIAN) + u16 revision; + u8 minor; + u8 major; +#endif + }; + u32 raw; +}; + +#define MXL862XX_FW_VER(maj, min, rev) \ + ((union mxl862xx_fw_version){ .major =3D (maj), .minor =3D (min), \ + .revision =3D (rev) }).raw +#define MXL862XX_FW_VER_MIN(priv, maj, min, rev) \ + ((priv)->fw_version.raw >=3D MXL862XX_FW_VER(maj, min, rev)) + /* Bit indices for struct mxl862xx_priv::flags */ #define MXL862XX_FLAG_CRC_ERR 0 #define MXL862XX_FLAG_WORK_STOPPED 1 @@ -258,6 +291,8 @@ struct mxl862xx_port { * @drop_meter: index of the single shared zero-rate firmware meter * used to unconditionally drop traffic (used to block * flooding) + * @fw_version: cached firmware version, populated at probe and + * compared with MXL862XX_FW_VER_MIN() * @ports: per-port state, indexed by switch port number * @bridges: maps DSA bridge number to firmware bridge ID; * zero means no firmware bridge allocated for that @@ -275,6 +310,7 @@ struct mxl862xx_priv { struct work_struct crc_err_work; unsigned long flags; u16 drop_meter; + union mxl862xx_fw_version fw_version; struct mxl862xx_port ports[MXL862XX_MAX_PORTS]; u16 bridges[MXL862XX_MAX_BRIDGES + 1]; u16 evlan_ingress_size; --=20 2.54.0 From nobody Sun May 24 22:35:50 2026 Received: from pidgin.makrotopia.org (pidgin.makrotopia.org [185.142.180.65]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 866C1375F99; Thu, 21 May 2026 02:40:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.142.180.65 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779331239; cv=none; b=pHAVlXabBdh+k0TUfTF8tUEMxhl3P4ZF0b/4VIgO/hrntzLOdgx/iBGgeQLruGVxfZCjc+1s+a4vH058TsAUMMsYhd4QmRwxY3iqi84Oy/d0G83waotv60gxk4VzytuQgxNQQ+SksDtYDQU1jDqgBFuLZpX6q7LmNkKmxMJo3Ik= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779331239; c=relaxed/simple; bh=mLuiGbSk4Q8qCTty4GAsumv4dLcoTqSlanXHByccKTI=; h=Date:From:To:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=kqd+pZT2N/rHcybMzMFEbf4lWmJNYs8gLng3Cbfn8zWe5Kqmzc5cuWbZQxLYoJD4MeD11eo+mvq92ib/C7rMQNYJ2MZXv4iWjpTnjaETiCSdkH2yPpQGWYBpY6TSCtvuLJiHbV4Axck4LnG1lxFa5wLo0itc3r1gPDItyVFm4Jk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=makrotopia.org; spf=pass smtp.mailfrom=makrotopia.org; arc=none smtp.client-ip=185.142.180.65 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=makrotopia.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=makrotopia.org Received: from local by pidgin.makrotopia.org with esmtpsa (TLS1.3:TLS_AES_256_GCM_SHA384:256) (Exim 4.99) (envelope-from ) id 1wPtKr-0000000034g-3liO; Thu, 21 May 2026 02:40:34 +0000 Date: Thu, 21 May 2026 03:40:31 +0100 From: Daniel Golle To: Daniel Golle , Andrew Lunn , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Russell King , linux-kernel@vger.kernel.org, netdev@vger.kernel.org Subject: [PATCH v2 2/4] net: dsa: mxl862xx: move phylink stubs to mxl862xx-phylink.c Message-ID: <8e31ea20fa8a7650176ce4c9c1603ee34571eeb5.1779330653.git.daniel@makrotopia.org> References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Move the phylink MAC operations and get_caps callback from mxl862xx.c into a dedicated mxl862xx-phylink.c file. This prepares for the SerDes PCS implementation which adds substantial phylink/PCS code -- keeping it in a separate file avoids function-position churn in the main driver file. No functional change. Signed-off-by: Daniel Golle Reviewed-by: Maxime Chevallier --- v2: no changes drivers/net/dsa/mxl862xx/Makefile | 2 +- drivers/net/dsa/mxl862xx/mxl862xx-phylink.c | 51 +++++++++++++++++++++ drivers/net/dsa/mxl862xx/mxl862xx-phylink.h | 14 ++++++ drivers/net/dsa/mxl862xx/mxl862xx.c | 38 +-------------- 4 files changed, 67 insertions(+), 38 deletions(-) create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-phylink.c create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-phylink.h diff --git a/drivers/net/dsa/mxl862xx/Makefile b/drivers/net/dsa/mxl862xx/M= akefile index d23dd3cd511d..a7be0e6669df 100644 --- a/drivers/net/dsa/mxl862xx/Makefile +++ b/drivers/net/dsa/mxl862xx/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_NET_DSA_MXL862) +=3D mxl862xx_dsa.o -mxl862xx_dsa-y :=3D mxl862xx.o mxl862xx-host.o +mxl862xx_dsa-y :=3D mxl862xx.o mxl862xx-host.o mxl862xx-phylink.o diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c b/drivers/net/dsa/= mxl862xx/mxl862xx-phylink.c new file mode 100644 index 000000000000..f17c429d1f1d --- /dev/null +++ b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Phylink and PCS support for MaxLinear MxL862xx switch family + * + * Copyright (C) 2024 MaxLinear Inc. + * Copyright (C) 2025 John Crispin + * Copyright (C) 2025 Daniel Golle + */ + +#include +#include + +#include "mxl862xx.h" +#include "mxl862xx-phylink.h" + +void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) +{ + config->mac_capabilities =3D MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | + MAC_100 | MAC_1000 | MAC_2500FD; + + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + config->supported_interfaces); +} + +static void mxl862xx_phylink_mac_config(struct phylink_config *config, + unsigned int mode, + const struct phylink_link_state *state) +{ +} + +static void mxl862xx_phylink_mac_link_down(struct phylink_config *config, + unsigned int mode, + phy_interface_t interface) +{ +} + +static void mxl862xx_phylink_mac_link_up(struct phylink_config *config, + struct phy_device *phydev, + unsigned int mode, + phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) +{ +} + +const struct phylink_mac_ops mxl862xx_phylink_mac_ops =3D { + .mac_config =3D mxl862xx_phylink_mac_config, + .mac_link_down =3D mxl862xx_phylink_mac_link_down, + .mac_link_up =3D mxl862xx_phylink_mac_link_up, +}; diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-phylink.h b/drivers/net/dsa/= mxl862xx/mxl862xx-phylink.h new file mode 100644 index 000000000000..c3d5215bdf60 --- /dev/null +++ b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __MXL862XX_PHYLINK_H +#define __MXL862XX_PHYLINK_H + +#include + +#include "mxl862xx.h" + +extern const struct phylink_mac_ops mxl862xx_phylink_mac_ops; +void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config); + +#endif /* __MXL862XX_PHYLINK_H */ diff --git a/drivers/net/dsa/mxl862xx/mxl862xx.c b/drivers/net/dsa/mxl862xx= /mxl862xx.c index 2f22adedfbf6..a193f3c07d35 100644 --- a/drivers/net/dsa/mxl862xx/mxl862xx.c +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c @@ -22,6 +22,7 @@ #include "mxl862xx-api.h" #include "mxl862xx-cmd.h" #include "mxl862xx-host.h" +#include "mxl862xx-phylink.h" =20 #define MXL862XX_API_WRITE(dev, cmd, data) \ mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), false, false) @@ -1424,16 +1425,6 @@ static void mxl862xx_port_teardown(struct dsa_switch= *ds, int port) priv->ports[port].setup_done =3D false; } =20 -static void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port, - struct phylink_config *config) -{ - config->mac_capabilities =3D MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | - MAC_100 | MAC_1000 | MAC_2500FD; - - __set_bit(PHY_INTERFACE_MODE_INTERNAL, - config->supported_interfaces); -} - static int mxl862xx_get_fid(struct dsa_switch *ds, struct dsa_db db) { struct mxl862xx_priv *priv =3D ds->priv; @@ -2099,33 +2090,6 @@ static const struct dsa_switch_ops mxl862xx_switch_o= ps =3D { .get_stats64 =3D mxl862xx_get_stats64, }; =20 -static void mxl862xx_phylink_mac_config(struct phylink_config *config, - unsigned int mode, - const struct phylink_link_state *state) -{ -} - -static void mxl862xx_phylink_mac_link_down(struct phylink_config *config, - unsigned int mode, - phy_interface_t interface) -{ -} - -static void mxl862xx_phylink_mac_link_up(struct phylink_config *config, - struct phy_device *phydev, - unsigned int mode, - phy_interface_t interface, - int speed, int duplex, - bool tx_pause, bool rx_pause) -{ -} - -static const struct phylink_mac_ops mxl862xx_phylink_mac_ops =3D { - .mac_config =3D mxl862xx_phylink_mac_config, - .mac_link_down =3D mxl862xx_phylink_mac_link_down, - .mac_link_up =3D mxl862xx_phylink_mac_link_up, -}; - static int mxl862xx_probe(struct mdio_device *mdiodev) { struct device *dev =3D &mdiodev->dev; --=20 2.54.0 From nobody Sun May 24 22:35:50 2026 Received: from pidgin.makrotopia.org (pidgin.makrotopia.org [185.142.180.65]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9C9DE3148C9; Thu, 21 May 2026 02:40:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.142.180.65 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779331245; cv=none; b=fAiGVwAFieSLp6Q8EJ4O2qz/4S1p/kUgV3rthkzExksAy7LQuiSOg/scKtP7zMNHag/7xfocUZOJN1uje3ifGuF7lIXYYQkrmVRqDrHsnaiNtVYCleIyVNQnR2oif6ItgPwD2HWS1wC2tKO/yymzL6WDa9oZB5FK0h/M2nwNprw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779331245; c=relaxed/simple; bh=74+/mhGsubGkzR1Q3/tbIF1S5yRos9UN6wCOqR//nqM=; h=Date:From:To:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=P7i2UWRdlvH4RPIfGEkN6eyyRV93IpWjJhZ8U4eBUAb9MP4Ii06eYeHoeaeKNtxXOIYk2H80Zorr8wX/jH2BfwI+dGhbJ7d4p3tAM9+4cmlhbmjr7/4oq0ckf6IuCF8ifUC64uAFvwm70vtm+gXoI8ogNh+HW0RKtUXkaWFNLAc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=makrotopia.org; spf=pass smtp.mailfrom=makrotopia.org; arc=none smtp.client-ip=185.142.180.65 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=makrotopia.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=makrotopia.org Received: from local by pidgin.makrotopia.org with esmtpsa (TLS1.3:TLS_AES_256_GCM_SHA384:256) (Exim 4.99) (envelope-from ) id 1wPtKz-0000000034y-0ZOV; Thu, 21 May 2026 02:40:41 +0000 Date: Thu, 21 May 2026 03:40:38 +0100 From: Daniel Golle To: Daniel Golle , Andrew Lunn , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Russell King , linux-kernel@vger.kernel.org, netdev@vger.kernel.org Subject: [PATCH v2 3/4] net: dsa: mxl862xx: move API macros to mxl862xx-host.h Message-ID: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Move the MXL862XX_API_WRITE, MXL862XX_API_READ and MXL862XX_API_READ_QUIET convenience macros from mxl862xx.c to mxl862xx-host.h next to the mxl862xx_api_wrap() prototype they wrap. This makes them available to other compilation units that include mxl862xx-host.h, which is needed once the SerDes PCS code in mxl862xx-phylink.c also calls firmware commands. No functional change. Signed-off-by: Daniel Golle Reviewed-by: Maxime Chevallier --- v2: no changes drivers/net/dsa/mxl862xx/mxl862xx-host.h | 8 ++++++++ drivers/net/dsa/mxl862xx/mxl862xx.c | 7 ------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-host.h b/drivers/net/dsa/mxl= 862xx/mxl862xx-host.h index 84512a30bc18..66d6ae198aff 100644 --- a/drivers/net/dsa/mxl862xx/mxl862xx-host.h +++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.h @@ -9,6 +9,14 @@ void mxl862xx_host_init(struct mxl862xx_priv *priv); void mxl862xx_host_shutdown(struct mxl862xx_priv *priv); int mxl862xx_api_wrap(struct mxl862xx_priv *priv, u16 cmd, void *data, u16= size, bool read, bool quiet); + +#define MXL862XX_API_WRITE(dev, cmd, data) \ + mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), false, false) +#define MXL862XX_API_READ(dev, cmd, data) \ + mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true, false) +#define MXL862XX_API_READ_QUIET(dev, cmd, data) \ + mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true, true) + int mxl862xx_reset(struct mxl862xx_priv *priv); =20 #endif /* __MXL862XX_HOST_H */ diff --git a/drivers/net/dsa/mxl862xx/mxl862xx.c b/drivers/net/dsa/mxl862xx= /mxl862xx.c index a193f3c07d35..0b1a23364eb5 100644 --- a/drivers/net/dsa/mxl862xx/mxl862xx.c +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c @@ -24,13 +24,6 @@ #include "mxl862xx-host.h" #include "mxl862xx-phylink.h" =20 -#define MXL862XX_API_WRITE(dev, cmd, data) \ - mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), false, false) -#define MXL862XX_API_READ(dev, cmd, data) \ - mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true, false) -#define MXL862XX_API_READ_QUIET(dev, cmd, data) \ - mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true, true) - /* Polling interval for RMON counter accumulation. At 2.5 Gbps with * minimum-size (64-byte) frames, a 32-bit packet counter wraps in ~880s. * 2s gives a comfortable margin. --=20 2.54.0 From nobody Sun May 24 22:35:50 2026 Received: from pidgin.makrotopia.org (pidgin.makrotopia.org [185.142.180.65]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AE70C3148C9; Thu, 21 May 2026 02:41:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.142.180.65 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779331275; cv=none; b=R6DHMW5IMQeP0chVOsCEfyiFtRSZHilEtVYjsRgAdSxTfDUqUsquMB2vYjgibSS5ZwIFbwvLtIEGML0noVqOJv9VcSSZb4XKK/P2kX+TRkM5SgvV6yAJVMmG2QzhUzvZreG890NCj2PEN0b9xM4rC9UBvPuAvIY77ai5htYESHI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779331275; c=relaxed/simple; bh=HnCpuZ4wKa3bnpfVoMDk6JPf0RPi/g4kKEDZvBbWIdE=; h=Date:From:To:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=Mhy+twODjrJ3h6mhvfU9I+FqLJjqZUIRD7SCx2DRNrjyFAxJi/tCDPXdakYIOXn4omWCZ+SVNRA5tuH5Y7redxsWcgK/V/WRZugSHPvTl8i9AJKxcy/j1KSkyqVr4pgU/rk/FYb0G8Gf2V9zaN0smI4n5oIgOhl+Gqxp2RsKFBI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=makrotopia.org; spf=pass smtp.mailfrom=makrotopia.org; arc=none smtp.client-ip=185.142.180.65 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=makrotopia.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=makrotopia.org Received: from local by pidgin.makrotopia.org with esmtpsa (TLS1.3:TLS_AES_256_GCM_SHA384:256) (Exim 4.99) (envelope-from ) id 1wPtLP-0000000035f-3cqY; Thu, 21 May 2026 02:41:08 +0000 Date: Thu, 21 May 2026 03:41:05 +0100 From: Daniel Golle To: Daniel Golle , Andrew Lunn , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Russell King , linux-kernel@vger.kernel.org, netdev@vger.kernel.org Subject: [PATCH v2 4/4] net: dsa: mxl862xx: add support for SerDes ports Message-ID: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The MxL862xx has two XPCS/SerDes interfaces (XPCS0 for ports 9-12, XPCS1 for ports 13-16). Each can operate in various single-lane modes (SGMII, 1000Base-X, 2500Base-X, 10GBase-R, 10GBase-KR, USXGMII) or as QSGMII or 10G_QXGMII providing four sub-ports per interface. Implement phylink PCS operations using the firmware's XPCS API: - pcs_config: configure negotiation mode and CL37/SGMII advertising. - pcs_get_state: read link/speed/duplex/LPA from firmware and decode using phylink's standard CL37, SGMII, and USXGMII decoders, with firmware-resolved speed/duplex override for downshift detection. - pcs_an_restart: restart CL37 or CL73 auto-negotiation. - pcs_link_up: force speed/duplex for SGMII. - pcs_inband_caps: report per-mode in-band status capabilities. Register a PCS instance for each SerDes interface and QSGMII/10G_QXGMII sub-ports during setup. Advertise the supported interface modes in phylink_get_caps based on port number. Lacking support for expressing PHY-side role modes in Linux only the MAC-side of SGMII, QSGMII, USXGMII and 10G_QXGMII are implemented for now. Signed-off-by: Daniel Golle --- v2: * add __{LE,BE}_BITFIELD layouts to ABI structs * per-sub-port QSGMII AN restart via usx_subport / usx_lane_mode * shared-SerDes refcount in pcs_disable via per-XPCS slot bitmap * let every sub-port call pcs_config * cache phy_interface_t instead of firmware type * skip pcs_link_up when inband-AN enabled * gate phylink_get_caps SerDes modes on same FW version as select_pcs * interpret xpcs_pcs_cfg.result as signed (s16) * drop dead MXL862XX_PCS_PORT macro * drop misleading "downshift detection" line from commit message drivers/net/dsa/mxl862xx/mxl862xx-api.h | 358 ++++++++++++++++++++ drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 9 + drivers/net/dsa/mxl862xx/mxl862xx-phylink.c | 332 +++++++++++++++++- drivers/net/dsa/mxl862xx/mxl862xx-phylink.h | 7 + drivers/net/dsa/mxl862xx/mxl862xx.c | 6 +- drivers/net/dsa/mxl862xx/mxl862xx.h | 31 ++ 6 files changed, 740 insertions(+), 3 deletions(-) diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-api.h b/drivers/net/dsa/mxl8= 62xx/mxl862xx-api.h index fb21ddc1bf1c..f55544772376 100644 --- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h +++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h @@ -1366,4 +1366,362 @@ struct mxl862xx_rmon_port_cnt { __le64 tx_good_bytes; } __packed; =20 +/** + * enum mxl862xx_xpcs_if_mode - XPCS interface mode + * @MXL862XX_XPCS_IF_SGMII: SGMII + * @MXL862XX_XPCS_IF_1000BASEX: 1000BASE-X + * @MXL862XX_XPCS_IF_2500BASEX: 2500BASE-X + * @MXL862XX_XPCS_IF_USXGMII: USXGMII (single or quad) + * @MXL862XX_XPCS_IF_10GBASER: 10GBASE-R + * @MXL862XX_XPCS_IF_10GKR: 10GBASE-KR + * @MXL862XX_XPCS_IF_5GBASER: 5GBASE-R + * @MXL862XX_XPCS_IF_QSGMII: QSGMII + */ +enum mxl862xx_xpcs_if_mode { + MXL862XX_XPCS_IF_SGMII =3D 0, + MXL862XX_XPCS_IF_1000BASEX =3D 1, + MXL862XX_XPCS_IF_2500BASEX =3D 2, + MXL862XX_XPCS_IF_USXGMII =3D 3, + MXL862XX_XPCS_IF_10GBASER =3D 4, + MXL862XX_XPCS_IF_10GKR =3D 5, + MXL862XX_XPCS_IF_5GBASER =3D 6, + MXL862XX_XPCS_IF_QSGMII =3D 7, +}; + +/** + * enum mxl862xx_xpcs_neg_mode - PCS negotiation mode + * @MXL862XX_XPCS_NEG_NONE: no inband negotiation + * @MXL862XX_XPCS_NEG_INBAND_AN_OFF: inband selected but AN disabled + * @MXL862XX_XPCS_NEG_INBAND_AN_ON: inband with AN enabled + */ +enum mxl862xx_xpcs_neg_mode { + MXL862XX_XPCS_NEG_NONE =3D 0, + MXL862XX_XPCS_NEG_INBAND_AN_OFF =3D 1, + MXL862XX_XPCS_NEG_INBAND_AN_ON =3D 2, +}; + +/** + * enum mxl862xx_xpcs_role - PCS protocol role + * @MXL862XX_XPCS_ROLE_MAC: local end is MAC side (TX_CONFIG =3D 0) + * @MXL862XX_XPCS_ROLE_PHY: local end is PHY side (TX_CONFIG =3D 1) + * + * Selects the role the XPCS plays in protocols that have an asymmetric + * AN code word (Cisco SGMII / QSGMII / USXGMII). Drives + * VR_MII_AN_CTRL.TX_CONFIG: 0 means the local end receives the partner's + * AN word, 1 means it sources one. Ignored for symmetric protocols + * (1000BASE-X, 2500BASE-X, 10GBASE-R/KR). + */ +enum mxl862xx_xpcs_role { + MXL862XX_XPCS_ROLE_MAC =3D 0, + MXL862XX_XPCS_ROLE_PHY =3D 1, +}; + +/** + * enum mxl862xx_xpcs_usx_lane_mode - USXGMII lane mode + * @MXL862XX_XPCS_USX_SINGLE: single USXGMII lane + * @MXL862XX_XPCS_USX_QUAD: quad USXGMII (4 ports per lane) + */ +enum mxl862xx_xpcs_usx_lane_mode { + MXL862XX_XPCS_USX_SINGLE =3D 0, + MXL862XX_XPCS_USX_QUAD =3D 1, +}; + +/** + * union mxl862xx_xpcs_an_word - XPCS AN code word, tagged by interface mo= de + * @cl37: 16-bit base-page word exchanged over the CL37 hardware AN path + * (SR_MII_AN_ADV on write, SR_MII_LP_BABL on read). Carries the + * 802.3 CL37 base page for 1000BASE-X/2500BASE-X and the Cisco + * SGMII config word for SGMII/QSGMII. + * @usx: USXGMII 16-bit AN code word, MDIO_USXGMII_* layout + * @cl73: CL73 48-bit base page (10GBASE-KR), three 16-bit registers per + * 802.3 Annex 28C + * @cl73.adv1: CL73 SR_AN_ADV1 / SR_AN_LP_ABL1 + * @cl73.adv2: CL73 SR_AN_ADV2 / SR_AN_LP_ABL2 + * @cl73.adv3: CL73 SR_AN_ADV3 / SR_AN_LP_ABL3 + * + * The host picks the right member based on the interface field of the + * surrounding struct (and, for the asymmetric protocols, on the role). + */ +union mxl862xx_xpcs_an_word { + __le16 cl37; + __le16 usx; + struct { + __le16 adv1; + __le16 adv2; + __le16 adv3; + } cl73; +} __packed; + +/** + * enum mxl862xx_xpcs_speed - PCS speed values + * @MXL862XX_XPCS_SPEED_UNKNOWN: unknown speed + * @MXL862XX_XPCS_SPEED_10: 10 Mbps + * @MXL862XX_XPCS_SPEED_100: 100 Mbps + * @MXL862XX_XPCS_SPEED_1000: 1000 Mbps + * @MXL862XX_XPCS_SPEED_2500: 2500 Mbps + * @MXL862XX_XPCS_SPEED_5000: 5000 Mbps + * @MXL862XX_XPCS_SPEED_10000: 10000 Mbps + */ +enum mxl862xx_xpcs_speed { + MXL862XX_XPCS_SPEED_UNKNOWN =3D 0, + MXL862XX_XPCS_SPEED_10 =3D 10, + MXL862XX_XPCS_SPEED_100 =3D 100, + MXL862XX_XPCS_SPEED_1000 =3D 1000, + MXL862XX_XPCS_SPEED_2500 =3D 2500, + MXL862XX_XPCS_SPEED_5000 =3D 5000, + MXL862XX_XPCS_SPEED_10000 =3D 10000, +}; + +/** + * enum mxl862xx_xpcs_duplex - PCS duplex mode + * @MXL862XX_XPCS_DUPLEX_HALF: half duplex + * @MXL862XX_XPCS_DUPLEX_FULL: full duplex + */ +enum mxl862xx_xpcs_duplex { + MXL862XX_XPCS_DUPLEX_HALF =3D 0, + MXL862XX_XPCS_DUPLEX_FULL =3D 1, +}; + +/** + * enum mxl862xx_xpcs_loopback_mode - XPCS loopback mode + * @MXL862XX_XPCS_LB_DISABLE: disable all loopback + * @MXL862XX_XPCS_LB_PCS_SERIAL: PCS TX-to-RX serial loopback + * @MXL862XX_XPCS_LB_PCS_PARALLEL: PCS RX-to-TX parallel loopback + * @MXL862XX_XPCS_LB_PMA_SERIAL: PMA TX-to-RX serial loopback + * @MXL862XX_XPCS_LB_PMA_PARALLEL: PMA RX-to-TX parallel loopback + */ +enum mxl862xx_xpcs_loopback_mode { + MXL862XX_XPCS_LB_DISABLE =3D 0, + MXL862XX_XPCS_LB_PCS_SERIAL =3D 1, + MXL862XX_XPCS_LB_PCS_PARALLEL =3D 2, + MXL862XX_XPCS_LB_PMA_SERIAL =3D 3, + MXL862XX_XPCS_LB_PMA_PARALLEL =3D 4, +}; + +/** + * enum mxl862xx_xpcs_reset_type - XPCS reset type + * @MXL862XX_XPCS_RESET_VR: vendor-specific reset (fast) + * @MXL862XX_XPCS_RESET_SOFT: PCS soft reset + * @MXL862XX_XPCS_RESET_HARD: full hardware reset + */ +enum mxl862xx_xpcs_reset_type { + MXL862XX_XPCS_RESET_VR =3D 0, + MXL862XX_XPCS_RESET_SOFT =3D 1, + MXL862XX_XPCS_RESET_HARD =3D 2, +}; + +/** + * struct mxl862xx_xpcs_pcs_cfg - PCS configuration parameters + * @port_id: XPCS port index (0-3) + * @interface: PCS interface mode. See &enum mxl862xx_xpcs_if_mode + * @neg_mode: PCS negotiation mode. See &enum mxl862xx_xpcs_neg_mode + * @permit_pause: Allow pause to MAC + * @usx_lane_mode: USXGMII lane mode. + * See &enum mxl862xx_xpcs_usx_lane_mode + * @role: PCS protocol role. See &enum mxl862xx_xpcs_role + * @usx_subport: Sub-port (0-3) within the XPCS. Used by the firmware + * to set MAC pause per sub-port; ignored for the + * XPCS-wide bringup, which is idempotent across slots. + * @advertising: AN code word the local end transmits. The active union + * member is selected by @interface (and, for the asymmetric + * protocols, by @role). Ignored when the local end does + * not transmit an AN word (role=3DMAC for SGMII/QSGMII/ + * USXGMII, 10GBASE-R, 5GBASE-R) or when @neg_mode is not + * INBAND_AN_ON. Pass all-zero to keep the firmware default + * advertisement. + * @result: Firmware result. >0 means the host must follow with an AN + * restart, 0 means no host follow-up is needed, <0 is an errno. + */ +struct mxl862xx_xpcs_pcs_cfg { +#ifdef __LITTLE_ENDIAN_BITFIELD + u8 port_id:2; + u8 interface:6; /* enum mxl862xx_xpcs_if_mode */ + u8 neg_mode:2; /* enum mxl862xx_xpcs_neg_mode */ + u8 permit_pause:1; + u8 usx_lane_mode:2; /* enum mxl862xx_xpcs_usx_lane_mode */ + u8 role:1; /* enum mxl862xx_xpcs_role */ + u8 usx_subport:2; +#elif defined(__BIG_ENDIAN_BITFIELD) + u8 interface:6; /* enum mxl862xx_xpcs_if_mode */ + u8 port_id:2; + u8 usx_subport:2; + u8 role:1; /* enum mxl862xx_xpcs_role */ + u8 usx_lane_mode:2; /* enum mxl862xx_xpcs_usx_lane_mode */ + u8 permit_pause:1; + u8 neg_mode:2; /* enum mxl862xx_xpcs_neg_mode */ +#else +#error "Unknown bitfield endianness" +#endif + union mxl862xx_xpcs_an_word advertising; + __le16 result; +} __packed; + +/** + * struct mxl862xx_xpcs_pcs_state - PCS link state + * @port_id: XPCS port index (0-3) (input) + * @interface: PCS interface mode (input). + * See &enum mxl862xx_xpcs_if_mode + * @usx_lane_mode: USX lane mode (input) + * @usx_subport: USX sub-port 0-3 (input) + * @link: Link up (1) / down (0) (output) + * @an_complete: Auto-negotiation complete (output) + * @duplex: Duplex mode (output). See &enum mxl862xx_xpcs_duplex + * @pcs_fault: PCS fault (output) + * @pause: Pause negotiation result, bit 0 symmetric, bit 1 asymmetric + * (output) + * @lp_eee_cap: Link partner supports EEE (output) + * @lp_eee_cs_cap: Link partner supports EEE clock-stop (output) + * @__rsv: reserved + * @__pad: padding + * @speed: Resolved speed (output). See &enum mxl862xx_xpcs_speed + * @lpa: Link partner ability word (output). Same union as + * &union mxl862xx_xpcs_an_word; the host picks the member based on + * @interface. + */ +struct mxl862xx_xpcs_pcs_state { +#ifdef __LITTLE_ENDIAN_BITFIELD + u8 port_id:2; + u8 interface:6; /* enum mxl862xx_xpcs_if_mode */ + u8 usx_lane_mode:2; /* enum mxl862xx_xpcs_usx_lane_mode */ + u8 usx_subport:2; + u8 link:1; + u8 an_complete:1; + u8 duplex:1; /* enum mxl862xx_xpcs_duplex */ + u8 pcs_fault:1; + u8 pause:2; + u8 lp_eee_cap:1; + u8 lp_eee_cs_cap:1; + u8 __rsv:4; +#elif defined(__BIG_ENDIAN_BITFIELD) + u8 interface:6; /* enum mxl862xx_xpcs_if_mode */ + u8 port_id:2; + u8 pcs_fault:1; + u8 duplex:1; /* enum mxl862xx_xpcs_duplex */ + u8 an_complete:1; + u8 link:1; + u8 usx_subport:2; + u8 usx_lane_mode:2; /* enum mxl862xx_xpcs_usx_lane_mode */ + u8 __rsv:4; + u8 lp_eee_cs_cap:1; + u8 lp_eee_cap:1; + u8 pause:2; +#else +#error "Unknown bitfield endianness" +#endif + u8 __pad; + __le16 speed; /* enum mxl862xx_xpcs_speed */ + union mxl862xx_xpcs_an_word lpa; +} __packed; + +/** + * struct mxl862xx_xpcs_pcs_disable - PCS disable parameters + * @port_id: XPCS port index + * @__pad: padding + * @result: Firmware result. 0 on success, <0 on error. + * + * Asserts IDDQ + PHY + XPCS resets to power down the SERDES when the + * port is admin-down or no module is plugged in. The next PCS config + * implicitly powers it back up and reprograms the desired interface. + */ +struct mxl862xx_xpcs_pcs_disable { + u8 port_id; + u8 __pad; + __le16 result; +} __packed; + +/** + * struct mxl862xx_xpcs_an_restart - AN restart parameters + * @port_id: XPCS port index (0-3) + * @interface: PCS interface mode. See &enum mxl862xx_xpcs_if_mode + * @usx_lane_mode: USX lane mode + * @usx_subport: Sub-port (0-3) within the XPCS. Selects the lane + * whose AN is restarted for QSGMII and QUSXGMII; + * ignored by single-lane modes. + * @__rsv: reserved + * @result: Firmware result. 0 on success, <0 on error. + * + * Restarts auto-negotiation on a single sub-port of the XPCS. The + * SERDES must already be configured. + */ +struct mxl862xx_xpcs_an_restart { +#ifdef __LITTLE_ENDIAN_BITFIELD + u8 port_id:2; + u8 interface:6; /* enum mxl862xx_xpcs_if_mode */ + u8 usx_lane_mode:2; /* enum mxl862xx_xpcs_usx_lane_mode */ + u8 usx_subport:2; + u8 __rsv:4; +#elif defined(__BIG_ENDIAN_BITFIELD) + u8 interface:6; /* enum mxl862xx_xpcs_if_mode */ + u8 port_id:2; + u8 __rsv:4; + u8 usx_subport:2; + u8 usx_lane_mode:2; /* enum mxl862xx_xpcs_usx_lane_mode */ +#else +#error "Unknown bitfield endianness" +#endif + __le16 result; +} __packed; + +/** + * struct mxl862xx_xpcs_pcs_link_up - PCS link-up parameters + * @port_id: XPCS port index (0-3) + * @interface: PCS interface mode. See &enum mxl862xx_xpcs_if_mode + * @duplex: Duplex mode. See &enum mxl862xx_xpcs_duplex + * @usx_lane_mode: USX lane mode (USXGMII only; ignored otherwise). + * See &enum mxl862xx_xpcs_usx_lane_mode + * @usx_subport: USX sub-port 0-3 (QUSXGMII only; ignored otherwise) + * @__rsv0: reserved + * @speed: Resolved speed. See &enum mxl862xx_xpcs_speed + * @result: Firmware result. 0 on success, <0 is errno. + * + * Called once per link-up event after the host has resolved the + * line-side speed/duplex (from the PHY's read_status, from a preceding + * PCS get-state, or from a fixed-link description). + */ +struct mxl862xx_xpcs_pcs_link_up { +#ifdef __LITTLE_ENDIAN_BITFIELD + u8 port_id:2; + u8 interface:6; /* enum mxl862xx_xpcs_if_mode */ + u8 duplex:1; /* enum mxl862xx_xpcs_duplex */ + u8 usx_lane_mode:2; /* enum mxl862xx_xpcs_usx_lane_mode */ + u8 usx_subport:2; + u8 __rsv0:3; +#elif defined(__BIG_ENDIAN_BITFIELD) + u8 interface:6; /* enum mxl862xx_xpcs_if_mode */ + u8 port_id:2; + u8 __rsv0:3; + u8 usx_subport:2; + u8 usx_lane_mode:2; /* enum mxl862xx_xpcs_usx_lane_mode */ + u8 duplex:1; /* enum mxl862xx_xpcs_duplex */ +#else +#error "Unknown bitfield endianness" +#endif + __le16 speed; /* enum mxl862xx_xpcs_speed */ + __le16 result; +} __packed; + +/** + * struct mxl862xx_xpcs_loopback_cfg - loopback control + * @port_id: XPCS port index + * @mode: loopback mode. See &enum mxl862xx_xpcs_loopback_mode + * @result: firmware result + */ +struct mxl862xx_xpcs_loopback_cfg { + u8 port_id; + u8 mode; /* enum mxl862xx_xpcs_loopback_mode */ + __le16 result; +} __packed; + +/** + * struct mxl862xx_xpcs_reset_cfg - XPCS reset + * @port_id: XPCS port index + * @reset_type: reset type. See &enum mxl862xx_xpcs_reset_type + * @result: firmware result + */ +struct mxl862xx_xpcs_reset_cfg { + u8 port_id; + u8 reset_type; /* enum mxl862xx_xpcs_reset_type */ + __le16 result; +} __packed; + #endif /* __MXL862XX_API_H */ diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h b/drivers/net/dsa/mxl8= 62xx/mxl862xx-cmd.h index f1ea40aa7ea0..c87a955c13c4 100644 --- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h +++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h @@ -24,6 +24,7 @@ #define MXL862XX_SS_MAGIC 0x1600 #define GPY_GPY2XX_MAGIC 0x1800 #define SYS_MISC_MAGIC 0x1900 +#define MXL862XX_XPCS_MAGIC 0x1a00 =20 #define MXL862XX_COMMON_CFGGET (MXL862XX_COMMON_MAGIC + 0x9) #define MXL862XX_COMMON_CFGSET (MXL862XX_COMMON_MAGIC + 0xa) @@ -71,6 +72,14 @@ =20 #define SYS_MISC_FW_VERSION (SYS_MISC_MAGIC + 0x2) =20 +#define MXL862XX_XPCS_PCS_CONFIG (MXL862XX_XPCS_MAGIC + 0x1) +#define MXL862XX_XPCS_PCS_GET_STATE (MXL862XX_XPCS_MAGIC + 0x2) +#define MXL862XX_XPCS_PCS_DISABLE (MXL862XX_XPCS_MAGIC + 0x4) +#define MXL862XX_XPCS_AN_RESTART (MXL862XX_XPCS_MAGIC + 0x5) +#define MXL862XX_XPCS_PCS_LINK_UP (MXL862XX_XPCS_MAGIC + 0x7) +#define MXL862XX_XPCS_LOOPBACK (MXL862XX_XPCS_MAGIC + 0x8) +#define MXL862XX_XPCS_RESET (MXL862XX_XPCS_MAGIC + 0x9) + #define MMD_API_MAXIMUM_ID 0x7fff =20 #endif /* __MXL862XX_CMD_H */ diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c b/drivers/net/dsa/= mxl862xx/mxl862xx-phylink.c index f17c429d1f1d..55be53d77cec 100644 --- a/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c +++ b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c @@ -11,16 +11,343 @@ #include =20 #include "mxl862xx.h" +#include "mxl862xx-api.h" +#include "mxl862xx-cmd.h" +#include "mxl862xx-host.h" #include "mxl862xx-phylink.h" =20 void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port, struct phylink_config *config) { + struct mxl862xx_priv *priv =3D ds->priv; + config->mac_capabilities =3D MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD; =20 - __set_bit(PHY_INTERFACE_MODE_INTERNAL, - config->supported_interfaces); + switch (port) { + case 1 ... 8: + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + config->supported_interfaces); + break; + case 9: + case 13: + if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 84)) + break; + __set_bit(PHY_INTERFACE_MODE_SGMII, config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_10GBASER, config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_10GKR, config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_USXGMII, config->supported_interfaces); + fallthrough; + case 10 ... 12: + case 14 ... 16: + if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 84)) + break; + __set_bit(PHY_INTERFACE_MODE_QSGMII, config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_10G_QXGMII, config->supported_interfaces); + + break; + default: + break; + } + + if (port =3D=3D 9 || port =3D=3D 13) + config->mac_capabilities |=3D MAC_10000FD | MAC_5000FD; +} + +static struct mxl862xx_pcs *pcs_to_mxl862xx_pcs(struct phylink_pcs *pcs) +{ + return container_of(pcs, struct mxl862xx_pcs, pcs); +} + +static int mxl862xx_xpcs_if_mode(phy_interface_t interface) +{ + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + return MXL862XX_XPCS_IF_SGMII; + case PHY_INTERFACE_MODE_QSGMII: + return MXL862XX_XPCS_IF_QSGMII; + case PHY_INTERFACE_MODE_1000BASEX: + return MXL862XX_XPCS_IF_1000BASEX; + case PHY_INTERFACE_MODE_2500BASEX: + return MXL862XX_XPCS_IF_2500BASEX; + case PHY_INTERFACE_MODE_USXGMII: + case PHY_INTERFACE_MODE_10G_QXGMII: + return MXL862XX_XPCS_IF_USXGMII; + case PHY_INTERFACE_MODE_10GBASER: + return MXL862XX_XPCS_IF_10GBASER; + case PHY_INTERFACE_MODE_10GKR: + return MXL862XX_XPCS_IF_10GKR; + default: + return -EINVAL; + } +} + +static int mxl862xx_xpcs_neg_mode(unsigned int neg_mode) +{ + if (!(neg_mode & PHYLINK_PCS_NEG_INBAND)) + return MXL862XX_XPCS_NEG_NONE; + if (neg_mode & PHYLINK_PCS_NEG_ENABLED) + return MXL862XX_XPCS_NEG_INBAND_AN_ON; + return MXL862XX_XPCS_NEG_INBAND_AN_OFF; +} + +static void mxl862xx_pcs_disable(struct phylink_pcs *pcs) +{ + struct mxl862xx_pcs *mpcs =3D pcs_to_mxl862xx_pcs(pcs); + struct mxl862xx_priv *priv =3D mpcs->priv; + struct mxl862xx_xpcs_pcs_disable dis =3D {}; + + /* The SerDes is shared across QSGMII/QUSXGMII sub-ports; only + * power it down once the last active sub-port goes away. + */ + priv->serdes_active[mpcs->serdes_id] &=3D ~BIT(mpcs->slot); + if (priv->serdes_active[mpcs->serdes_id]) + return; + + dis.port_id =3D mpcs->serdes_id; + + MXL862XX_API_WRITE(priv, MXL862XX_XPCS_PCS_DISABLE, dis); +} + +static int mxl862xx_pcs_config(struct phylink_pcs *pcs, unsigned int neg_m= ode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + struct mxl862xx_pcs *mpcs =3D pcs_to_mxl862xx_pcs(pcs); + struct mxl862xx_priv *priv =3D mpcs->priv; + struct mxl862xx_xpcs_pcs_cfg cfg =3D {}; + int if_mode, ret; + u16 adv; + + mpcs->interface =3D interface; + priv->serdes_active[mpcs->serdes_id] |=3D BIT(mpcs->slot); + + if_mode =3D mxl862xx_xpcs_if_mode(interface); + if (if_mode < 0) { + dev_err(priv->ds->dev, "unsupported interface: %s\n", + phy_modes(interface)); + return if_mode; + } + + /* The XPCS bringup is per-instance and idempotent in the + * firmware: every QSGMII/QUSXGMII sub-port may call pcs_config + * and the firmware will skip the bringup if the requested mode + * matches the cached one, then update MAC pause for the + * sub-port indicated by @usx_subport. + */ + cfg.port_id =3D mpcs->serdes_id; + cfg.usx_subport =3D mpcs->slot; + cfg.usx_lane_mode =3D (interface =3D=3D PHY_INTERFACE_MODE_10G_QXGMII) ? + MXL862XX_XPCS_USX_QUAD : MXL862XX_XPCS_USX_SINGLE; + cfg.interface =3D if_mode; + cfg.neg_mode =3D mxl862xx_xpcs_neg_mode(neg_mode); + cfg.role =3D MXL862XX_XPCS_ROLE_MAC; + cfg.permit_pause =3D permit_pause_to_mac ? 1 : 0; + + if (neg_mode & PHYLINK_PCS_NEG_INBAND) { + switch (interface) { + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + adv =3D linkmode_adv_to_mii_adv_x(advertising, + ETHTOOL_LINK_MODE_1000baseX_Full_BIT); + cfg.advertising.cl37 =3D cpu_to_le16(adv); + break; + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: + cfg.advertising.cl37 =3D cpu_to_le16(ADVERTISE_SGMII); + break; + default: + break; + } + } + + ret =3D MXL862XX_API_READ(priv, MXL862XX_XPCS_PCS_CONFIG, cfg); + if (ret) + return ret; + + return (s16)le16_to_cpu(cfg.result) > 0 ? 1 : 0; +} + +static void mxl862xx_pcs_get_state(struct phylink_pcs *pcs, + unsigned int neg_mode, + struct phylink_link_state *state) +{ + struct mxl862xx_pcs *mpcs =3D pcs_to_mxl862xx_pcs(pcs); + struct mxl862xx_priv *priv =3D mpcs->priv; + struct mxl862xx_xpcs_pcs_state st =3D {}; + int if_mode, ret; + u16 bmsr; + + if_mode =3D mxl862xx_xpcs_if_mode(state->interface); + if (if_mode < 0) + return; + + st.port_id =3D mpcs->serdes_id; + st.interface =3D if_mode; + st.usx_subport =3D mpcs->slot; + + ret =3D MXL862XX_API_READ(priv, MXL862XX_XPCS_PCS_GET_STATE, st); + if (ret) + return; + + state->link =3D st.link && !st.pcs_fault; + state->an_complete =3D st.an_complete; + + switch (state->interface) { + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: + bmsr =3D (state->link ? BMSR_LSTATUS : 0) | + (state->an_complete ? BMSR_ANEGCOMPLETE : 0); + phylink_mii_c22_pcs_decode_state(state, neg_mode, bmsr, + le16_to_cpu(st.lpa.cl37)); + break; + + case PHY_INTERFACE_MODE_USXGMII: + case PHY_INTERFACE_MODE_10G_QXGMII: + if (state->link) + phylink_decode_usxgmii_word(state, + le16_to_cpu(st.lpa.usx)); + break; + + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_10GKR: + if (state->link) { + state->speed =3D SPEED_10000; + state->duplex =3D DUPLEX_FULL; + } + break; + + default: + state->link =3D false; + break; + } +} + +static void mxl862xx_pcs_an_restart(struct phylink_pcs *pcs) +{ + struct mxl862xx_pcs *mpcs =3D pcs_to_mxl862xx_pcs(pcs); + struct mxl862xx_priv *priv =3D mpcs->priv; + struct mxl862xx_xpcs_an_restart an =3D {}; + int if_mode; + + if_mode =3D mxl862xx_xpcs_if_mode(mpcs->interface); + if (if_mode < 0) + return; + + an.port_id =3D mpcs->serdes_id; + an.interface =3D if_mode; + an.usx_subport =3D mpcs->slot; + an.usx_lane_mode =3D (mpcs->interface =3D=3D PHY_INTERFACE_MODE_10G_QXGMI= I) ? + MXL862XX_XPCS_USX_QUAD : MXL862XX_XPCS_USX_SINGLE; + + MXL862XX_API_WRITE(priv, MXL862XX_XPCS_AN_RESTART, an); +} + +static void mxl862xx_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg= _mode, + phy_interface_t interface, int speed, + int duplex) +{ + struct mxl862xx_pcs *mpcs =3D pcs_to_mxl862xx_pcs(pcs); + struct mxl862xx_priv *priv =3D mpcs->priv; + struct mxl862xx_xpcs_pcs_link_up lu =3D {}; + int if_mode; + + /* With inband-AN enabled (role=3DMAC), the XPCS auto-resolves + * speed/duplex from the partner's AN word and the firmware + * short-circuits link_up. Skip the firmware round-trip, same + * as pcs-mtk-lynxi. + */ + if (neg_mode =3D=3D PHYLINK_PCS_NEG_INBAND_ENABLED) + return; + + if_mode =3D mxl862xx_xpcs_if_mode(interface); + if (if_mode < 0) + return; + + lu.port_id =3D mpcs->serdes_id; + lu.interface =3D if_mode; + lu.duplex =3D (duplex =3D=3D DUPLEX_FULL) ? MXL862XX_XPCS_DUPLEX_FULL : + MXL862XX_XPCS_DUPLEX_HALF; + lu.speed =3D cpu_to_le16(speed); + + MXL862XX_API_WRITE(priv, MXL862XX_XPCS_PCS_LINK_UP, lu); +} + +static unsigned int mxl862xx_pcs_inband_caps(struct phylink_pcs *pcs, + phy_interface_t interface) +{ + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE; + case PHY_INTERFACE_MODE_USXGMII: + case PHY_INTERFACE_MODE_10G_QXGMII: + case PHY_INTERFACE_MODE_10GKR: + return LINK_INBAND_ENABLE; + case PHY_INTERFACE_MODE_10GBASER: + return LINK_INBAND_DISABLE; + default: + return 0; + } +} + +static const struct phylink_pcs_ops mxl862xx_pcs_ops =3D { + .pcs_disable =3D mxl862xx_pcs_disable, + .pcs_config =3D mxl862xx_pcs_config, + .pcs_get_state =3D mxl862xx_pcs_get_state, + .pcs_an_restart =3D mxl862xx_pcs_an_restart, + .pcs_link_up =3D mxl862xx_pcs_link_up, + .pcs_inband_caps =3D mxl862xx_pcs_inband_caps, +}; + +void mxl862xx_setup_pcs(struct mxl862xx_priv *priv, struct mxl862xx_pcs *p= cs, + int port) +{ + pcs->priv =3D priv; + pcs->serdes_id =3D MXL862XX_SERDES_PORT_ID(port); + pcs->slot =3D MXL862XX_SERDES_SLOT(port); + pcs->interface =3D PHY_INTERFACE_MODE_NA; + + pcs->pcs.ops =3D &mxl862xx_pcs_ops; + pcs->pcs.poll =3D true; + + __set_bit(PHY_INTERFACE_MODE_QSGMII, pcs->pcs.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_10G_QXGMII, pcs->pcs.supported_interfaces); + if (pcs->slot !=3D 0) + return; + + __set_bit(PHY_INTERFACE_MODE_SGMII, pcs->pcs.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, pcs->pcs.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, pcs->pcs.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_10GBASER, pcs->pcs.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_10GKR, pcs->pcs.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_USXGMII, pcs->pcs.supported_interfaces); +} + +static struct phylink_pcs * +mxl862xx_phylink_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) +{ + struct dsa_port *dp =3D dsa_phylink_to_port(config); + struct mxl862xx_priv *priv =3D dp->ds->priv; + int port =3D dp->index; + + if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 84)) + return NULL; + + switch (port) { + case 9 ... 16: + return &priv->serdes_ports[port - 9].pcs; + default: + return NULL; + } } =20 static void mxl862xx_phylink_mac_config(struct phylink_config *config, @@ -48,4 +375,5 @@ const struct phylink_mac_ops mxl862xx_phylink_mac_ops = =3D { .mac_config =3D mxl862xx_phylink_mac_config, .mac_link_down =3D mxl862xx_phylink_mac_link_down, .mac_link_up =3D mxl862xx_phylink_mac_link_up, + .mac_select_pcs =3D mxl862xx_phylink_mac_select_pcs, }; diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-phylink.h b/drivers/net/dsa/= mxl862xx/mxl862xx-phylink.h index c3d5215bdf60..03bb9caad9aa 100644 --- a/drivers/net/dsa/mxl862xx/mxl862xx-phylink.h +++ b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.h @@ -7,8 +7,15 @@ =20 #include "mxl862xx.h" =20 +#define MXL862XX_SERDES_SLOT(port) \ + (((port) - MXL862XX_FIRST_SERDES_PORT) % MXL862XX_SERDES_SLOTS) +#define MXL862XX_SERDES_PORT_ID(port) \ + (((port) - MXL862XX_FIRST_SERDES_PORT) / MXL862XX_SERDES_SLOTS) + extern const struct phylink_mac_ops mxl862xx_phylink_mac_ops; void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port, struct phylink_config *config); +void mxl862xx_setup_pcs(struct mxl862xx_priv *priv, struct mxl862xx_pcs *p= cs, + int port); =20 #endif /* __MXL862XX_PHYLINK_H */ diff --git a/drivers/net/dsa/mxl862xx/mxl862xx.c b/drivers/net/dsa/mxl862xx= /mxl862xx.c index 0b1a23364eb5..0af41efccbc6 100644 --- a/drivers/net/dsa/mxl862xx/mxl862xx.c +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c @@ -622,7 +622,7 @@ static int mxl862xx_setup(struct dsa_switch *ds) int n_user_ports =3D 0, max_vlans; int ingress_finals, vid_rules; struct dsa_port *dp; - int ret; + int ret, i; =20 ret =3D mxl862xx_reset(priv); if (ret) @@ -632,6 +632,10 @@ static int mxl862xx_setup(struct dsa_switch *ds) if (ret) return ret; =20 + for (i =3D 0; i < ARRAY_SIZE(priv->serdes_ports); i++) + mxl862xx_setup_pcs(priv, &priv->serdes_ports[i], + i + MXL862XX_FIRST_SERDES_PORT); + /* Calculate Extended VLAN block sizes. * With VLAN Filter handling VID membership checks: * Ingress: only final catchall rules (PVID insertion, 802.1Q diff --git a/drivers/net/dsa/mxl862xx/mxl862xx.h b/drivers/net/dsa/mxl862xx= /mxl862xx.h index 79fd32c4db4e..d5879094ffd3 100644 --- a/drivers/net/dsa/mxl862xx/mxl862xx.h +++ b/drivers/net/dsa/mxl862xx/mxl862xx.h @@ -11,6 +11,9 @@ struct mxl862xx_priv; =20 #define MXL862XX_MAX_PORTS 17 +#define MXL862XX_FIRST_SERDES_PORT 9 +#define MXL862XX_SERDES_SLOTS 4 + #define MXL862XX_DEFAULT_BRIDGE 0 #define MXL862XX_MAX_BRIDGES 48 #define MXL862XX_MAX_BRIDGE_PORTS 128 @@ -242,6 +245,26 @@ struct mxl862xx_port { spinlock_t stats_lock; /* protects stats accumulators */ }; =20 +/** + * struct mxl862xx_pcs - link SerDes interfaces to bridge ports + * @pcs: &struct phylink_pcs instance + * @priv: pointer to &struct mxl862xx_priv + * @serdes_id: SerDes instance index (0 or 1) + * @slot: slot within the SerDes (0-3 for QSGMII/QUSXGMII, 0 otherwis= e) + * @interface: cached PHY interface, last value passed to pcs_config(). + * %PHY_INTERFACE_MODE_NA before the first successful + * pcs_config(). Used by pcs_an_restart() to populate the + * firmware command and by pcs_disable() to skip the + * firmware power-down for shared (QSGMII/QUSXGMII) modes. + */ +struct mxl862xx_pcs { + struct phylink_pcs pcs; + struct mxl862xx_priv *priv; + int serdes_id; + int slot; + phy_interface_t interface; +}; + /** * union mxl862xx_fw_version - firmware version for comparison and display * @major: firmware major version @@ -293,6 +316,12 @@ union mxl862xx_fw_version { * flooding) * @fw_version: cached firmware version, populated at probe and * compared with MXL862XX_FW_VER_MIN() + * @serdes_ports: SerDes interfaces incl. sub-interfaces in case of + * 10G_QXGMII or QSGMII + * @serdes_active: per-XPCS bitmap, BIT(slot) set while that + * sub-port's PCS is enabled by phylink. pcs_disable + * only powers the XPCS down once the last bit + * clears. Updates are serialised by rtnl. * @ports: per-port state, indexed by switch port number * @bridges: maps DSA bridge number to firmware bridge ID; * zero means no firmware bridge allocated for that @@ -311,6 +340,8 @@ struct mxl862xx_priv { unsigned long flags; u16 drop_meter; union mxl862xx_fw_version fw_version; + struct mxl862xx_pcs serdes_ports[8]; + u8 serdes_active[2]; struct mxl862xx_port ports[MXL862XX_MAX_PORTS]; u16 bridges[MXL862XX_MAX_BRIDGES + 1]; u16 evlan_ingress_size; --=20 2.54.0