From mboxrd@z Thu Jan 1 00:00:00 1970 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 bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 40653CD343B for ; Tue, 5 May 2026 18:27:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Content-Transfer-Encoding: MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:To:From:Reply-To: Cc:Content-Type:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=j1xB6kfSssin2iCS0jgzSw2Kk68sCL72sL40Zd4Pfxg=; b=Z6Ni6hoo48edI6OXdxRD1XBo0S iLUSccZE0eDz16xDx7tMXWm1kZFYb+natefEJC3ceTxVvu81Gis8KLD2bDT/7DfEYANg4JAJ3kh8l 3yiS7KmAjaDPJ2kWEIHadJ1F+YUVaTXv5nbMfBiqNKOMt2PxV14/c4hLgJHv/cTMziEtz4I/LjcKH WtnXjivmUeYkf3qsOOpg3XS9V1RPDYYgBoGF51VzXVP+X2Q+65nc8lWgWQv8zuexBeWmvK2iVyNGS aMcbwh547H7zUZLfvFaT0JvDOPKBlkXN2gm7ceV5TAnBH1h4CE8iiDLONsK9upTJ2H28PJCw4NCDR EjT2EvJg==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.98.2 #2 (Red Hat Linux)) id 1wKKUY-0000000HAJz-1AsU; Tue, 05 May 2026 18:27:34 +0000 Received: from mail-wm1-x332.google.com ([2a00:1450:4864:20::332]) by bombadil.infradead.org with esmtps (Exim 4.98.2 #2 (Red Hat Linux)) id 1wKKUU-0000000HAF9-37JH for linux-arm-kernel@lists.infradead.org; Tue, 05 May 2026 18:27:32 +0000 Received: by mail-wm1-x332.google.com with SMTP id 5b1f17b1804b1-488ad135063so53093695e9.0 for ; Tue, 05 May 2026 11:27:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778005649; x=1778610449; darn=lists.infradead.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=j1xB6kfSssin2iCS0jgzSw2Kk68sCL72sL40Zd4Pfxg=; b=Q/i1P9+dMt5uhCoJPRU78B8UyntqMEW0Yu3EccvSPoTr5TgS4HzqrkQ8/XxDU1xM+5 ckgrYRHdC6a9CpSCXNJEr6vE0u72jYEOvR9z/fvDLwRX2SRT5bQ30hNUbceCtWofTZXK LZPfztg4W9pMhwAo3vJqs6+cWCAlSYPDIRoeHh0Cvrc9ZbMLfJCm4T/D9J39hFQmuoZ9 6Zuu8Cv+Hko78LrSmh0YGQw4qbQPzYRYXyulpB6/erAKe2OkBrb2s4U0UKzSrq1WRzXV QsMoQayhNc/UuXolin4+3zGujbSaxBpM6bF/u9O/28ynIvQd2HmY7n2ROzS/3CVftlz7 Ecrg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778005649; x=1778610449; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=j1xB6kfSssin2iCS0jgzSw2Kk68sCL72sL40Zd4Pfxg=; b=bRk959PPgj89TYqyGGt/brr7/cXwgcWQ3SjtulkrV/Gxkb3J0hAAU2hhbJ3dh2ecub E/uSN7vrMuYmn9mG3oL84kAH+rFnDiWvjbjQ3azA73ZwAUHbUm6aFYCQX+h5YkXWGYh7 t+YsyjwELvAYx1vNw8DqX4Hl7R3jNJTmcxvzFoGZsfqBbijw6snZGRM/jBjrf6Ic3qm4 /z+nxqbhctEkxKMja9hpMf0GBxxER9JuQURZs4nAfTiW6XHT5n2hUeiD8F7xkE8pUGg8 15UqUrH6k7Jg3zbpwN4zOf0h8qH+ho9pJy3JGF6+poyt0KAtMbIAsejjnwcu3CWd0yZV Zbew== X-Forwarded-Encrypted: i=1; AFNElJ+6ZjSgaHgGFEsvHEH9sqw2CfnW49s+3fPt3fLVByuvcSPRIWTeeDf1NgtOwjiKvy0JwjsHdFySZrq8RzzEvj2u@lists.infradead.org X-Gm-Message-State: AOJu0YyvoM2MBl5EG3pCLGG+p8eurdmFqsOw6uz4g52LiGLCdNgdv7fl M1VlvlM0TrcpLWrozVXNJ0nf5GziSxKLeeulZKEHCOfPIrRbIVA7hZUR X-Gm-Gg: AeBDievXJX/GChN9L2c4mTj9rVVNq0QWWMCZV9x+mFTdXaMqubpIlHARES5nCgwyR8V H7tvtvzN7dGcjjGsVe+A5glwptmjAEgWWbgbiNFoFML4RQEshZzonxUmAajKn1IA54w/u617DWD vp1qUkcEldWEW56JFM/QgWwuJDl/cqtca2q6DhaK/gSKg8/HeXALyscJbr93SkJHyLp+TKVxv9g 1evZMgb8OdPHTqtyG+jHf5nddxm2x1437wzlckdRNBXY/FA7oDgXrCCjuF6Jukgl/NcA3QSdxYJ thrqQ48ssiyEvMT7SmSJ7Krzmx506mj15amYn5ygD3iEVDJ8OvxChaQJnh6ljy8UGC0ecnKL0Hc X1A7VCMuD4VZFApIKxK9ZtZWt3XgGtJtyUkDc9vKX5tdI5o50CiDkOj7BERSvymcOZ9gWW8pQSD jtVT69o7KlYEPedIAtSsk8lTi1O8i2rIHTbAE3rkYWov+J4FqHDtvF/H688K6rX92SYez7o5I2i xMuBjw= X-Received: by 2002:a05:600c:8011:b0:485:35d3:ce59 with SMTP id 5b1f17b1804b1-48e51e1a545mr6615225e9.10.1778005648639; Tue, 05 May 2026 11:27:28 -0700 (PDT) Received: from Ansuel-XPS24 (host-82-59-227-65.retail.telecomitalia.it. [82.59.227.65]) by smtp.googlemail.com with ESMTPSA id 5b1f17b1804b1-48a820c865esm408208245e9.5.2026.05.05.11.27.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 05 May 2026 11:27:28 -0700 (PDT) From: Christian Marangi To: Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Lorenzo Bianconi , Heiner Kallweit , Russell King , Philipp Zabel , Nathan Chancellor , Nick Desaulniers , Bill Wendling , Justin Stitt , Christian Marangi , Daniel Golle , netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-mediatek@lists.infradead.org, llvm@lists.linux.dev Subject: [net-next RFC PATCH v5 02/10] net: phylink: introduce internal phylink PCS handling Date: Tue, 5 May 2026 20:27:03 +0200 Message-ID: <20260505182713.27644-3-ansuelsmth@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260505182713.27644-1-ansuelsmth@gmail.com> References: <20260505182713.27644-1-ansuelsmth@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20260505_112730_923604_769A8BC6 X-CRM114-Status: GOOD ( 38.94 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org Introduce internal handling of PCS for phylink. This is an alternative to .mac_select_pcs that moves the selection logic of the PCS entirely to phylink with the usage of the supported_interface value in the PCS struct. MAC should now provide an array of available PCS in phylink_config in .available_pcs and fill the .num_available_pcs with the number of elements in the array. MAC should also define a new bitmap, pcs_interfaces, in phylink_config to define for what interface mode a dedicated PCS is required. On phylink_create() this array is parsed and a linked list of PCS is created based on the PCS passed in phylink_config. Also the supported_interface value in phylink struct is updated with the new supported_interface from the provided PCS. On phylink_start() every PCS in phylink PCS list gets attached to the phylink instance. This is done by setting the phylink value in phylink_pcs struct to the phylink instance. On phylink_stop(), every PCS in phylink PCS list is detached from the phylink instance. This is done by setting the phylink value in phylink_pcs struct to NULL. phylink_validate_mac_and_pcs(), phylink_major_config() and phylink_inband_caps() are updated to support this new implementation with the PCS list stored in phylink. They will make use of phylink_validate_pcs_interface() that will loop for every PCS in the phylink PCS available list and find one that supports the passed interface. phylink_validate_pcs_interface() applies the same logic of .mac_select_pcs where if a supported_interface value is not set for the PCS struct, then it's assumed every interface is supported. A MAC is required to implement either a .mac_select_pcs or make use of the PCS list implementation. Implementing both will result in a fail on MAC/PCS validation. phylink value in phylink_pcs struct with this implementation is used to track from PCS side when it's attached to a phylink instance. PCS driver will make use of this information to correctly detach from a phylink instance if needed. The .mac_select_pcs implementation is not changed but it's expected that every MAC driver migrates to the new implementation to later deprecate and remove .mac_select_pcs. Signed-off-by: Christian Marangi --- drivers/net/phy/phylink.c | 149 +++++++++++++++++++++++++++++++++----- include/linux/phylink.h | 11 +++ 2 files changed, 141 insertions(+), 19 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 4d59c0dd78db..1a261060d78e 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -60,6 +60,9 @@ struct phylink { /* The link configuration settings */ struct phylink_link_state link_config; + /* List of available PCS */ + struct list_head pcs_list; + /* What interface are supported by the current link. * Can change on removal or addition of new PCS. */ @@ -154,6 +157,8 @@ static const phy_interface_t phylink_sfp_interface_preference[] = { static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces); +static void phylink_run_resolve(struct phylink *pl); + /** * phylink_set_port_modes() - set the port type modes in the ethtool mask * @mask: ethtool link mode mask @@ -518,22 +523,59 @@ static void phylink_validate_mask_caps(unsigned long *supported, linkmode_and(state->advertising, state->advertising, mask); } +static int phylink_validate_pcs_interface(struct phylink_pcs *pcs, + phy_interface_t interface) +{ + /* If PCS define an empty supported_interfaces value, assume + * all interface are supported. + */ + if (phy_interface_empty(pcs->supported_interfaces)) + return 0; + + /* Ensure that this PCS supports the interface mode */ + if (!test_bit(interface, pcs->supported_interfaces)) + return -EINVAL; + + return 0; +} + static int phylink_validate_mac_and_pcs(struct phylink *pl, unsigned long *supported, struct phylink_link_state *state) { - struct phylink_pcs *pcs = NULL; unsigned long capabilities; + struct phylink_pcs *pcs; + bool pcs_found = false; int ret; /* Get the PCS for this interface mode */ if (pl->mac_ops->mac_select_pcs) { + /* Make sure either PCS internal validation or .mac_select_pcs + * is used. Return error if both are defined. + */ + if (!list_empty(&pl->pcs_list)) { + phylink_err(pl, "either phylink_pcs_add() or .mac_select_pcs must be used\n"); + return -EINVAL; + } + pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface); if (IS_ERR(pcs)) return PTR_ERR(pcs); + + pcs_found = !!pcs; + } else { + /* Check every assigned PCS and search for one that supports + * the interface. + */ + list_for_each_entry(pcs, &pl->pcs_list, list) { + if (!phylink_validate_pcs_interface(pcs, state->interface)) { + pcs_found = true; + break; + } + } } - if (pcs) { + if (pcs_found) { /* The PCS, if present, must be setup before phylink_create() * has been called. If the ops is not initialised, print an * error and backtrace rather than oopsing the kernel. @@ -545,13 +587,10 @@ static int phylink_validate_mac_and_pcs(struct phylink *pl, return -EINVAL; } - /* Ensure that this PCS supports the interface which the MAC - * returned it for. It is an error for the MAC to return a PCS - * that does not support the interface mode. - */ - if (!phy_interface_empty(pcs->supported_interfaces) && - !test_bit(state->interface, pcs->supported_interfaces)) { - phylink_err(pl, "MAC returned PCS which does not support %s\n", + /* Recheck PCS to handle legacy way for .mac_select_pcs */ + ret = phylink_validate_pcs_interface(pcs, state->interface); + if (ret) { + phylink_err(pl, "selected PCS does not support %s\n", phy_modes(state->interface)); return -EINVAL; } @@ -965,12 +1004,22 @@ static unsigned int phylink_inband_caps(struct phylink *pl, phy_interface_t interface) { struct phylink_pcs *pcs; + bool pcs_found = false; - if (!pl->mac_ops->mac_select_pcs) - return 0; + if (pl->mac_ops->mac_select_pcs) { + pcs = pl->mac_ops->mac_select_pcs(pl->config, + interface); + pcs_found = !!pcs; + } else { + list_for_each_entry(pcs, &pl->pcs_list, list) { + if (!phylink_validate_pcs_interface(pcs, interface)) { + pcs_found = true; + break; + } + } + } - pcs = pl->mac_ops->mac_select_pcs(pl->config, interface); - if (!pcs) + if (!pcs_found) return 0; return phylink_pcs_inband_caps(pcs, interface); @@ -1265,10 +1314,36 @@ static void phylink_major_config(struct phylink *pl, bool restart, pl->major_config_failed = true; return; } + /* Find a PCS in available PCS list for the requested interface. + * This doesn't overwrite the previous .mac_select_pcs as either + * .mac_select_pcs or PCS list implementation are permitted. + * + * Skip searching if the MAC doesn't require a dedicaed PCS for + * the requested interface. + */ + } else if (test_bit(state->interface, pl->config->pcs_interfaces)) { + bool pcs_found = false; + + list_for_each_entry(pcs, &pl->pcs_list, list) { + if (!phylink_validate_pcs_interface(pcs, + state->interface)) { + pcs_found = true; + break; + } + } + + if (!pcs_found) { + phylink_err(pl, + "couldn't find a PCS for %s\n", + phy_modes(state->interface)); - pcs_changed = pl->pcs != pcs; + pl->major_config_failed = true; + return; + } } + pcs_changed = pl->pcs != pcs; + phylink_pcs_neg_mode(pl, pcs, state->interface, state->advertising); phylink_dbg(pl, "major config, active %s/%s/%s\n", @@ -1295,11 +1370,13 @@ static void phylink_major_config(struct phylink *pl, bool restart, if (pcs_changed) { phylink_pcs_disable(pl->pcs); - if (pl->pcs) - pl->pcs->phylink = NULL; + if (pl->mac_ops->mac_select_pcs) { + if (pl->pcs) + pl->pcs->phylink = NULL; - if (pcs) - pcs->phylink = pl; + if (pcs) + pcs->phylink = pl; + } pl->pcs = pcs; } @@ -1855,8 +1932,9 @@ struct phylink *phylink_create(struct phylink_config *config, phy_interface_t iface, const struct phylink_mac_ops *mac_ops) { + struct phylink_pcs *pcs; struct phylink *pl; - int ret; + int i, ret; /* Validate the supplied configuration */ if (phy_interface_empty(config->supported_interfaces)) { @@ -1872,9 +1950,21 @@ struct phylink *phylink_create(struct phylink_config *config, mutex_init(&pl->phydev_mutex); mutex_init(&pl->state_mutex); INIT_WORK(&pl->resolve, phylink_resolve); + INIT_LIST_HEAD(&pl->pcs_list); + + /* Fill the PCS list with available PCS from phylink config */ + for (i = 0; i < config->num_available_pcs; i++) { + pcs = config->available_pcs[i]; + + list_add(&pcs->list, &pl->pcs_list); + } phy_interface_copy(pl->supported_interfaces, config->supported_interfaces); + list_for_each_entry(pcs, &pl->pcs_list, list) + phy_interface_or(pl->supported_interfaces, + pl->supported_interfaces, + pcs->supported_interfaces); pl->config = config; if (config->type == PHYLINK_NETDEV) { @@ -1953,10 +2043,16 @@ EXPORT_SYMBOL_GPL(phylink_create); */ void phylink_destroy(struct phylink *pl) { + struct phylink_pcs *pcs, *tmp; + sfp_bus_del_upstream(pl->sfp_bus); if (pl->link_gpio) gpiod_put(pl->link_gpio); + /* Remove every PCS from phylink PCS list */ + list_for_each_entry_safe(pcs, tmp, &pl->pcs_list, list) + list_del(&pcs->list); + cancel_work_sync(&pl->resolve); kfree(pl); } @@ -2437,6 +2533,7 @@ static irqreturn_t phylink_link_handler(int irq, void *data) */ void phylink_start(struct phylink *pl) { + struct phylink_pcs *pcs; bool poll = false; ASSERT_RTNL(); @@ -2463,6 +2560,10 @@ void phylink_start(struct phylink *pl) pl->pcs_state = PCS_STATE_STARTED; + /* link available PCS to phylink struct */ + list_for_each_entry(pcs, &pl->pcs_list, list) + pcs->phylink = pl; + phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_STOPPED); if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) { @@ -2507,6 +2608,8 @@ EXPORT_SYMBOL_GPL(phylink_start); */ void phylink_stop(struct phylink *pl) { + struct phylink_pcs *pcs; + ASSERT_RTNL(); if (pl->sfp_bus) @@ -2524,6 +2627,14 @@ void phylink_stop(struct phylink *pl) pl->pcs_state = PCS_STATE_DOWN; phylink_pcs_disable(pl->pcs); + + /* Drop link between phylink and PCS */ + list_for_each_entry(pcs, &pl->pcs_list, list) + pcs->phylink = NULL; + + /* Restore original supported interfaces */ + phy_interface_copy(pl->supported_interfaces, + pl->config->supported_interfaces); } EXPORT_SYMBOL_GPL(phylink_stop); diff --git a/include/linux/phylink.h b/include/linux/phylink.h index 2bc0db3d52ac..9c5a43febde1 100644 --- a/include/linux/phylink.h +++ b/include/linux/phylink.h @@ -151,6 +151,8 @@ enum phylink_op_type { * if MAC link is at %MLO_AN_FIXED mode. * @supported_interfaces: bitmap describing which PHY_INTERFACE_MODE_xxx * are supported by the MAC/PCS. + * @pcs_interfaces: bitmap describing for which PHY_INTERFACE_MODE_xxx a + * dedicated PCS is required. * @lpi_interfaces: bitmap describing which PHY interface modes can support * LPI signalling. * @mac_capabilities: MAC pause/speed/duplex capabilities. @@ -160,6 +162,8 @@ enum phylink_op_type { * @wol_phy_legacy: Use Wake-on-Lan with PHY even if phy_can_wakeup() is false * @wol_phy_speed_ctrl: Use phy speed control on suspend/resume * @wol_mac_support: Bitmask of MAC supported %WAKE_* options + * @available_pcs: array of available phylink_pcs PCS + * @num_available_pcs: num of available phylink_pcs PCS */ struct phylink_config { struct device *dev; @@ -172,6 +176,7 @@ struct phylink_config { void (*get_fixed_state)(struct phylink_config *config, struct phylink_link_state *state); DECLARE_PHY_INTERFACE_MASK(supported_interfaces); + DECLARE_PHY_INTERFACE_MASK(pcs_interfaces); DECLARE_PHY_INTERFACE_MASK(lpi_interfaces); unsigned long mac_capabilities; unsigned long lpi_capabilities; @@ -182,6 +187,9 @@ struct phylink_config { bool wol_phy_legacy; bool wol_phy_speed_ctrl; u32 wol_mac_support; + + struct phylink_pcs **available_pcs; + unsigned int num_available_pcs; }; void phylink_limit_mac_speed(struct phylink_config *config, u32 max_speed); @@ -497,6 +505,9 @@ struct phylink_pcs { struct phylink *phylink; bool poll; bool rxc_always_on; + + /* private: */ + struct list_head list; }; /** -- 2.53.0