From nobody Sun Sep 27 02:43:32 2026 Received: from mail-wm1-f51.google.com (mail-wm1-f51.google.com [209.85.128.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 01D353CAA3B for ; Sun, 6 Sep 2026 17:46:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788716785; cv=none; b=Mt77c6CF89/JhFwv9SYAo/tldu6nJB1HDLyOOtKoYKj3Bu+/LexNeYZz4EL0bM6I9Ht9JKnxZlz+SNG9vK7MqgkKWvFVKFhQlStFYGho7qvh1WCH8sLpDE/Mq7/pt/Cv9igapJAii+TkDOoMuczBW1I1GwTe3dSnVGnIbEEpLUo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788716785; c=relaxed/simple; bh=MJJRvWz8UjYd+xyF4swuo7FdbMrj6DL1ms5UwQRbozE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=tY30veSutcjvu5EFl+038rlDzHaPNtrUv5w/H3fcLliwA5ahCVrUXUyd4pRUJ79ZZ3im79y+cZrxDj3CA5lAs4Z3/4kxEtnZkqlo8kjFnkzYIez031FY7oZA8Ge+J4XoBAWK6yOmTegs8FUpwmOoFuIZKEMBSPny/GPYNme7jVo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=lex.la; spf=pass smtp.mailfrom=lex.la; dkim=pass (2048-bit key) header.d=lex.la header.i=@lex.la header.b=ZYrxHTP4; arc=none smtp.client-ip=209.85.128.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=lex.la Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=lex.la Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=lex.la header.i=@lex.la header.b="ZYrxHTP4" Received: by mail-wm1-f51.google.com with SMTP id 5b1f17b1804b1-49b965570d7so32600145e9.0 for ; Sun, 06 Sep 2026 10:46:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=lex.la; s=google; t=1788716782; x=1789321582; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=mBtppkbSqdf9lns4tDaMFXgOQ88x850RE5fhEe1B1CE=; b=ZYrxHTP4ECRDDNd1y8B9ngbf+qxyC1XfL7O+XDKscO41Iuvfv17+JVsdqhmQm6uRBj PRK8ehIWbegUqvP9rvddXMCD6QiK0erjh65PxC3bMBH7tqn/s1LG9HzZmGU7xe2i6rjH lLCESNXVc5QTT0d11hmaNBkG3e/HhDTdls4LfSof0mk8+s780Xe48HMnfssYPQOc5T3M VZEkm4MdYLWU0Gw1KjnqIZE/S2t8puswSQM5pKApWXfXvpUcrgEdWxqB/4V9P9T9CaPp iawdWNYtrOakjTQbTtT+/2O0JeyFCbAXCI19CR3GOaGf3zeyFi/si6k72QU7PJRZvHPd 7ZkA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788716782; x=1789321582; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=mBtppkbSqdf9lns4tDaMFXgOQ88x850RE5fhEe1B1CE=; b=R5oTJJEYo0/e26L9WtwtASJtTqCoEOZWRQS7iHk6o0Zz2KjJzBVHjHuWBDJysUGyxe K7Pg8Zrz5YOJKLleCl6yegWmkZGi66Ezp8hAyImFwImrFmsFJfBzzmrLVhNHRvSt2Q86 nQQiLjecedPqHEYgohBivETCrNAhDvcFMF/N76TJC7zT4DkwyoOjw7YyUzk04pvDwnFk bEqaJIttUA3ux8x2KNOvqWNZJyS8yK4wZzZuX4P+G2dX9ScVwI0DVedbU8if8AuilRvB y3ENvtYK4KgIKOskqcGndZ4BhaaKWnepvSpRInB4Qy4e3uRf8Vp3+5Lai1T5sur5jevv /Bqg== X-Forwarded-Encrypted: i=1; AKwUvBxvWhXqpn4b+Xyq4V63QTomqqfEWMhRvzd5O+yfXu8hPq/QXmQwSt866whGMttZgCn+gKKcGzs64fbJerc=@vger.kernel.org X-Gm-Message-State: AFuF++nibtsjU6da0yTIAityp486gvPo2OYAo/+Ad6DpzceSYbgkoJNt mVg2LeyhIFvGa9ObZPh/vZlyWZUCMn0rPxOTC4cvL4g6AB9vxVWPD30XjMY3uc4KBnU= X-Gm-Gg: AYBFou3btOyFpSGfBKsZKXTMeMnU5rp7vBFZ6PSD/sM8On3s42KlRLVbDxVwMj9V80I XUk6/aHkoiUE66r5qaD3sWG2rwCY5ujPT0W2ddsUzM5s9jyWqJdWXIcGRxZ4R+0N3w+myW+MLL6 zYfF4pAFOsKn1FJ9GrZXTBSOB17oDMbLVpgrf+YKBjx5dl73RrNQMVScRcqyIG/3JOP98TId1Q1 N5+wIeMYRK0YH7d3YOTkHvwJTSXxleRp/wlqyqiU5/TRy1yIIOb5NEHYnN7nSL7drbrElHi0tPP Zu//vceCkCXFGmQv2xaRzC+JqR/8NOzJSLf0RK8rOTyG5PKwz+NQs9tXH0iltp/2lcLMCJTWqjK JN7tWu0nllRuzdvQxCCXQIKLSNm9lLjdPJakmy2MFGv4FZFnM3E2nO/kWiaQg3DWKMjJQRQYCGH uUtCzRSeZMj8OUiqA/3T3MVgynSM0mjHLzLNEFyJ9oMfth/wuTLQ== X-Received: by 2002:a05:600c:4e11:b0:49c:f2f4:eddd with SMTP id 5b1f17b1804b1-49cf8268795mr202671025e9.16.1788716782231; Sun, 06 Sep 2026 10:46:22 -0700 (PDT) Received: from remote-01 ([84.17.55.227]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49cee6158e9sm331528735e9.12.2026.09.06.10.46.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 06 Sep 2026 10:46:22 -0700 (PDT) From: Aleksei Sviridkin To: linux@armlinux.org.uk, andrew@lunn.ch, andrew+netdev@lunn.ch, hkallweit1@gmail.com, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, horms@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org Cc: netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH net-next 1/2] dt-bindings: net: ethernet-controller: add phy-needs-host-firmware Date: Sun, 6 Sep 2026 17:46:18 +0000 Message-ID: <20260906174621.4107320-1-f@lex.la> X-Mailer: git-send-email 2.53.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" A port can reference a PHY that cannot be identified or driven when the controller connects to it, because the firmware the chip needs is not in hand yet. Such a port is dropped at setup and stays unusable for the whole uptime, even though the PHY works seconds later. The flag declares that the PHY behind phy-handle is expected to become usable only after this controller has probed. A consumer that sees it keeps the port and connects the PHY once its driver binds, instead of failing the connect. The prefix names the entity, like the other properties here that name what they describe rather than the controller; a PCS needing firmware would get pcs-needs-host-firmware by the same rule. It sits on the controller here because that is where the waiting happens. The flag says nothing without a phy-handle to qualify, so it depends on one; the deprecated phy and phy-device spellings are left out, since no tree combines them with a property this new. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin --- .../devicetree/bindings/net/ethernet-controller.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/net/ethernet-controller.yaml= b/Documentation/devicetree/bindings/net/ethernet-controller.yaml index 1bafd687dcb1..074c54fa6d78 100644 --- a/Documentation/devicetree/bindings/net/ethernet-controller.yaml +++ b/Documentation/devicetree/bindings/net/ethernet-controller.yaml @@ -108,6 +108,13 @@ properties: $ref: "#/properties/phy-handle" deprecated: true =20 + phy-needs-host-firmware: + $ref: /schemas/types.yaml#/definitions/flag + description: + The PHY referenced by phy-handle runs firmware that the host must + load before the PHY can be driven, and so is not usable for as + long as that has not happened. + ptp-timer: $ref: /schemas/types.yaml#/definitions/phandle description: @@ -240,6 +247,7 @@ properties: =20 dependencies: pcs-handle-names: [pcs-handle] + phy-needs-host-firmware: [phy-handle] =20 allOf: - $ref: /schemas/net/network-class.yaml# --=20 2.53.0 From nobody Sun Sep 27 02:43:32 2026 Received: from mail-wr1-f50.google.com (mail-wr1-f50.google.com [209.85.221.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 63B863CB569 for ; Sun, 6 Sep 2026 17:46:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788716788; cv=none; b=hJHoeuITYW1lM8Auz8LYiwi/wm1pC4+7q4PE/bq2/Lyx8OuwdBI7OrDDEHUEi6yb42Fv33eAesAB0W6zpbXzTtW6zO6Foa5ta5d68y8XopcrXwXFMiQRrD5NMD5auhYdUdukdRtF4UBgpgAl8QX8fUKCy739F6waIUTxXWSXzd8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788716788; c=relaxed/simple; bh=84oGUrB+WBPIhLAqt0VUTXVSjfC5JrCdIHFNpc/Hgz8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=lPRa8c+ke5K4CUtHLquaRXYVhBkBM13Kgb3ohWqzs32H8lpl4KkJ8nQ5wBQmdWaUePDgU/E+zB9WoqWK5TTfdF8to2xd3MKKFIZ4KLAXV7LOWEWDN9B/8NIqs4wY6kapZCsSSSHQQJvR44lvizffsLMkyJmZkPfQyMCMZdNQtCk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=lex.la; spf=pass smtp.mailfrom=lex.la; dkim=pass (2048-bit key) header.d=lex.la header.i=@lex.la header.b=epiINeAy; arc=none smtp.client-ip=209.85.221.50 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=lex.la Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=lex.la Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=lex.la header.i=@lex.la header.b="epiINeAy" Received: by mail-wr1-f50.google.com with SMTP id ffacd0b85a97d-47fe89fb333so1648624f8f.3 for ; Sun, 06 Sep 2026 10:46:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=lex.la; s=google; t=1788716784; x=1789321584; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=oVXQGRfHTwN7JmTF4K4ZjJRNjzl/+eJLkkoYOwe5wbQ=; b=epiINeAyaYClPgVl42eFLyHQeTRi3U+vSFxoFiDrJPW28OWMnrNGfPZog8nTF5drTU +sqwJv43+HYkiHjtog8hR46UU2t5ZZDLMs/hVJK+ApCflHwVIgo4hLnZ/GVGqxbo2W2k Bk0BJNMKHGr0nzeXkHF+DlLXGDa+ftkpNqRzSFuWhU2rcBJFykKL4NLZRqALrDCj472L pkEM1l8Jw/WLS3x25qGcG8MqYyVGIyZ6X8qLJ1MpWbkT8pRqsJ0mopkOlQ0lphXPxWPt vAHXn0A+25Ek+AjrlLtZvhnU0B0qdg1fZI2tvkqBR719uXwiuNDyZq33kLyVOpCPcM1R RJ7g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788716784; x=1789321584; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=oVXQGRfHTwN7JmTF4K4ZjJRNjzl/+eJLkkoYOwe5wbQ=; b=aKWFG0Zj+FNNJg/EzWEBR/CPoblGmNjYdLLX94sEhW7wsQQbjGBBcOImp2/k3raffQ 8SqDibTeZjfhE8kCBx+nGFp6q6jg/VEaGiLoNNE+OjouytitB67pU+6WGCEuQ3aLT91t vMea1b8F13sLYfqpuxhhwfmD8GZvY22cNTzmiIy6VgqamZB7QyGDyGqPSVKvS3tVzUeK mXemD0bu4eDTfZJKLSQXZxpVQ4Ry7m+qqSna9vTIEVTgKnXtdti+tTEGIYqUWdG8o9tn nJKtRamLGW+6f7mKb/u0Ss1EUCg0hmFl5cQxTljPLo93tCHlLRHD2FqgHSYyp+nu6qBR HkOA== X-Forwarded-Encrypted: i=1; AKwUvByI9QPvBMUsBatjxJnNHLA6mNnyrd3WeLX0A4AbGWx4UFA+XqQ+0nO4bbWqrY+WFpqbrrV0Gq56Vkv7LGc=@vger.kernel.org X-Gm-Message-State: AFuF++nER0pDC/BpSZGldiruvqOjfWREOtmScPI2SPHxldE/YYsXTRv2 hnFu+0mLDC7Wsm4nKgzoZNUGHqqyOnlpIc1L+v4R+pOatYiXEa1cQrq/uha502f9pTM= X-Gm-Gg: AYBFou2QIqOLR7Go1NaOQcvf9rvDtGrr1ruK8S9Gfbu7kK8/AnxjQRiRA4DdvKw4adt e84XWJQRYIHwZHz6kO0IZJ5sBj5V3q1whxfugGBgdDUcCp9AJhclOj60OK/HURsOEjqX4pbtO6a 3raY6D7nU7Kpplwk5U1nCiwjVr77oll4ydVzmFP7AFhNrxVbiGwISJWDe90FtXvGSFlwX1RSZ7p t7PQTHEN5WDhZAuihQzAjk9OndW/q/t5UnAiuG1v8w1QVv3qrjo1pATmZZInzl/u3RQi/0Cvu9A O/QARzPqX5U1QyKrRZkr3r1g+nWr+kXKu/AkWlS8qIJBEhTSAbuQ0y+A/GwjVjoGWLWM/oZFVgi TMUtoY23BqUgWYVW8ai9h2tx/2nig1mmbbqKTYn4p92CRpV/B9jor6lnn5fFHnPozb8ILbC8eJi y+O92IXaRizJfGoSv7hB2aRwz3sVkrSVFNJ/KJFvUM X-Received: by 2002:a05:600c:3b99:b0:499:bf0e:95c8 with SMTP id 5b1f17b1804b1-49cf81fb72amr198570765e9.1.1788716783520; Sun, 06 Sep 2026 10:46:23 -0700 (PDT) Received: from remote-01 ([84.17.55.227]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49cee6158e9sm331528735e9.12.2026.09.06.10.46.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 06 Sep 2026 10:46:23 -0700 (PDT) From: Aleksei Sviridkin To: linux@armlinux.org.uk, andrew@lunn.ch, andrew+netdev@lunn.ch, hkallweit1@gmail.com, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, horms@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org Cc: netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH net-next 2/2] net: phylink: wait for PHYs that are known to probe late Date: Sun, 6 Sep 2026 17:46:19 +0000 Message-ID: <20260906174621.4107320-2-f@lex.la> X-Mailer: git-send-email 2.53.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" A PHY whose driver or firmware lives on a filesystem mounted after the MAC probes cannot be connected when the port is set up, and the port is lost for the rest of the uptime. Let a port declare that with phy-needs-host-firmware and poll for the PHY instead of failing. Deferring the MAC's own probe is not an option: it would take every port with it, including the one needed to mount the filesystem that holds the firmware. Return 0 rather than -ENODEV, because DSA reads -ENODEV as permission to look for the PHY on the switch's internal MDIO bus, which is the wrong device. Wait for a driver that has bound, not for a device that exists, because the generic driver would otherwise bind and cannot drive such a PHY. The test cannot be made to hold past its own return: the device lock it wants cannot be held across the attach, whose failure path takes it again. What is caught instead is the outcome one step later, where the attach bound a generic driver and returned success, and the poll puts that back. The window before it, where phy_attach_direct() meets a NULL phydev->drv, stays open; closing it wants a check inside that function, or an event from the bind instead of this poll. Only that lost race is retried. A connect that fails with the real driver bound is not, because the failure path ends in phy_detach(), which asserts a PHY-node reset line - and on the boards this exists for that erases the firmware a retry would need, once per attempt for the uptime. A PHY that was ready at connect time arms no poll and keeps the old behaviour. Every path that arms the poller cancels it first and waits, so nothing else has to keep the poller and its state apart. While the poll runs the port has no PHY, so reporting the MAC's own link modes would describe a link that cannot come up and would let ethtool accept settings for it. Report an empty set instead, and refuse to configure, to set pause parameters, and to restart autonegotiation, which has nothing to renegotiate with. The reply says autonegotiation is off, which is the ethtool core's zero left in place and agrees with the empty set: a port advertising nothing is negotiating nothing. Reading pause parameters is left alone, because it reports the configured request rather than a capability, and the EEE calls already return -EOPNOTSUPP with no PHY attached. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin --- drivers/net/phy/phylink.c | 210 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 203 insertions(+), 7 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 6ed2219961fb..030924d9db12 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -98,6 +98,14 @@ struct phylink { =20 u32 wolopts_mac; u8 wol_sopass[SOPASS_MAX]; + + /* The poller owns these; every other writer cancels it first. */ + struct fwnode_handle *late_phy_fwnode; + u32 late_phy_flags; + struct delayed_work late_phy_poll; + unsigned int late_phy_poll_ms; + unsigned int late_phy_waited_ms; + bool late_phy_warned; }; =20 #define phylink_printk(level, pl, fmt, ...) \ @@ -1829,6 +1837,20 @@ int phylink_set_fixed_link(struct phylink *pl, } EXPORT_SYMBOL_GPL(phylink_set_fixed_link); =20 +static void phylink_late_phy_poll(struct work_struct *work); + +/* Synchronous because the node is put here and the poller reads it, and + * not every caller holds the rtnl that would keep them apart. It cannot + * deadlock on a caller that does: the poller only ever takes rtnl with + * trylock, so it never waits for the lock this may be called under. + */ +static void phylink_late_phy_cancel(struct phylink *pl) +{ + cancel_delayed_work_sync(&pl->late_phy_poll); + fwnode_handle_put(pl->late_phy_fwnode); + pl->late_phy_fwnode =3D NULL; +} + /** * phylink_update_pause_state() - Update the phylink pause frame configura= tion * @pl: a pointer to a &struct phylink instance @@ -1987,6 +2009,7 @@ 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_DELAYED_WORK(&pl->late_phy_poll, phylink_late_phy_poll); =20 pl->config =3D config; if (config->type =3D=3D PHYLINK_NETDEV) { @@ -2070,6 +2093,8 @@ void phylink_destroy(struct phylink *pl) if (pl->link_gpio) gpiod_put(pl->link_gpio); =20 + phylink_late_phy_cancel(pl); + cancel_work_sync(&pl->resolve); kfree(pl); } @@ -2339,10 +2364,8 @@ static int phylink_bringup_phy(struct phylink *pl, s= truct phy_device *phy, } =20 static int phylink_attach_phy(struct phylink *pl, struct phy_device *phy, - phy_interface_t interface) + phy_interface_t interface, u32 flags) { - u32 flags =3D 0; - if (WARN_ON(pl->cfg_link_an_mode =3D=3D MLO_AN_FIXED)) return -EINVAL; =20 @@ -2380,7 +2403,7 @@ int phylink_connect_phy(struct phylink *pl, struct ph= y_device *phy) pl->link_config.interface =3D pl->link_interface; } =20 - ret =3D phylink_attach_phy(pl, phy, pl->link_interface); + ret =3D phylink_attach_phy(pl, phy, pl->link_interface, 0); if (ret < 0) return ret; =20 @@ -2392,6 +2415,133 @@ int phylink_connect_phy(struct phylink *pl, struct = phy_device *phy) } EXPORT_SYMBOL_GPL(phylink_connect_phy); =20 +#define PHYLINK_LATE_PHY_POLL_MS 1000 +#define PHYLINK_LATE_PHY_WARN_MS 60000 +#define PHYLINK_LATE_PHY_POLL_MAX_MS 30000 + +static bool phylink_late_phy_pending(struct phylink *pl) +{ + return pl->late_phy_fwnode && !pl->phydev; +} + +/* Stale the moment it returns: the device lock this wants cannot be held + * across the attach, whose own failure path takes it again. + */ +static bool phylink_phy_is_usable(struct phy_device *phy_dev) +{ + return phy_dev && device_is_bound(&phy_dev->mdio.dev) && phy_dev->drv; +} + +static void phylink_late_phy_backoff(struct phylink *pl) +{ + pl->late_phy_poll_ms =3D min_t(unsigned int, pl->late_phy_poll_ms * 2, + PHYLINK_LATE_PHY_POLL_MAX_MS); +} + +static void phylink_late_phy_poll(struct work_struct *work) +{ + struct phylink *pl =3D container_of(to_delayed_work(work), struct phylink, + late_phy_poll); + struct phy_device *phy_dev; + bool again =3D false, lost_race =3D false; + int ret; + + /* Never block on rtnl: this runs on a shared workqueue. */ + if (!rtnl_trylock()) { + pl->late_phy_waited_ms +=3D pl->late_phy_poll_ms; + goto requeue; + } + + /* Stable here: whoever clears it waits for this work first. */ + phy_dev =3D fwnode_phy_find_device(pl->late_phy_fwnode); + if (!phylink_phy_is_usable(phy_dev)) { + if (phy_dev) + phy_device_free(phy_dev); + + if (!pl->late_phy_warned && + pl->late_phy_waited_ms >=3D PHYLINK_LATE_PHY_WARN_MS) { + pl->late_phy_warned =3D true; + phylink_warn(pl, + "still waiting for %pfw (phy-needs-host-firmware)\n", + pl->late_phy_fwnode); + } + /* Past the warn it may never come: stop paying 1 Hz for it. */ + if (pl->late_phy_waited_ms >=3D PHYLINK_LATE_PHY_WARN_MS) + phylink_late_phy_backoff(pl); + /* The first run is immediate, so count the sleep ahead. */ + pl->late_phy_waited_ms +=3D pl->late_phy_poll_ms; + rtnl_unlock(); + goto requeue; + } + + /* Under the mutex, unlike at connect: this port may be live. */ + if (pl->link_interface =3D=3D PHY_INTERFACE_MODE_NA) { + mutex_lock(&pl->state_mutex); + pl->link_interface =3D phy_dev->interface; + pl->link_config.interface =3D pl->link_interface; + mutex_unlock(&pl->state_mutex); + } + + ret =3D phylink_attach_phy(pl, phy_dev, pl->link_interface, + pl->late_phy_flags); + if (!ret && phy_driver_is_genphy(phy_dev)) { + /* Lost the race: the attach bound the generic driver, which + * is the outcome this poller exists to avoid. + */ + phy_detach(phy_dev); + lost_race =3D true; + ret =3D -EAGAIN; + } + if (!ret) { + ret =3D phylink_bringup_phy(pl, phy_dev, + pl->link_config.interface); + if (ret) { + phy_detach(phy_dev); + } else { + /* Only a major config programs the masks bringup + * narrowed. + */ + if (!test_bit(PHYLINK_DISABLE_STOPPED, + &pl->phylink_disable_state)) { + mutex_lock(&pl->state_mutex); + pl->force_major_config =3D true; + mutex_unlock(&pl->state_mutex); + /* MAC before the PHY, the order a start + * uses; on a port already running that is a + * forced major config, not an initial one. + */ + phylink_run_resolve(pl); + flush_work(&pl->resolve); + phy_start(phy_dev); + } + } + } + if (lost_race) { + /* The lost race unbound the generic driver again, and the + * real one is arriving, so look again at the current rate + * without spending the wait's budget. + */ + again =3D true; + } else if (ret) { + /* Not retried: every attempt ends in phy_detach(), which + * asserts a PHY-node reset line, and on the boards this + * exists for that erases the firmware a retry would need. + */ + phylink_err(pl, "failed to connect late PHY: %pe\n", + ERR_PTR(ret)); + } + phy_device_free(phy_dev); + rtnl_unlock(); + + if (!again) + return; + +requeue: + queue_delayed_work(system_freezable_power_efficient_wq, + &pl->late_phy_poll, + msecs_to_jiffies(pl->late_phy_poll_ms)); +} + /** * phylink_of_phy_connect() - connect the PHY specified in the DT mode. * @pl: a pointer to a &struct phylink returned from phylink_create() @@ -2402,7 +2552,8 @@ EXPORT_SYMBOL_GPL(phylink_connect_phy); * specified by @pl. Actions specified in phylink_connect_phy() will be * performed. * - * Returns 0 on success or a negative errno. + * Returns what phylink_fwnode_phy_connect() returns, including 0 for a + * deferred connect with no PHY attached yet. */ int phylink_of_phy_connect(struct phylink *pl, struct device_node *dn, u32 flags) @@ -2420,7 +2571,13 @@ EXPORT_SYMBOL_GPL(phylink_of_phy_connect); * Connect the phy specified @fwnode to the phylink instance specified * by @pl. * - * Returns 0 on success or a negative errno. + * If the port node carries the phy-needs-host-firmware property and the + * PHY is not usable yet, 0 is returned with no PHY connected: a poller + * connects it once its driver has probed. Until then the MAC runs + * without a PHY and ethtool reports no link modes. + * + * Returns 0 on success - the PHY connected, or the deferred connect + * armed - or a negative errno. */ int phylink_fwnode_phy_connect(struct phylink *pl, const struct fwnode_handle *fwnode, @@ -2430,6 +2587,8 @@ int phylink_fwnode_phy_connect(struct phylink *pl, struct phy_device *phy_dev; int ret; =20 + phylink_late_phy_cancel(pl); + if (!phylink_expects_phy(pl)) return 0; =20 @@ -2442,6 +2601,22 @@ int phylink_fwnode_phy_connect(struct phylink *pl, } =20 phy_dev =3D fwnode_phy_find_device(phy_fwnode); + if (fwnode_property_present(fwnode, "phy-needs-host-firmware") && + !phylink_phy_is_usable(phy_dev)) { + /* -ENODEV here would also send DSA to the switch's own bus. */ + if (phy_dev) + phy_device_free(phy_dev); + + pl->late_phy_fwnode =3D phy_fwnode; + pl->late_phy_flags =3D flags; + pl->late_phy_poll_ms =3D PHYLINK_LATE_PHY_POLL_MS; + pl->late_phy_waited_ms =3D 0; + pl->late_phy_warned =3D false; + queue_delayed_work(system_freezable_power_efficient_wq, + &pl->late_phy_poll, 0); + return 0; + } + /* We're done with the phy_node handle */ fwnode_handle_put(phy_fwnode); if (!phy_dev) @@ -2483,6 +2658,8 @@ void phylink_disconnect_phy(struct phylink *pl) =20 ASSERT_RTNL(); =20 + phylink_late_phy_cancel(pl); + mutex_lock(&pl->phydev_mutex); phy =3D pl->phydev; if (phy) @@ -3042,6 +3219,14 @@ int phylink_ethtool_ksettings_get(struct phylink *pl, =20 ASSERT_RTNL(); =20 + /* No PHY yet: the port supports nothing, not what the MAC alone can. */ + if (phylink_late_phy_pending(pl)) { + kset->base.port =3D pl->link_port; + kset->base.speed =3D SPEED_UNKNOWN; + kset->base.duplex =3D DUPLEX_UNKNOWN; + return 0; + } + if (pl->phydev) phy_ethtool_ksettings_get(pl->phydev, kset); else @@ -3114,6 +3299,10 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, =20 ASSERT_RTNL(); =20 + /* Would configure the MAC alone, for a link that cannot come up. */ + if (phylink_late_phy_pending(pl)) + return -EOPNOTSUPP; + if (pl->phydev) { struct ethtool_link_ksettings phy_kset =3D *kset; =20 @@ -3287,6 +3476,9 @@ int phylink_ethtool_nway_reset(struct phylink *pl) =20 ASSERT_RTNL(); =20 + if (phylink_late_phy_pending(pl)) + return -EOPNOTSUPP; + if (pl->phydev) ret =3D phy_restart_aneg(pl->phydev); phylink_pcs_an_restart(pl); @@ -3326,6 +3518,10 @@ int phylink_ethtool_set_pauseparam(struct phylink *p= l, if (pl->req_link_an_mode =3D=3D MLO_AN_FIXED) return -EOPNOTSUPP; =20 + /* pl->supported still describes the MAC, so the test below passes. */ + if (phylink_late_phy_pending(pl)) + return -EOPNOTSUPP; + if (!phylink_test(pl->supported, Pause) && !phylink_test(pl->supported, Asym_Pause)) return -EOPNOTSUPP; @@ -3812,7 +4008,7 @@ static int phylink_sfp_config_phy(struct phylink *pl,= struct phy_device *phy) /* Attach the PHY so that the PHY is present when we do the major * configuration step. */ - ret =3D phylink_attach_phy(pl, phy, config.interface); + ret =3D phylink_attach_phy(pl, phy, config.interface, 0); if (ret < 0) return ret; =20 --=20 2.53.0